コード例 #1
0
ファイル: g_usable.cpp プロジェクト: AlexXT/OpenJK
void func_wait_return_solid( gentity_t *self )
{
	//once a frame, see if it's clear.
	self->clipmask = CONTENTS_BODY;//|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;
	if ( !(self->spawnflags&16) || G_TestEntityPosition( self ) == NULL )
	{
		gi.SetBrushModel( self, self->model );
		VectorCopy( self->currentOrigin, self->pos1 );
		InitMover( self );
		/*
		VectorCopy( self->s.origin, self->s.pos.trBase );
		VectorCopy( self->s.origin, self->currentOrigin );
		*/
		//if we moved, we want the *current* origin, not our start origin!
		VectorCopy( self->currentOrigin, self->s.pos.trBase );
		gi.linkentity( self );
		self->svFlags &= ~SVF_NOCLIENT;
		self->s.eFlags &= ~EF_NODRAW;
		self->e_UseFunc = useF_func_usable_use;
		self->clipmask = 0;
		if ( self->target2 && self->target2[0] )
		{
			G_UseTargets2( self, self->activator, self->target2 );
		}
		if ( self->s.eFlags & EF_ANIM_ONCE )
		{//Start our anim
			self->s.frame = 0;
		}
		//NOTE: be sure to reset the brushmodel before doing this or else CONTENTS_OPAQUE may not be on when you call this
		if ( !(self->spawnflags&1) )
		{//START_OFF doesn't effect area portals
			gi.AdjustAreaPortalState( self, qfalse );
		}
	}
	else
	{
		self->clipmask = 0;
		self->e_ThinkFunc = thinkF_func_wait_return_solid;
		self->nextthink = level.time + FRAMETIME;
	}
}
コード例 #2
0
ファイル: g_mover.c プロジェクト: AHPlankton/Quake-III-Arena
/*
============
G_MoverPush

Objects need to be moved back on a failed push,
otherwise riders would continue to slide.
If qfalse is returned, *obstacle will be the blocking entity
============
*/
qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t **obstacle ) {
	int			i, e;
	gentity_t	*check;
	vec3_t		mins, maxs;
	pushed_t	*p;
	int			entityList[MAX_GENTITIES];
	int			listedEntities;
	vec3_t		totalMins, totalMaxs;

	*obstacle = NULL;


	// mins/maxs are the bounds at the destination
	// totalMins / totalMaxs are the bounds for the entire move
	if ( pusher->r.currentAngles[0] || pusher->r.currentAngles[1] || pusher->r.currentAngles[2]
		|| amove[0] || amove[1] || amove[2] ) {
		float		radius;

		radius = RadiusFromBounds( pusher->r.mins, pusher->r.maxs );
		for ( i = 0 ; i < 3 ; i++ ) {
			mins[i] = pusher->r.currentOrigin[i] + move[i] - radius;
			maxs[i] = pusher->r.currentOrigin[i] + move[i] + radius;
			totalMins[i] = mins[i] - move[i];
			totalMaxs[i] = maxs[i] - move[i];
		}
	} else {
		for (i=0 ; i<3 ; i++) {
			mins[i] = pusher->r.absmin[i] + move[i];
			maxs[i] = pusher->r.absmax[i] + move[i];
		}

		VectorCopy( pusher->r.absmin, totalMins );
		VectorCopy( pusher->r.absmax, totalMaxs );
		for (i=0 ; i<3 ; i++) {
			if ( move[i] > 0 ) {
				totalMaxs[i] += move[i];
			} else {
				totalMins[i] += move[i];
			}
		}
	}

	// unlink the pusher so we don't get it in the entityList
	trap_UnlinkEntity( pusher );

	listedEntities = trap_EntitiesInBox( totalMins, totalMaxs, entityList, MAX_GENTITIES );

	// move the pusher to it's final position
	VectorAdd( pusher->r.currentOrigin, move, pusher->r.currentOrigin );
	VectorAdd( pusher->r.currentAngles, amove, pusher->r.currentAngles );
	trap_LinkEntity( pusher );

	// see if any solid entities are inside the final position
	for ( e = 0 ; e < listedEntities ; e++ ) {
		check = &g_entities[ entityList[ e ] ];

#ifdef MISSIONPACK
		if ( check->s.eType == ET_MISSILE ) {
			// if it is a prox mine
			if ( !strcmp(check->classname, "prox mine") ) {
				// if this prox mine is attached to this mover try to move it with the pusher
				if ( check->enemy == pusher ) {
					if (!G_TryPushingProxMine( check, pusher, move, amove )) {
						//explode
						check->s.loopSound = 0;
						G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 );
						G_ExplodeMissile(check);
						if (check->activator) {
							G_FreeEntity(check->activator);
							check->activator = NULL;
						}
						//G_Printf("prox mine explodes\n");
					}
				}
				else {
					//check if the prox mine is crushed by the mover
					if (!G_CheckProxMinePosition( check )) {
						//explode
						check->s.loopSound = 0;
						G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 );
						G_ExplodeMissile(check);
						if (check->activator) {
							G_FreeEntity(check->activator);
							check->activator = NULL;
						}
						//G_Printf("prox mine explodes\n");
					}
				}
				continue;
			}
		}
#endif
		// only push items and players
		if ( check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject ) {
			continue;
		}

		// if the entity is standing on the pusher, it will definitely be moved
		if ( check->s.groundEntityNum != pusher->s.number ) {
			// see if the ent needs to be tested
			if ( check->r.absmin[0] >= maxs[0]
			|| check->r.absmin[1] >= maxs[1]
			|| check->r.absmin[2] >= maxs[2]
			|| check->r.absmax[0] <= mins[0]
			|| check->r.absmax[1] <= mins[1]
			|| check->r.absmax[2] <= mins[2] ) {
				continue;
			}
			// see if the ent's bbox is inside the pusher's final position
			// this does allow a fast moving object to pass through a thin entity...
			if (!G_TestEntityPosition (check)) {
				continue;
			}
		}

		// the entity needs to be pushed
		if ( G_TryPushingEntity( check, pusher, move, amove ) ) {
			continue;
		}

		// the move was blocked an entity

		// bobbing entities are instant-kill and never get blocked
		if ( pusher->s.pos.trType == TR_SINE || pusher->s.apos.trType == TR_SINE ) {
			G_Damage( check, pusher, pusher, NULL, NULL, 99999, 0, MOD_CRUSH );
			continue;
		}

		
		// save off the obstacle so we can call the block function (crush, etc)
		*obstacle = check;

		// move back any entities we already moved
		// go backwards, so if the same entity was pushed
		// twice, it goes back to the original position
		for ( p=pushed_p-1 ; p>=pushed ; p-- ) {
			VectorCopy (p->origin, p->ent->s.pos.trBase);
			VectorCopy (p->angles, p->ent->s.apos.trBase);
			if ( p->ent->client ) {
				p->ent->client->ps.delta_angles[YAW] = p->deltayaw;
				VectorCopy (p->origin, p->ent->client->ps.origin);
			}
			trap_LinkEntity (p->ent);
		}
		return qfalse;
	}

	return qtrue;
}
コード例 #3
0
ファイル: g_mover.c プロジェクト: AHPlankton/Quake-III-Arena
/*
==================
G_TryPushingEntity

Returns qfalse if the move is blocked
==================
*/
qboolean	G_TryPushingEntity( gentity_t *check, gentity_t *pusher, vec3_t move, vec3_t amove ) {
	vec3_t		matrix[3], transpose[3];
	vec3_t		org, org2, move2;
	gentity_t	*block;

	// EF_MOVER_STOP will just stop when contacting another entity
	// instead of pushing it, but entities can still ride on top of it
	if ( ( pusher->s.eFlags & EF_MOVER_STOP ) && 
		check->s.groundEntityNum != pusher->s.number ) {
		return qfalse;
	}

	// save off the old position
	if (pushed_p > &pushed[MAX_GENTITIES]) {
		G_Error( "pushed_p > &pushed[MAX_GENTITIES]" );
	}
	pushed_p->ent = check;
	VectorCopy (check->s.pos.trBase, pushed_p->origin);
	VectorCopy (check->s.apos.trBase, pushed_p->angles);
	if ( check->client ) {
		pushed_p->deltayaw = check->client->ps.delta_angles[YAW];
		VectorCopy (check->client->ps.origin, pushed_p->origin);
	}
	pushed_p++;

	// try moving the contacted entity 
	// figure movement due to the pusher's amove
	G_CreateRotationMatrix( amove, transpose );
	G_TransposeMatrix( transpose, matrix );
	if ( check->client ) {
		VectorSubtract (check->client->ps.origin, pusher->r.currentOrigin, org);
	}
	else {
		VectorSubtract (check->s.pos.trBase, pusher->r.currentOrigin, org);
	}
	VectorCopy( org, org2 );
	G_RotatePoint( org2, matrix );
	VectorSubtract (org2, org, move2);
	// add movement
	VectorAdd (check->s.pos.trBase, move, check->s.pos.trBase);
	VectorAdd (check->s.pos.trBase, move2, check->s.pos.trBase);
	if ( check->client ) {
		VectorAdd (check->client->ps.origin, move, check->client->ps.origin);
		VectorAdd (check->client->ps.origin, move2, check->client->ps.origin);
		// make sure the client's view rotates when on a rotating mover
		check->client->ps.delta_angles[YAW] += ANGLE2SHORT(amove[YAW]);
	}

	// may have pushed them off an edge
	if ( check->s.groundEntityNum != pusher->s.number ) {
		check->s.groundEntityNum = -1;
	}

	block = G_TestEntityPosition( check );
	if (!block) {
		// pushed ok
		if ( check->client ) {
			VectorCopy( check->client->ps.origin, check->r.currentOrigin );
		} else {
			VectorCopy( check->s.pos.trBase, check->r.currentOrigin );
		}
		trap_LinkEntity (check);
		return qtrue;
	}

	// if it is ok to leave in the old position, do it
	// this is only relevent for riding entities, not pushed
	// Sliding trapdoors can cause this.
	VectorCopy( (pushed_p-1)->origin, check->s.pos.trBase);
	if ( check->client ) {
		VectorCopy( (pushed_p-1)->origin, check->client->ps.origin);
	}
	VectorCopy( (pushed_p-1)->angles, check->s.apos.trBase );
	block = G_TestEntityPosition (check);
	if ( !block ) {
		check->s.groundEntityNum = -1;
		pushed_p--;
		return qtrue;
	}

	// blocked
	return qfalse;
}
コード例 #4
0
/*
================
G_SetEntState

  sets the entstate of an entity.
================
*/
void G_SetEntState( gentity_t *ent, entState_t state ) {
	if ( ent->entstate == state ) {
		G_DPrintf( "entity %i already in desired state [%i]\n", ent->s.number, state );
		return;
	}

	switch ( state ) {
	case STATE_DEFAULT:             if ( ent->entstate == STATE_UNDERCONSTRUCTION ) {
			ent->clipmask = ent->realClipmask;
			ent->r.contents = ent->realContents;
			if ( !ent->realNonSolidBModel ) {
				ent->s.eFlags &= ~EF_NONSOLID_BMODEL;
			}
	}

		ent->entstate = STATE_DEFAULT;
		ent->s.powerups = STATE_DEFAULT;

		if ( ent->s.eType == ET_WOLF_OBJECTIVE ) {
			char cs[MAX_STRING_CHARS];
			trap_GetConfigstring( ent->count, cs, sizeof( cs ) );
			ent->count2 &= ~256;
			Info_SetValueForKey( cs, "t", va( "%i", ent->count2 ) );
			trap_SetConfigstring( ent->count, cs );
		}

		if ( ent->s.eType != ET_COMMANDMAP_MARKER ) {
			trap_LinkEntity( ent );
		}

		// deal with any entities in the solid
		{
			int listedEntities, e;
			int entityList[MAX_GENTITIES];
			gentity_t *check, *block;

			listedEntities = trap_EntitiesInBox( ent->r.absmin, ent->r.absmax, entityList, MAX_GENTITIES );

			for ( e = 0; e < listedEntities; e++ ) {
				check = &g_entities[entityList[e]];

				// ignore everything but items, players and missiles (grenades too)
				if ( check->s.eType != ET_MISSILE && check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject ) {
					continue;
				}

				if ( ( block = G_TestEntityPosition( check ) ) == NULL ) {
					continue;
				}

				if ( block != ent ) {
					// the entity is blocked by another entity - that block this should take care of this itself
					continue;
				}

				if ( check->client || check->s.eType == ET_CORPSE ) {
					// gibs anything player like
					G_Damage( check, ent, ent, NULL, NULL, 9999, DAMAGE_NO_PROTECTION, MOD_CRUSH_CONSTRUCTIONDEATH_NOATTACKER );
				} else if ( check->s.eType == ET_ITEM && check->item->giType == IT_TEAM ) {
					// see if it's a critical entity, one that we can't just simply kill (basically flags)
					Team_DroppedFlagThink( check );
				} else {
					// remove the landmine from both teamlists
					if ( check->s.eType == ET_MISSILE && check->methodOfDeath == MOD_LANDMINE ) {
						mapEntityData_t *mEnt;

						if ( ( mEnt = G_FindMapEntityData( &mapEntityData[0], check - g_entities ) ) != NULL ) {
							G_FreeMapEntityData( &mapEntityData[0], mEnt );
						}

						if ( ( mEnt = G_FindMapEntityData( &mapEntityData[1], check - g_entities ) ) != NULL ) {
							G_FreeMapEntityData( &mapEntityData[1], mEnt );
						}
					}

					// just get rid of it
					G_TempEntity( check->s.origin, EV_ITEM_POP );
					G_FreeEntity( check );
				}
			}
		}

		// if this is an mg42, then we should try and calculate mg42 spots again
		BotCalculateMg42Spots();

		break;
	case STATE_UNDERCONSTRUCTION:   ent->entstate = STATE_UNDERCONSTRUCTION;
		ent->s.powerups = STATE_UNDERCONSTRUCTION;
		ent->realClipmask = ent->clipmask;
		if ( ent->s.eType != ET_CONSTRUCTIBLE ) {                           // don't make nonsolid as we want to make them partially solid for staged construction
			ent->clipmask = 0;
		}
		ent->realContents = ent->r.contents;
		if ( ent->s.eType != ET_CONSTRUCTIBLE ) {
			ent->r.contents = 0;
		}
		if ( ent->s.eFlags & EF_NONSOLID_BMODEL ) {
			ent->realNonSolidBModel = qtrue;
		} else
		if ( ent->s.eType != ET_CONSTRUCTIBLE ) {
			ent->s.eFlags |= EF_NONSOLID_BMODEL;
		}

		if ( !Q_stricmp( ent->classname, "misc_mg42" ) ) {
			// stop using the mg42
			mg42_stopusing( ent );
		}

		if ( ent->s.eType == ET_COMMANDMAP_MARKER ) {
			mapEntityData_t *mEnt;

			if ( ( mEnt = G_FindMapEntityData( &mapEntityData[0], ent - g_entities ) ) != NULL ) {
				G_FreeMapEntityData( &mapEntityData[0], mEnt );
			}
			if ( ( mEnt = G_FindMapEntityData( &mapEntityData[1], ent - g_entities ) ) != NULL ) {
				G_FreeMapEntityData( &mapEntityData[1], mEnt );
			}
		}

		trap_LinkEntity( ent );
		break;
	case STATE_INVISIBLE:           if ( ent->entstate == STATE_UNDERCONSTRUCTION ) {
			ent->clipmask = ent->realClipmask;
			ent->r.contents = ent->realContents;
			if ( !ent->realNonSolidBModel ) {
				ent->s.eFlags &= ~EF_NONSOLID_BMODEL;
			}
	}

		ent->entstate = STATE_INVISIBLE;
		ent->s.powerups = STATE_INVISIBLE;

		if ( !Q_stricmp( ent->classname, "misc_mg42" ) ) {
			mg42_stopusing( ent );
		} else if ( ent->s.eType == ET_WOLF_OBJECTIVE ) {
			char cs[MAX_STRING_CHARS];
			trap_GetConfigstring( ent->count, cs, sizeof( cs ) );
			ent->count2 |= 256;
			Info_SetValueForKey( cs, "t", va( "%i", ent->count2 ) );
			trap_SetConfigstring( ent->count, cs );
		}

		if ( ent->s.eType == ET_COMMANDMAP_MARKER ) {
			mapEntityData_t *mEnt;

			if ( ( mEnt = G_FindMapEntityData( &mapEntityData[0], ent - g_entities ) ) != NULL ) {
				G_FreeMapEntityData( &mapEntityData[0], mEnt );
			}
			if ( ( mEnt = G_FindMapEntityData( &mapEntityData[1], ent - g_entities ) ) != NULL ) {
				G_FreeMapEntityData( &mapEntityData[1], mEnt );
			}
		}

		trap_UnlinkEntity( ent );
		break;
	}
}
コード例 #5
0
ファイル: g_mover.c プロジェクト: Razish/QtZ
// Objects need to be moved back on a failed push, otherwise riders would continue to slide.
//	If qfalse is returned, *obstacle will be the blocking entity
qboolean G_MoverPush( gentity_t *pusher, vector3 *move, vector3 *amove, gentity_t **obstacle ) {
	int			i, e;
	gentity_t	*check;
	vector3		mins, maxs;
	pushed_t	*p;
	int			entityList[MAX_GENTITIES];
	int			listedEntities;
	vector3		totalMins, totalMaxs;

	*obstacle = NULL;


	// mins/maxs are the bounds at the destination
	// totalMins / totalMaxs are the bounds for the entire move
	if ( pusher->r.currentAngles.x || pusher->r.currentAngles.y || pusher->r.currentAngles.z
		|| amove->x || amove->y || amove->z ) {
		float		radius;

		radius = RadiusFromBounds( &pusher->r.mins, &pusher->r.maxs );
		for ( i = 0 ; i < 3 ; i++ ) {
			mins.data[i] = pusher->r.currentOrigin.data[i] + move->data[i] - radius;
			maxs.data[i] = pusher->r.currentOrigin.data[i] + move->data[i] + radius;
			totalMins.data[i] = mins.data[i] - move->data[i];
			totalMaxs.data[i] = maxs.data[i] - move->data[i];
		}
	} else {
		for (i=0 ; i<3 ; i++) {
			mins.data[i] = pusher->r.absmin.data[i] + move->data[i];
			maxs.data[i] = pusher->r.absmax.data[i] + move->data[i];
		}

		VectorCopy( &pusher->r.absmin, &totalMins );
		VectorCopy( &pusher->r.absmax, &totalMaxs );
		for (i=0 ; i<3 ; i++) {
			if ( move->data[i] > 0 )	totalMaxs.data[i] += move->data[i];
			else						totalMins.data[i] += move->data[i];
		}
	}

	// unlink the pusher so we don't get it in the entityList
	trap->SV_UnlinkEntity( (sharedEntity_t *)pusher );

	listedEntities = trap->SV_AreaEntities( &totalMins, &totalMaxs, entityList, MAX_GENTITIES );

	// move the pusher to its final position
	VectorAdd( &pusher->r.currentOrigin, move, &pusher->r.currentOrigin );
	VectorAdd( &pusher->r.currentAngles, amove, &pusher->r.currentAngles );
	trap->SV_LinkEntity( (sharedEntity_t *)pusher );

	// see if any solid entities are inside the final position
	for ( e = 0 ; e < listedEntities ; e++ ) {
		check = &g_entities[ entityList[ e ] ];

		// only push items and players
		if ( check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject ) {
			continue;
		}

		// if the entity is standing on the pusher, it will definitely be moved
		if ( check->s.groundEntityNum != pusher->s.number ) {
			// see if the ent needs to be tested
			if ( check->r.absmin.x >= maxs.x ||
				 check->r.absmin.y >= maxs.y ||
				 check->r.absmin.z >= maxs.z ||
				 check->r.absmax.x <= mins.x ||
				 check->r.absmax.y <= mins.y ||
				 check->r.absmax.z <= mins.z ) {
				continue;
			}
			// see if the ent's bbox is inside the pusher's final position
			// this does allow a fast moving object to pass through a thin entity...
			if (!G_TestEntityPosition (check)) {
				continue;
			}
		}

		// the entity needs to be pushed
		if ( G_TryPushingEntity( check, pusher, move, amove ) ) {
			continue;
		}

		// the move was blocked an entity

		// bobbing entities are instant-kill and never get blocked
		if ( pusher->s.pos.trType == TR_SINE || pusher->s.apos.trType == TR_SINE ) {
			//Raz: don't care about the affector for pushers
			G_Damage( check, pusher, pusher, NULL, NULL, NULL, 99999, 0, MOD_CRUSH );
			continue;
		}

		
		// save off the obstacle so we can call the block function (crush, etc)
		*obstacle = check;

		// move back any entities we already moved
		// go backwards, so if the same entity was pushed
		// twice, it goes back to the original position
		for ( p=pushed_p-1 ; p>=pushed ; p-- ) {
			VectorCopy (&p->origin, &p->ent->s.pos.trBase);
			VectorCopy (&p->angles, &p->ent->s.apos.trBase);
			if ( p->ent->client ) {
				p->ent->client->ps.delta_angles.yaw = (int)p->deltayaw;
				VectorCopy (&p->origin, &p->ent->client->ps.origin);
			}
			trap->SV_LinkEntity ((sharedEntity_t *)p->ent);
		}
		return qfalse;
	}

	return qtrue;
}
コード例 #6
0
ファイル: g_active.c プロジェクト: chegestar/omni-bot
void WolfReviveBbox( gentity_t *self ) {
	int touch[MAX_GENTITIES];
	int num,i, touchnum = 0;
	gentity_t   *hit = NULL;
	vec3_t mins, maxs;
	gentity_t   *capsulehit = NULL;

	VectorAdd( self->r.currentOrigin, playerMins, mins );
	VectorAdd( self->r.currentOrigin, playerMaxs, maxs );

	num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );

	// Arnout, we really should be using capsules, do a quick, more refined test using mover collision
	if ( num ) {
		capsulehit = G_TestEntityPosition( self );
	}

	for ( i = 0 ; i < num ; i++ ) {
		hit = &g_entities[touch[i]];
		if ( hit->client ) {
			// ATVI Wolfenstein Misc #467
			// don't look at yourself when counting the hits
			if ( hit->client->ps.persistant[PERS_HWEAPON_USE] && hit != self ) {
				touchnum++;
				// Move corpse directly to the person who revived them
				if ( self->props_frame_state >= 0 ) {
					trap_UnlinkEntity( self );
					VectorCopy( g_entities[self->props_frame_state].client->ps.origin, self->client->ps.origin );
					VectorCopy( self->client->ps.origin, self->r.currentOrigin );
					trap_LinkEntity( self );

					// Reset value so we don't continue to warp them
					self->props_frame_state = -1;
				}
			} else if ( hit->health > 0 ) {
				if ( hit->s.number != self->s.number ) {
					WolfRevivePushEnt( hit, self );
					touchnum++;
				}
			}
		} else if ( hit->r.contents & ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_PLAYERCLIP ) )   {
			// Arnout: if hit is a mover, use capsulehit (this will only work if we touch one mover at a time - situations where you hit two are
			// really rare anyway though. The real fix is to move everything to capsule collision detection though
			if ( hit->s.eType == ET_MOVER ) {
				if ( capsulehit && capsulehit != hit ) {
					continue;   // we collided with a mover, but we're not stuck in this one
				} else {
					continue;   // we didn't collide with any movers
				}
			}

			WolfRevivePushEnt( hit, self );
			touchnum++;
		}
	}

	if ( g_dbgRevive.integer ) {
		G_Printf( "WolfReviveBbox: touchnum: %d\n", touchnum );
	}

	if ( touchnum == 0 ) {
		if ( g_dbgRevive.integer ) {
			G_Printf( "WolfReviveBbox:  Player is solid now!\n" );
		}
		self->r.contents = CONTENTS_BODY;
	}
}
コード例 #7
0
ファイル: g_physics.c プロジェクト: jayschwa/quake2world
/*
 * @brief Objects need to be moved back on a failed push,
 * otherwise riders would continue to slide.
 */
static _Bool G_Push(g_edict_t *pusher, vec3_t move, vec3_t amove) {
	int32_t i, e;
	g_edict_t *check, *block;
	vec3_t mins, maxs;
	g_pushed_t *p;
	vec3_t org, org2, move2, forward, right, up;

	// clamp the move to 1/8 units, so the position will
	// be accurate for client side prediction
	for (i = 0; i < 3; i++) {
		vec_t temp;
		temp = move[i] * 8.0;
		if (temp > 0.0)
			temp += 0.5;
		else
			temp -= 0.5;
		move[i] = 0.125 * (int16_t) temp;
	}

	// find the bounding box
	for (i = 0; i < 3; i++) {
		mins[i] = pusher->abs_mins[i] + move[i];
		maxs[i] = pusher->abs_maxs[i] + move[i];
	}

	// we need this for pushing things later
	VectorSubtract(vec3_origin, amove, org);
	AngleVectors(org, forward, right, up);

	// save the pusher's original position
	g_pushed_p->ent = pusher;
	VectorCopy(pusher->s.origin, g_pushed_p->origin);
	VectorCopy(pusher->s.angles, g_pushed_p->angles);
	if (pusher->client)
		g_pushed_p->delta_yaw = pusher->client->ps.pm_state.delta_angles[YAW];
	g_pushed_p++;

	// move the pusher to it's final position
	VectorAdd(pusher->s.origin, move, pusher->s.origin);
	VectorAdd(pusher->s.angles, amove, pusher->s.angles);
	gi.LinkEdict(pusher);

	// see if any solid entities are inside the final position
	check = g_game.edicts + 1;
	for (e = 1; e < ge.num_edicts; e++, check++) {

		if (!check->in_use)
			continue;

		if (check->locals.move_type == MOVE_TYPE_PUSH || check->locals.move_type == MOVE_TYPE_STOP
				|| check->locals.move_type == MOVE_TYPE_NONE || check->locals.move_type
				== MOVE_TYPE_NO_CLIP)
			continue;

		if (!check->area.prev)
			continue; // not linked in anywhere

		// if the entity is standing on the pusher, it will definitely be moved
		if (check->locals.ground_entity != pusher) {

			// do not push entities which are beside us
			if (check->locals.item)
				continue;

			// see if the ent needs to be tested
			if (check->abs_mins[0] >= maxs[0] || check->abs_mins[1] >= maxs[1]
					|| check->abs_mins[2] >= maxs[2] || check->abs_maxs[0] <= mins[0]
					|| check->abs_maxs[1] <= mins[1] || check->abs_maxs[2] <= mins[2])
				continue;

			// see if the ent's bbox is inside the pusher's final position
			if (!G_TestEntityPosition(check))
				continue;
		}

		if ((pusher->locals.move_type == MOVE_TYPE_PUSH) || (check->locals.ground_entity == pusher)) {
			// move this entity
			g_pushed_p->ent = check;
			VectorCopy(check->s.origin, g_pushed_p->origin);
			VectorCopy(check->s.angles, g_pushed_p->angles);
			g_pushed_p++;

			// try moving the contacted entity
			VectorAdd(check->s.origin, move, check->s.origin);
			if (check->client) { // rotate the client
				check->client->ps.pm_state.delta_angles[YAW] += PackAngle(amove[YAW]);
			}

			// figure movement due to the pusher's move
			VectorSubtract(check->s.origin, pusher->s.origin, org);
			org2[0] = DotProduct(org, forward);
			org2[1] = -DotProduct(org, right);
			org2[2] = DotProduct(org, up);
			VectorSubtract(org2, org, move2);
			VectorAdd(check->s.origin, move2, check->s.origin);

			// may have pushed them off an edge
			if (check->locals.ground_entity != pusher)
				check->locals.ground_entity = NULL;

			block = G_TestEntityPosition(check);
			if (!block) { // pushed okay
				gi.LinkEdict(check);
				continue;
			}

			// if it is okay to leave in the old position, do it
			// this is only relevant for riding entities, not pushed
			// FIXME: this doesn't account for rotation
			VectorSubtract(check->s.origin, move, check->s.origin);
			block = G_TestEntityPosition(check);
			if (!block) {
				g_pushed_p--;
				continue;
			}
		}

		// save off the obstacle so we can call the block function
		obstacle = check;

		// move back any entities we already moved
		// go backwards, so if the same entity was pushed
		// twice, it goes back to the original position
		for (p = g_pushed_p - 1; p >= g_pushed; p--) {
			VectorCopy(p->origin, p->ent->s.origin);
			VectorCopy(p->angles, p->ent->s.angles);
			if (p->ent->client) {
				p->ent->client->ps.pm_state.delta_angles[YAW] = p->delta_yaw;
			}
			gi.LinkEdict(p->ent);
		}
		return false;
	}

	// FIXME: is there a better way to handle this?
	// see if anything we moved has touched a trigger
	for (p = g_pushed_p - 1; p >= g_pushed; p--) {
		if (p->ent->in_use && p->ent->client && p->ent->locals.health > 0)
			G_TouchTriggers(p->ent);
	}

	return true;
}