Beispiel #1
0
void CAudioChannel::SetVolume (int nVolume)
{
if (m_info.bPlaying) {
	m_info.nVolume = FixMulDiv (nVolume, audio.Volume (), I2X (1));
#if USE_SDL_MIXER
	if (gameOpts->sound.bUseSDLMixer)
		Mix_VolPan (this - audio.Channel (), m_info.nVolume, -1);
#endif
	}
}
Beispiel #2
0
//this routine will set the thrust for an tObject to a value that will
// (hopefully) maintain the tObject's current velocity
void set_thrust_from_velocity (tObject *objP)
{
	fix k;

	Assert (objP->movementType == MT_PHYSICS);

	k = FixMulDiv (objP->mType.physInfo.mass, objP->mType.physInfo.drag, (f1_0-objP->mType.physInfo.drag));

	VmVecCopyScale (&objP->mType.physInfo.thrust, &objP->mType.physInfo.velocity, k);

}
Beispiel #3
0
void DigiSetChannelVolume(int channel, int volume)
{
    if (!gameStates.sound.digi.bInitialized)
        return;
    if (!SoundSlots[channel].playing)
        return;
    SoundSlots[channel].volume = FixMulDiv(volume, gameStates.sound.digi.nVolume, F1_0);
#if USE_SDL_MIXER
    if (gameOpts->sound.bUseSDLMixer)
        Mix_Volume (channel, (volume * gameStates.sound.digi.nVolume / F1_0) / (SOUND_MAX_VOLUME / MIX_MAX_VOLUME));
#endif
}
Beispiel #4
0
//------------------------------------------------------------------------------
//compute aspect ratio for this canvas
void CTransformation::ComputeAspect (void)
{
fix s = FixMulDiv (screen.Aspect (), CCanvas::Current ()->Height (), CCanvas::Current ()->Width ());
if (s <= I2X (1)) {	   //scale x
	m_info.aspect [X] = s;
	m_info.aspect [Y] = I2X (1);
	}
else {
	m_info.aspect [Y] = FixDiv (I2X (1), s);
	m_info.aspect [X] = I2X (1);
	}
m_info.aspect [Z] = I2X (1);		//always 1
}
Beispiel #5
0
//------------------------------------------------------------------------------
//added on 980905 by adb from original source to make sfx volume work
void DigiSetFxVolume( int dvolume )
{
    dvolume = FixMulDiv (dvolume, SOUND_MAX_VOLUME, 0x7fff);
    if (dvolume > SOUND_MAX_VOLUME)
        gameStates.sound.digi.nVolume = SOUND_MAX_VOLUME;
    else if (dvolume < 0)
        gameStates.sound.digi.nVolume = 0;
    else
        gameStates.sound.digi.nVolume = dvolume;
    if (!gameStates.sound.digi.bInitialized)
        return;
    DigiSyncSounds();
}
Beispiel #6
0
//------------------------------------------------------------------------------
//added on 980905 by adb from original source to make sfx nVolume work
void CAudio::SetFxVolume (int nVolume)
{
if (!gameStates.app.bUseSound)
	return;
nVolume = FixMulDiv (nVolume, SOUND_MAX_VOLUME, 0x7fff);
if (nVolume > SOUND_MAX_VOLUME)
	m_info.nVolume = SOUND_MAX_VOLUME;
else if (nVolume < 0)
	m_info.nVolume = 0;
else
	m_info.nVolume = nVolume;
if (!m_info.bAvailable) 
	return;
SyncSounds ();
}
Beispiel #7
0
void DrawModelPicture (int nModel, CAngleVector *orientAngles)
{
	CFixVector	p = CFixVector::ZERO;
	CFixMatrix	o = CFixMatrix::IDENTITY;

Assert ((nModel >= 0) && (nModel < gameData.models.nPolyModels));
G3StartFrame (0, 0);
glDisable (GL_BLEND);
G3SetViewMatrix (p, o, gameStates.render.xZoom, 1);
if (gameData.models.polyModels [0][nModel].Rad ())
	p [Z] = FixMulDiv (DEFAULT_VIEW_DIST, gameData.models.polyModels [0][nModel].Rad (), BASE_MODEL_SIZE);
else
	p [Z] = DEFAULT_VIEW_DIST;
o = CFixMatrix::Create (*orientAngles);
DrawPolyModel (NULL, &p, &o, NULL, nModel, 0, I2X (1), NULL, NULL, NULL);
G3EndFrame ();
if (gameStates.ogl.nDrawBuffer != GL_BACK)
	GrUpdate (0);
}
Beispiel #8
0
//------------------------------------------------------------------------------
//stop the redbook, so we can read off the CD
void SongsStopRedbook(void)
{
	int oldVolume = gameConfig.nRedbookVolume*REDBOOK_VOLUME_SCALE/8;
	fix oldTime = TimerGetFixedSeconds ();

if (gameStates.sound.bRedbookPlaying) {		//fade out volume
	int newVolume;
	do {
		fix t = TimerGetFixedSeconds ();

		newVolume = FixMulDiv (oldVolume, (FADE_TIME - (t-oldTime)), FADE_TIME);
		if (newVolume < 0)
			newVolume = 0;
		RBASetVolume(newVolume);
	} while (newVolume > 0);
}
RBAStop ();              	// Stop CD, if playing
RBASetVolume(oldVolume);	//restore volume
gameStates.sound.bRedbookPlaying = 0;
}
Beispiel #9
0
void DrawModelPicture (int mn, vms_angvec *orient_angles)
{
    vms_vector	temp_pos=ZERO_VECTOR;
    vms_matrix	temp_orient = IDENTITY_MATRIX;
#if TEMP_CANV
    grs_canvas	*save_canv = grdCurCanv, *temp_canv;
#endif
    Assert (mn>=0 && mn<gameData.models.nPolyModels);
#if TEMP_CANV
    temp_canv = GrCreateCanvas (save_canv->cv_bitmap.bm_props.w, save_canv->cv_bitmap.bm_props.h);
    temp_canv->cv_bitmap.bm_props.x = grdCurCanv->cv_bitmap.bm_props.x;
    temp_canv->cv_bitmap.bm_props.y = grdCurCanv->cv_bitmap.bm_props.y;
    GrSetCurrentCanvas (temp_canv);
#endif
    GrClearCanvas (0);
    G3StartFrame (1, 0);
    glDisable (GL_BLEND);
    G3SetViewMatrix (&temp_pos, &temp_orient, 0x9000);
    if (gameData.models.polyModels [mn].rad != 0)
        temp_pos.z = FixMulDiv (DEFAULT_VIEW_DIST, gameData.models.polyModels [mn].rad, BASE_MODEL_SIZE);
    else
        temp_pos.z = DEFAULT_VIEW_DIST;
    VmAngles2Matrix (&temp_orient, orient_angles);
    PA_DFX (save_light = gameStates.render.nLighting);
    PA_DFX (gameStates.render.nLighting = 0);
    DrawPolygonModel (NULL, &temp_pos, &temp_orient, NULL, mn, 0, f1_0, NULL, NULL, NULL);
    PA_DFX (gameStates.render.nLighting = save_light);
#if TEMP_CANV
    GrSetCurrentCanvas (save_canv);
    glDisable (GL_BLEND);
    GrBitmap (0, 0, &temp_canv->cv_bitmap);
    glEnable (GL_BLEND);
    GrFreeCanvas (temp_canv);
#endif
    G3EndFrame ();
    if (curDrawBuffer != GL_BACK)
        GrUpdate (0);
}
int RenderWeaponCorona (CObject *objP, tRgbaColorf *colorP, float alpha, fix xOffset,
								float fScale, int bSimple, int bViewerOffset, int bDepthSort)
{
if (!SHOW_OBJ_FX)
	return 0;
if (SHOW_SHADOWS && (gameStates.render.nShadowPass != 1))
	return 0;
if ((objP->info.nType == OBJ_WEAPON) && (objP->info.renderType == RT_POLYOBJ))
	RenderLaserCorona (objP, colorP, alpha, fScale);
else if (gameOpts->render.coronas.bShots && LoadCorona ()) {
	fix			xSize;
	tRgbaColorf	color;

	static tTexCoord2f	tcCorona [4] = {{{0,0}},{{1,0}},{{1,1}},{{0,1}}};

	CFixVector	vPos = objP->info.position.vPos;
	xSize = (fix) (WeaponBlobSize (objP->info.nId) * F2X (fScale));
	if (xOffset) {
		if (bViewerOffset) {
			CFixVector o = gameData.render.mine.viewerEye - vPos;
			CFixVector::Normalize (o);
			vPos += o * xOffset;
			}
		else
			vPos += objP->info.position.mOrient.FVec () * xOffset;
		}
	if (xSize < I2X (1))
		xSize = I2X (1);
	color.alpha = alpha;
	alpha = coronaIntensities [gameOpts->render.coronas.nObjIntensity] / 2;
	color.red = colorP->red * alpha;
	color.green = colorP->green * alpha;
	color.blue = colorP->blue * alpha;
	return transparencyRenderer.AddSprite (bmpCorona, vPos, &color, FixMulDiv (xSize, bmpCorona->Width (), bmpCorona->Height ()), xSize, 0, LIGHTTRAIL_BLENDMODE, 3);
	}
return 0;
}
Beispiel #11
0
int CAudioChannel::Speedup (CSoundSample *soundP, int speed)
{
	int	h, i, j, l;
	ubyte	*pDest, *pSrc;

l = FixMulDiv (m_info.bResampled ? m_info.nLength : soundP->nLength [soundP->bCustom], speed, I2X (1));
if (!(pDest = new ubyte [l]))
	return -1;
pSrc = m_info.bResampled ? m_info.sample.Buffer () : soundP->data [soundP->bCustom].Buffer ();
for (h = i = j = 0; i < l; i++) {
	pDest [j] = pSrc [i];
	h += speed;
	while (h >= I2X (1)) {
		j++;
		h -= I2X (1);
		}
	}
if (m_info.bResampled)
	m_info.sample.Destroy ();
else
	m_info.bResampled = 1;
m_info.sample.SetBuffer (pDest, 0, j);
return m_info.nLength = j;
}
Beispiel #12
0
void DrawObjectBlob (CObject *objP, int bmi0, int bmi, int iFrame, tRgbaColorf *colorP, float fAlpha)
{
	CBitmap*		bmP;
	tRgbaColorf	color;
	int			nType = objP->info.nType;
	int			nId = objP->info.nId;
#if 0
	int			bMuzzleFlash = 0;
#endif
	int			bAdditive = 0, bEnergy = 0, nTransp = (nType == OBJ_POWERUP) ? 3 : 2;
	fix			xSize;
	float			fScale;

if ((nType == OBJ_WEAPON) && (objP->info.nId == OMEGA_ID) && omegaLightnings.Exist ())
	return;
#if DBG
if ((nType == nDbgObjType) && ((nDbgObjId < 0) || (objP->info.nId == nDbgObjId)))
	nDbgObjType = nDbgObjType;
#endif
if (gameOpts->render.textures.bUseHires [0] || gameOpts->render.effects.bTransparent) {
	if (fAlpha) {
		bAdditive = (nType == OBJ_FIREBALL) || (nType == OBJ_EXPLOSION) || ((nType == OBJ_WEAPON) && (objP->info.nId == OMEGA_ID));
#if 0
		bMuzzleFlash = (nType == OBJ_FIREBALL) && ((nId == 11) || (nId == 12) || (nId == 15) || (nId == 22) || (nId == 86));
#endif
		}
	else {
		if (nType == OBJ_POWERUP) {
			if (IsEnergyPowerup (nId)) {
				fAlpha = 2.0f / 3.0f;
				bEnergy = 1;
				}
			else
				fAlpha = 1.0f;
			}
		else if ((nType != OBJ_FIREBALL) && (nType != OBJ_EXPLOSION))
			fAlpha = 1.0f;
		else {
			fAlpha = 2.0f / 3.0f;
			bAdditive = 1;
			}
		}
	}
else {
	nTransp = 3;
	fAlpha = 1.0f;
	}

#if 0
if (bmi < 0) {
	PageInAddonBitmap (bmi);
	bmP = gameData.pig.tex.addonBitmaps - bmi - 1;
#if DBG
	if ((objP->rType.vClipInfo.nCurFrame < 0) || (objP->rType.vClipInfo.nCurFrame >= bmP->FrameCount ())) {
		objP->rType.vClipInfo.nCurFrame = 0;
		return;
		}
#endif
	}
else {
	LoadBitmap (bmi, 0);
	bmP = gameData.pig.tex.bitmaps [0] + bmi;
	}
#else
if (bmi < 0) {
	bmP = gameData.pig.tex.addonBitmaps - bmi - 1;
	bmP = bmP->SetCurFrame (iFrame);
	}
else {
	CBitmap* bmoP;

	bmP = gameData.pig.tex.bitmaps [0] + bmi;
	if ((bmP->Type () == BM_TYPE_STD) && (bmoP = bmP->Override ()))
		bmP = bmoP->SetCurFrame (iFrame);
	}
#endif

//bmP->SetupTexture (1, 1);
if (!bmP)
	return;
#if 0
if (!bmP->Prepared () && bmP->PrepareTexture (1, 0))
	return;
#endif
fScale = ObjectBlobColor (objP, bmP, &color);
if (colorP /*&& (bmi >= 0)*/)
	*colorP = color;
	//memcpy (colorP, gameData.pig.tex.bitmapColors + bmi, sizeof (tRgbaColorf));

xSize = objP->info.xSize;

if (nType == OBJ_POWERUP) {
	if ((bEnergy && gameOpts->render.coronas.bPowerups) || (!bEnergy && gameOpts->render.coronas.bWeapons))
		RenderPowerupCorona (objP, color.red, color.green, color.blue,
									coronaIntensities [gameOpts->render.coronas.nObjIntensity]);
	}
if ((objP->info.nType == OBJ_POWERUP) && (objP->info.nId == POW_SHIELD_BOOST) &&
	 !gameStates.app.bNostalgia && gameOpts->render.powerups.b3D && gameOpts->render.powerups.b3DShields) {
	if ((objP->mType.physInfo.velocity.IsZero ()) && (objP->info.movementType != MT_SPINNING)) {
		objP->info.movementType = MT_SPINNING;
		objP->mType.spinRate = objP->info.position.mOrient.UVec () * (I2X (1) / 8);
		}
	//the actual shield in the sprite texture has 3/4 of the textures size
	DrawShieldSphere (objP, 3 * color.red / 2, 3 * color.green / 2, 3 * color.blue / 2, 1.0f, 3 * objP->info.xSize / 4);
	}
else if ((gameOpts->render.bDepthSort > 0) && (fAlpha < 1)) {
	if (bAdditive) {
#if 1
		color.red =
		color.green =
		color.blue = 0.5f;
#else
		if ((nType == OBJ_FIREBALL) && (fScale > 0)) {
			fScale = 1.0f - fScale / 6.0f;
			color.red *= fScale;
			color.green *= fScale;
			color.blue *= fScale;
			}
#endif
		}
	else
		color.red =
		color.green =
		color.blue = 1;
	color.alpha = fAlpha;
	if (bmP->Width () > bmP->Height ())
		transparencyRenderer.AddSprite (bmP, objP->info.position.vPos, &color, xSize, FixMulDiv (xSize, bmP->Height (), bmP->Width ()),
												  iFrame, bAdditive, (nType == OBJ_FIREBALL) ? 10.0f : 0.0f);
	else
		transparencyRenderer.AddSprite (bmP, objP->info.position.vPos, &color, FixMulDiv (xSize, bmP->Width (), bmP->Height ()), xSize,
												  iFrame, bAdditive, (nType == OBJ_FIREBALL) ? 10.0f : 0.0f);
	}
else {
	if (bmP->Width () > bmP->Height ())
		G3DrawBitmap (objP->info.position.vPos, xSize, FixMulDiv (xSize, bmP->Height (), bmP->Width ()), bmP, NULL, fAlpha, nTransp);
	else
		G3DrawBitmap (objP->info.position.vPos, FixMulDiv (xSize, bmP->Width (), bmP->Height ()), xSize, bmP, NULL, fAlpha, nTransp);
	}
gameData.render.nTotalSprites++;
}
Beispiel #13
0
void DoPhysicsSim (tObject *objP)
{
	short					ignoreObjList [MAX_IGNORE_OBJS], nIgnoreObjs;
	int					iseg;
	int					bRetry;
	int					fate;
	vmsVector			vFrame;			//movement in this frame
	vmsVector			vNewPos, ipos;		//position after this frame
	int					count=0;
	short					nObject = OBJ_IDX (objP);
	short					nWallHitSeg, nWallHitSide;
	fvi_info				hi;
	fvi_query			fq;
	vmsVector			vSavePos;
	int					nSaveSeg;
	fix					xDrag;
	fix					xSimTime, xOldSimTime, xTimeScale;
	vmsVector			vStartPos;
	int					bObjStopped=0;
	fix					xMovedTime;			//how long objected moved before hit something
	vmsVector			vSaveP0, vSaveP1;
	tPhysicsInfo		*pi;
	short					nOrigSegment = objP->nSegment;
	int					bBounced = 0;
	tSpeedBoostData	sbd = gameData.objs.speedBoost [nObject];
	int					bDoSpeedBoost = sbd.bBoosted; // && (objP == gameData.objs.console);

Assert (objP->nType != OBJ_NONE);
Assert (objP->movementType == MT_PHYSICS);
if (objP->nType == OBJ_PLAYER && Controls.headingTime)
	objP = objP;
#ifdef _DEBUG
if (bDontMoveAIObjects)
	if (objP->controlType == CT_AI)
		return;
#endif
pi = &objP->mType.physInfo;
DoPhysicsSimRot (objP);
#if 1
if (!(pi->velocity.x || pi->velocity.y || pi->velocity.z)) {
	UnstickObject (objP);
	if (objP == gameData.objs.console)
		gameData.objs.speedBoost [nObject].bBoosted = sbd.bBoosted = 0;
	if (!(pi->thrust.x || pi->thrust.y || pi->thrust.z))
		return;
	}
#endif
nPhysSegs = 0;
bSimpleFVI = (objP->nType != OBJ_PLAYER);
xSimTime = gameData.time.xFrame;
vStartPos = objP->pos;
nIgnoreObjs = 0;
Assert (objP->mType.physInfo.brakes==0);		//brakes not used anymore?
//if uses thrust, cannot have zero xDrag
Assert (!(objP->mType.physInfo.flags&PF_USES_THRUST) || objP->mType.physInfo.drag!=0);
//do thrust & xDrag
if (xDrag = objP->mType.physInfo.drag) {
	int count;
	vmsVector accel, *vel = &objP->mType.physInfo.velocity;
	fix r, k, d, a;

	d = f1_0 - xDrag;
	count = xSimTime / FT;
	r = xSimTime % FT;
	k = FixDiv (r, FT);
	if (objP->mType.physInfo.flags & PF_USES_THRUST) {
		VmVecCopyScale (&accel, &objP->mType.physInfo.thrust, FixDiv (f1_0, objP->mType.physInfo.mass));
		a = (accel.x || accel.y || accel.z);
		if (bDoSpeedBoost && !(a || gameStates.input.bControlsSkipFrame))
			*vel = sbd.vVel;
		else {
			while (count--) {
				if (a)
					VmVecInc (vel, &accel);
				VmVecScale (vel, d);
			}
			//do linear scale on remaining bit of time
			VmVecScaleInc (vel, &accel, k);
			VmVecScale (vel, f1_0 - FixMul (k, xDrag));
			if (bDoSpeedBoost) {
				if (vel->x < sbd.vMinVel.x)
					vel->x = sbd.vMinVel.x;
				else if (vel->x > sbd.vMaxVel.x)
					vel->x = sbd.vMaxVel.x;
				if (vel->y < sbd.vMinVel.y)
					vel->y = sbd.vMinVel.y;
				else if (vel->y > sbd.vMaxVel.y)
					vel->y = sbd.vMaxVel.y;
				if (vel->z < sbd.vMinVel.z)
					vel->z = sbd.vMinVel.z;
				else if (vel->z > sbd.vMaxVel.z)
					vel->z = sbd.vMaxVel.z;
				}
			}
		}
	else {
		fix xTotalDrag = f1_0;
		while (count--)
			xTotalDrag = FixMul (xTotalDrag, d);
		//do linear scale on remaining bit of time
		xTotalDrag = FixMul (xTotalDrag, f1_0-FixMul (k, xDrag));
		VmVecScale (&objP->mType.physInfo.velocity, xTotalDrag);
		}
	}
if (extraGameInfo [IsMultiGame].bFluidPhysics) {
	if (gameData.segs.segment2s [objP->nSegment].special == SEGMENT_IS_WATER)
		xTimeScale = 75;
	else if (gameData.segs.segment2s [objP->nSegment].special == SEGMENT_IS_LAVA)
		xTimeScale = 66;
	else
		xTimeScale = 100;
	}
else
	xTimeScale = 100;
do {
	bRetry = 0;
	//Move the tObject
	VmVecCopyScale (
		&vFrame, 
		&objP->mType.physInfo.velocity, 
		FixMulDiv (xSimTime, xTimeScale, 100));
	if ((vFrame.x == 0) && (vFrame.y == 0) && (vFrame.z == 0))
		break;

retryMove:

	count++;
	//	If retry count is getting large, then we are trying to do something stupid.
	if (count > 3) 	{
		if (objP->nType == OBJ_PLAYER) {
			if (count > 8) {
				if (sbd.bBoosted)
					sbd.bBoosted = 0;
				break;
			}
		} else
			break;
	}
	VmVecAdd (&vNewPos, &objP->pos, &vFrame);
	ignoreObjList [nIgnoreObjs] = -1;
	fq.p0 = &objP->pos;
	fq.startSeg = objP->nSegment;
	fq.p1 = &vNewPos;
	fq.rad = objP->size;
	fq.thisObjNum = nObject;
	fq.ignoreObjList = ignoreObjList;
	fq.flags = FQ_CHECK_OBJS;

	if (objP->nType == OBJ_WEAPON)
		fq.flags |= FQ_TRANSPOINT;
	//if (objP->nType == OBJ_PLAYER)
		fq.flags |= FQ_GET_SEGLIST;

	vSaveP0 = *fq.p0;
	vSaveP1 = *fq.p1;
	fate = FindVectorIntersection (&fq, &hi);
#ifdef _DEBUG
	if (fate == HIT_WALL)
		fate = FindVectorIntersection (&fq, &hi);
#endif
	//	Matt: Mike's hack.
	if (fate == HIT_OBJECT) {
		tObject	*objP = gameData.objs.objects + hi.hit.nObject;

		if ((objP->nType == OBJ_WEAPON) && ((objP->id == PROXIMITY_ID) || (objP->id == SUPERPROX_ID)))
			count--;
	}

#ifdef _DEBUG
	if (fate == HIT_BAD_P0) {
#if 0 //TRACE				
		con_printf (CON_DEBUG, "Warning: Bad p0 in physics! Object = %i, nType = %i [%s]\n", 
			objP - gameData.objs.objects, objP->nType, ObjectType_names [objP->nType]);
#endif
		Int3 ();
	}
#endif

	//if ((objP->nType == OBJ_PLAYER) || (objP->nType == OBJ_ROBOT) || (objP->nType == OBJ_MONSTERBALL)) 
		{
		int i;
#ifdef RELEASE
		int j;
#endif
		if (nPhysSegs && (physSegList [nPhysSegs-1] == hi.segList [0]))
			nPhysSegs--;
#ifdef RELEASE
		j = MAX_FVI_SEGS - nPhysSegs - 1;
		if (j > hi.nSegments)
			j = hi.nSegments;
		memcpy (physSegList + nPhysSegs, hi.segList, j * sizeof (*physSegList));
		nPhysSegs += j;
#else
			for (i = 0; (i < hi.nSegments) && (nPhysSegs < MAX_FVI_SEGS-1); ) {
				if (hi.segList [i] > gameData.segs.nLastSegment)
					LogErr ("Invalid segment in segment list #1\n");
				physSegList [nPhysSegs++] = hi.segList [i++];
				}
#endif
	}

	ipos = hi.hit.vPoint;
	iseg = hi.hit.nSegment;
	nWallHitSide = hi.hit.nSide;
	nWallHitSeg = hi.hit.nSideSegment;
	if (iseg==-1) {		//some sort of horrible error
		if (objP->nType == OBJ_WEAPON)
			objP->flags |= OF_SHOULD_BE_DEAD;
		break;
		}
	Assert (!((fate==HIT_WALL) && ((nWallHitSeg == -1) || (nWallHitSeg > gameData.segs.nLastSegment))));
	vSavePos = objP->pos;			//save the tObject's position
	nSaveSeg = objP->nSegment;
	// update tObject's position and tSegment number
	objP->pos = ipos;
	if (iseg != objP->nSegment)
		RelinkObject (nObject, iseg);
	//if start point not in tSegment, move tObject to center of tSegment
	if (GetSegMasks (&objP->pos, objP->nSegment, 0).centerMask) {	//tObject stuck
		vmsVector	vCenter;
		int n = FindObjectSeg (objP);

		if (n == -1) {
			n = FindSegByPoint (&objP->last_pos, objP->nSegment);
			if (n == -1) {
				objP->flags |= OF_SHOULD_BE_DEAD;
				return;
				}
			}
		objP->pos = objP->last_pos;
		RelinkObject (nObject, n);
		COMPUTE_SEGMENT_CENTER_I (&vCenter, objP->nSegment);
		VmVecDec (&vCenter, &objP->pos);
		if (VmVecMag (&vCenter) > F1_0) {
			VmVecNormalize (&vCenter);
			VmVecScaleFrac (&vCenter, 1, 10);
			}
		VmVecDec (&objP->pos, &vCenter);
		//return;
		}

	//calulate new sim time
	{
		//vmsVector vMoved;
		vmsVector vMoveNormal;
		fix attemptedDist, actualDist;
		xOldSimTime = xSimTime;
		actualDist = VmVecNormalizedDir (&vMoveNormal, &objP->pos, &vSavePos);
		if ((fate == HIT_WALL) && (VmVecDot (&vMoveNormal, &vFrame) < 0)) {		//moved backwards
			//don't change position or xSimTime
			objP->pos = vSavePos;
			//iseg = objP->nSegment;		//don't change tSegment
			if (nSaveSeg != iseg)
				RelinkObject (nObject, nSaveSeg);
			if (bDoSpeedBoost) {
//					int h = FindSegByPoint (&vNewPos, -1);
				objP->pos = vStartPos;
				SetSpeedBoostVelocity (nObject, -1, -1, -1, -1, -1, &vStartPos, &sbd.vDest, 0);
				VmVecCopyScale (&vFrame, &sbd.vVel, xSimTime);
				goto retryMove;
				}
			xMovedTime = 0;
			}
		else {
//retryMove2:
			attemptedDist = VmVecMag (&vFrame);
			xSimTime = FixMulDiv (xSimTime, attemptedDist-actualDist, attemptedDist);
			xMovedTime = xOldSimTime - xSimTime;
			if ((xSimTime < 0) || (xSimTime > xOldSimTime)) {
				xSimTime = xOldSimTime;
				xMovedTime = 0;
				}
			}
		}

	switch (fate) {
		case HIT_WALL: {
			vmsVector vMoved;
			fix xHitSpeed, xWallPart, xSideDist, xSideDists [6];
			short nSegment;
			// Find hit speed	

#if 0//def _DEBUG
			if (objP->nType == OBJ_PLAYER)
				HUDMessage (0, "WALL CONTACT");
			fate = FindVectorIntersection (&fq, &hi);
#endif
			VmVecSub (&vMoved, &objP->pos, &vSavePos);
			xWallPart = VmVecDot (&vMoved, &hi.hit.vNormal);
			if (xWallPart && (xMovedTime > 0) && (xHitSpeed = -FixDiv (xWallPart, xMovedTime)) > 0) {
				CollideObjectWithWall (objP, xHitSpeed, nWallHitSeg, nWallHitSide, &hi.hit.vPoint);
#if 0//def _DEBUG
				if (objP->nType == OBJ_PLAYER)
					HUDMessage (0, "BUMP!");
#endif
				}
			else {
#if 0//def _DEBUG
				if (objP->nType == OBJ_PLAYER)
					HUDMessage (0, "SCREEEEEEEEEECH");
#endif
				ScrapeObjectOnWall (objP, nWallHitSeg, nWallHitSide, &hi.hit.vPoint);
				}
			Assert (nWallHitSeg > -1);
			Assert (nWallHitSide > -1);
			GetSideDistsAll (&objP->pos, nWallHitSeg, xSideDists);
			if ((xSideDist = xSideDists [nWallHitSide]) && (xSideDist < objP->size - objP->size / 100)) {
#if 1
				float r = 0.1f;
#else
				float r;
				xSideDist = objP->size - xSideDist;
				r = ((float) xSideDist / (float) objP->size) * f2fl (objP->size);
#endif
				objP->pos.x += (fix) ((float) hi.hit.vNormal.x * r);
				objP->pos.y += (fix) ((float) hi.hit.vNormal.y * r);
				objP->pos.z += (fix) ((float) hi.hit.vNormal.z * r);
				nSegment = FindSegByPoint (&objP->pos, objP->nSegment);
				if (nSegment != objP->nSegment)
					RelinkObject (OBJ_IDX (objP), nSegment);
#if 0//def _DEBUG
				if (objP->nType == OBJ_PLAYER)
					HUDMessage (0, "PENETRATING WALL (%d, %1.4f)", objP->size - xSideDists [nWallHitSide], r);
#endif
				bRetry = 1;
				}
			if (!(objP->flags & OF_SHOULD_BE_DEAD)) {
				int forcefield_bounce;		//bounce off a forcefield

				Assert (gameStates.app.cheats.bBouncingWeapons || !(objP->mType.physInfo.flags & PF_STICK && objP->mType.physInfo.flags & PF_BOUNCE));	//can't be bounce and stick
				forcefield_bounce = (gameData.pig.tex.pTMapInfo [gameData.segs.segments [nWallHitSeg].sides [nWallHitSide].nBaseTex].flags & TMI_FORCE_FIELD);
				if (!forcefield_bounce && (objP->mType.physInfo.flags & PF_STICK)) {		//stop moving
					AddStuckObject (objP, nWallHitSeg, nWallHitSide);
					VmVecZero (&objP->mType.physInfo.velocity);
					bObjStopped = 1;
					bRetry = 0;
				}
				else {				// Slide tObject along wall
					int bCheckVel = 0;
					//We're constrained by wall, so subtract wall part from
					//velocity vector
					xWallPart = VmVecDot (&hi.hit.vNormal, &objP->mType.physInfo.velocity);
					if (forcefield_bounce || (objP->mType.physInfo.flags & PF_BOUNCE)) {		//bounce off wall
						xWallPart *= 2;	//Subtract out wall part twice to achieve bounce
						if (forcefield_bounce) {
							bCheckVel = 1;				//check for max velocity
							if (objP->nType == OBJ_PLAYER)
								xWallPart *= 2;		//player bounce twice as much
							}
						if (objP->mType.physInfo.flags & PF_BOUNCES_TWICE) {
							Assert (objP->mType.physInfo.flags & PF_BOUNCE);
							if (objP->mType.physInfo.flags & PF_BOUNCED_ONCE)
								objP->mType.physInfo.flags &= ~ (PF_BOUNCE+PF_BOUNCED_ONCE+PF_BOUNCES_TWICE);
							else
								objP->mType.physInfo.flags |= PF_BOUNCED_ONCE;
							}
						bBounced = 1;		//this tObject bBounced
						}
					VmVecScaleInc (&objP->mType.physInfo.velocity, &hi.hit.vNormal, -xWallPart);
					if (bCheckVel) {
						fix vel = VmVecMag (&objP->mType.physInfo.velocity);
						if (vel > MAX_OBJECT_VEL)
							VmVecScale (&objP->mType.physInfo.velocity, FixDiv (MAX_OBJECT_VEL, vel));
						}
					if (bBounced && (objP->nType == OBJ_WEAPON))
						VmVector2Matrix (&objP->orient, &objP->mType.physInfo.velocity, &objP->orient.uVec, NULL);
					bRetry = 1;
					}
				}
			break;
			}

		case HIT_OBJECT: {
			vmsVector vOldVel;
			vmsVector	*ppos0, *ppos1, vHitPos;
			fix			size0, size1;
			// Mark the hit tObject so that on a retry the fvi code
			// ignores this tObject.
			//if (bSpeedBoost && (objP == gameData.objs.console))
			//	break;
			Assert (hi.hit.nObject != -1);
			ppos0 = &gameData.objs.objects [hi.hit.nObject].pos;
			ppos1 = &objP->pos;
			size0 = gameData.objs.objects [hi.hit.nObject].size;
			size1 = objP->size;
			//	Calculcate the hit point between the two objects.
			Assert (size0+size1 != 0);	// Error, both sizes are 0, so how did they collide, anyway?!?
			//VmVecScale (VmVecSub (&pos_hit, ppos1, ppos0), FixDiv (size0, size0 + size1);
			//VmVecInc (&pos_hit, ppos0);
			VmVecSub (&vHitPos, ppos1, ppos0);
			VmVecScaleAdd (&vHitPos, ppos0, &vHitPos, FixDiv (size0, size0 + size1));
			vOldVel = objP->mType.physInfo.velocity;
			CollideTwoObjects (objP, gameData.objs.objects + hi.hit.nObject, &vHitPos);
			if (sbd.bBoosted && (objP == gameData.objs.console))
				objP->mType.physInfo.velocity = vOldVel;

			// Let tObject continue its movement
			if (!(objP->flags&OF_SHOULD_BE_DEAD) )	{
				if (objP->mType.physInfo.flags&PF_PERSISTENT || (vOldVel.x == objP->mType.physInfo.velocity.x && vOldVel.y == objP->mType.physInfo.velocity.y && vOldVel.z == objP->mType.physInfo.velocity.z)) {
					//if (gameData.objs.objects [hi.hit.nObject].nType == OBJ_POWERUP)
						ignoreObjList [nIgnoreObjs++] = hi.hit.nObject;
					bRetry = 1;
					}
				}
			break;
			}	
		case HIT_NONE:		
#ifdef TACTILE
			if (TactileStick && objP==gameData.objs.console && !(FrameCount & 15))
				Tactile_Xvibrate_clear ();
	#endif
			break;

		#ifdef _DEBUG
		case HIT_BAD_P0:
			Int3 ();		// Unexpected collision nType: start point not in specified tSegment.
#if TRACE				
			con_printf (CON_DEBUG, "Warning: Bad p0 in physics!!!\n");
#endif
			break;
		default:
			// Unknown collision nType returned from FindVectorIntersection!!
			Int3 ();
			break;
		#endif
		}
	} while (bRetry);
	//	Pass retry count info to AI.
	if (objP->controlType == CT_AI) {
		if (count > 0) {
			gameData.ai.localInfo [nObject].nRetryCount = count-1;
#ifdef _DEBUG
			Total_retries += count-1;
			Total_sims++;
#endif
			}
		}

	//I'm not sure why we do this.  I wish there were a comment that
	//explained it.  I think maybe it only needs to be done if the tObject
	//is sliding, but I don't know
	if (!(sbd.bBoosted || bObjStopped || bBounced))	{	//Set velocity from actual movement
		vmsVector vMoved;
		VmVecSub (&vMoved, &objP->pos, &vStartPos);
		VmVecCopyScale (&objP->mType.physInfo.velocity, &vMoved, 
								 FixMulDiv (FixDiv (f1_0, gameData.time.xFrame), 100, xTimeScale));
#ifdef BUMP_HACK
		if (objP == gameData.objs.console && 
			 objP->mType.physInfo.velocity.x==0 && 
			 objP->mType.physInfo.velocity.y==0 && 
			 objP->mType.physInfo.velocity.z==0 &&
			 (objP->mType.physInfo.thrust.x!=0 || 
			  objP->mType.physInfo.thrust.y!=0 ||
			  objP->mType.physInfo.thrust.z!=0)) {
			vmsVector vCenter, vBump;
			//bump player a little towards vCenter of tSegment to unstick
			COMPUTE_SEGMENT_CENTER_I (&vCenter, objP->nSegment);
			//HUDMessage (0, "BUMP! %d %d", d1, d2);
			//don't bump player toward vCenter of reactor tSegment
			if (gameData.segs.segment2s [objP->nSegment].special == SEGMENT_IS_CONTROLCEN)
				VmVecNegate (&vBump);
			VmVecScaleInc (&objP->pos, &vBump, objP->size/5);
			//if moving away from seg, might move out of seg, so update
			if (gameData.segs.segment2s [objP->nSegment].special == SEGMENT_IS_CONTROLCEN)
				UpdateObjectSeg (objP);
			}
#endif
		}

	//Assert (check_point_in_seg (&objP->pos, objP->nSegment, 0).centerMask==0);
	//if (objP->controlType == CT_FLYING)
	if (objP->mType.physInfo.flags & PF_LEVELLING)
		DoPhysicsAlignObject (objP);

	//hack to keep player from going through closed doors
	if ((objP->nType == OBJ_PLAYER) && (objP->nSegment != nOrigSegment) && 
		 (gameStates.app.cheats.bPhysics != 0xBADA55)) {
		int nSide;
		nSide = FindConnectedSide (gameData.segs.segments + objP->nSegment, gameData.segs.segments + nOrigSegment);
		if (nSide != -1) {
			if (!(WALL_IS_DOORWAY (gameData.segs.segments + nOrigSegment, nSide, NULL) & WID_FLY_FLAG)) {
				tSide *sideP;
				int nVertex, nFaces;
				fix dist;
				int vertex_list [6];

				//bump tObject back
				sideP = gameData.segs.segments [nOrigSegment].sides + nSide;
				if (nOrigSegment==-1)
					Error ("nOrigSegment == -1 in physics");
				CreateAbsVertexLists (&nFaces, vertex_list, nOrigSegment, nSide);
				//let'sideP pretend this wall is not triangulated
				nVertex = vertex_list [0];
				if (nVertex > vertex_list [1])
					nVertex = vertex_list [1];
				if (nVertex > vertex_list [2])
					nVertex = vertex_list [2];
				if (nVertex > vertex_list [3])
					nVertex = vertex_list [3];
#ifdef COMPACT_SEGS
				{
					vmsVector _vn;
					get_side_normal (gameData.segs.segments + nOrigSegment, nSide, 0, &_vn);
					dist = VmDistToPlane (&vStartPos, &_vn, &gameData.segs.vertices [nVertex]);
					VmVecScaleAdd (&objP->pos, &vStartPos, &_vn, objP->size-dist);
				}
#else
				dist = VmDistToPlane (&vStartPos, sideP->normals, gameData.segs.vertices + nVertex);
				VmVecScaleAdd (&objP->pos, &vStartPos, sideP->normals, objP->size-dist);
#endif
				UpdateObjectSeg (objP);
				}
			}
		}

	//if end point not in tSegment, move tObject to last pos, or tSegment center
	if (GetSegMasks (&objP->pos, objP->nSegment, 0).centerMask) {
		if (FindObjectSeg (objP) == -1) {
			int n;

			if (objP->nType==OBJ_PLAYER && (n=FindSegByPoint (&objP->last_pos, objP->nSegment))!=-1) {
				objP->pos = objP->last_pos;
				RelinkObject (nObject, n);
				}
			else {
				COMPUTE_SEGMENT_CENTER_I (&objP->pos, objP->nSegment);
				objP->pos.x += nObject;
				}
			if (objP->nType == OBJ_WEAPON)
				objP->flags |= OF_SHOULD_BE_DEAD;
		}
	}
UnstickObject (objP);
}
Beispiel #14
0
CObject* CreateExplosion (CObject* parentP, short nSegment, CFixVector& vPos, fix xSize,
								  ubyte nVClip, fix xMaxDamage, fix xMaxDistance, fix xMaxForce, short nParent)
{
	short			nObject;
	CObject		*explObjP, *objP;
	fix			dist, force, damage;
	CFixVector	vHit, vForce;
	int			nType, id;
	int			flash = parentP ? static_cast<int> (gameData.weapons.info [parentP->info.nId].flash) : 0;

nObject = CreateFireball (nVClip, nSegment, vPos, xSize, RT_FIREBALL);

if (nObject < 0) {
#if TRACE
	console.printf (1, "Can'nType create CObject in /*Object*/CreateExplosionSub.\n");
#endif
	return NULL;
	}

explObjP = OBJECTS + nObject;
//now set explosion-specific data
explObjP->info.xLifeLeft = gameData.eff.vClips [0][nVClip].xTotalTime;
explObjP->cType.explInfo.nSpawnTime = -1;
explObjP->cType.explInfo.nDeleteObj = -1;
explObjP->cType.explInfo.nDeleteTime = -1;

if (xMaxDamage <= 0)
	return explObjP;
// -- now legal for xBadAss explosions on a CWall. Assert (this != NULL);
FORALL_OBJS (objP, i) {
	nType = objP->info.nType;
	id = objP->info.nId;
	//	Weapons used to be affected by xBadAss explosions, but this introduces serious problems.
	//	When a smart bomb blows up, if one of its children goes right towards a nearby CWall, it will
	//	blow up, blowing up all the children.  So I remove it.  MK, 09/11/94
	if (objP == parentP)
		continue;
	if (objP->info.nFlags & OF_SHOULD_BE_DEAD)
		continue;
	if (nType == OBJ_WEAPON) {
		if (!WeaponIsMine (objP->info.nId))
		continue;
		}
	else if (nType == OBJ_ROBOT) {
		if (nParent < 0)
			continue;
		if ((OBJECTS [nParent].info.nType == OBJ_ROBOT) && (OBJECTS [nParent].info.nId == id))
			continue;
		}
	else if ((nType != OBJ_REACTOR) && (nType != OBJ_PLAYER))
		continue;
	dist = CFixVector::Dist (objP->info.position.vPos, explObjP->info.position.vPos);
	// Make damage be from 'xMaxDamage' to 0.0, where 0.0 is 'xMaxDistance' away;
	if (dist >= xMaxDistance)
		continue;
	if (!ObjectToObjectVisibility (explObjP, objP, FQ_TRANSWALL))
		continue;
	damage = xMaxDamage - FixMulDiv (dist, xMaxDamage, xMaxDistance);
	force = xMaxForce - FixMulDiv (dist, xMaxForce, xMaxDistance);
	// Find the force vector on the CObject
	CFixVector::NormalizedDir(vForce, objP->info.position.vPos, explObjP->info.position.vPos);
	vForce *= force;
	// Find where the point of impact is... (vHit)
	vHit = explObjP->info.position.vPos - objP->info.position.vPos;
	vHit *= (FixDiv (objP->info.xSize, objP->info.xSize + dist));
	if (nType == OBJ_WEAPON) {
		objP->ApplyForce (vForce);
		if (WeaponIsMine (objP->info.nId) && (FixMul (dist, force) > I2X (8000))) {	//prox bombs have chance of blowing up
			objP->Die ();
			objP->ExplodeBadassWeapon (objP->info.position.vPos);
			}
		}
	else if (nType == OBJ_ROBOT) {
		CFixVector	vNegForce;
		fix			xScale = -2 * (7 - gameStates.app.nDifficultyLevel) / 8;

		objP->ApplyForce (vForce);
		//	If not a boss, stun for 2 seconds at 32 force, 1 second at 16 force
		if (flash && !ROBOTINFO (objP->info.nId).bossFlag) {
			tAIStaticInfo	*aip = &objP->cType.aiInfo;
			int				nForce = X2I (FixDiv (vForce.Mag () * flash, gameData.time.xFrame) / 128) + 2;

			if (explObjP->cType.aiInfo.SKIP_AI_COUNT * gameData.time.xFrame >= I2X (1))
				aip->SKIP_AI_COUNT--;
			else {
				aip->SKIP_AI_COUNT += nForce;
				objP->mType.physInfo.rotThrust [X] = ((d_rand () - 16384) * nForce) / 16;
				objP->mType.physInfo.rotThrust [Y] = ((d_rand () - 16384) * nForce) / 16;
				objP->mType.physInfo.rotThrust [Z] = ((d_rand () - 16384) * nForce) / 16;
				objP->mType.physInfo.flags |= PF_USES_THRUST;
				}
			}
		vNegForce [X] = vForce [X] * xScale;
		vNegForce [Y] = vForce [Y] * xScale;
		vNegForce [Z] = vForce [Z] * xScale;
		objP->ApplyRotForce (vNegForce);
		if (objP->info.xShields >= 0) {
			if (ROBOTINFO (objP->info.nId).bossFlag &&
				 bossProps [gameStates.app.bD1Mission][ROBOTINFO (objP->info.nId).bossFlag - BOSS_D2].bInvulKinetic)
				damage /= 4;
			if (objP->ApplyDamageToRobot (damage, nParent)) {
				if (!gameStates.gameplay.bNoBotAI && parentP && (nParent == LOCALPLAYER.nObject))
					cockpit->AddPointsToScore (ROBOTINFO (objP->info.nId).scoreValue);
				}
			}
		if (!flash && ROBOTINFO (objP->info.nId).companion)
			BuddyOuchMessage (damage);
		}
	else if (nType == OBJ_REACTOR) {
		if (objP->info.xShields >= 0)
			objP->ApplyDamageToReactor (damage, nParent);
		}
	else if (nType == OBJ_PLAYER) {
		CObject*		killerP = NULL;
		CFixVector	vRotForce;

		//	Hack!Warning!Test code!
		if (flash && (objP->info.nId == gameData.multiplayer.nLocalPlayer)) {
			int fe = min (I2X (4), force * flash / 32);	//	For four seconds or less
			if (parentP->cType.laserInfo.parent.nSignature == gameData.objs.consoleP->info.nSignature) {
				fe /= 2;
				force /= 2;
				}
			if (force > I2X (1)) {
				paletteManager.SetFlashDuration (fe);
				force = PK1 + X2I (PK2 * force);
				paletteManager.BumpEffect (force, force, force);
#if TRACE
				console.printf (CON_DBG, "force = %7.3f, adding %i\n", X2F (force), PK1 + X2I (PK2*force));
#endif
				}
			}
		if (parentP && IsMultiGame && (parentP->info.nType == OBJ_PLAYER))
			killerP = parentP;
		vRotForce = vForce;
		if (nParent > -1) {
			killerP = OBJECTS + nParent;
			if (killerP != gameData.objs.consoleP)		// if someone else whacks you, cut force by 2x
				vRotForce [X] /= 2;
				vRotForce [Y] /= 2;
				vRotForce [Z] /= 2;
			}
		vRotForce [X] /= 2;
		vRotForce [Y] /= 2;
		vRotForce [Z] /= 2;
		objP->ApplyForce (vForce);
		objP->ApplyRotForce (vRotForce);
		if (gameStates.app.nDifficultyLevel == 0)
			damage /= 4;
		if (objP->info.xShields >= 0)
			objP->ApplyDamageToPlayer (killerP, damage);
		}
	}
Beispiel #15
0
tObject *ObjectCreateExplosionSub (tObject *objP, short nSegment, vmsVector *vPos, fix xSize, 
											  ubyte nVClip, fix xMaxDamage, fix xMaxDistance, fix xMaxForce, short nParent)
{
	short			nObject;
	tObject		*explObjP, *obj0P;
	fix			dist, force, damage;
	vmsVector	pos_hit, vForce;
	int			i, t, id;

nObject = CreateObject (OBJ_FIREBALL, nVClip, -1, nSegment, vPos, &vmdIdentityMatrix, xSize, 
							   CT_EXPLOSION, MT_NONE, RT_FIREBALL, 1);

if (nObject < 0) {
#if TRACE
	con_printf (1, "Can't create tObject in ObjectCreateExplosionSub.\n");
#endif
	return NULL;
	}

explObjP = gameData.objs.objects + nObject;
//now set explosion-specific data
explObjP->lifeleft = gameData.eff.vClips [0][nVClip].xTotalTime;
explObjP->cType.explInfo.nSpawnTime = -1;
explObjP->cType.explInfo.nDeleteObj = -1;
explObjP->cType.explInfo.nDeleteTime = -1;

if (xMaxDamage <= 0)
	return explObjP;
// -- now legal for xBadAss explosions on a tWall. Assert (objP != NULL);
for (i = 0, obj0P = gameData.objs.objects; i <= gameData.objs.nLastObject; i++, obj0P++) {
	t = obj0P->nType;
	id = obj0P->id;
	//	Weapons used to be affected by xBadAss explosions, but this introduces serious problems.
	//	When a smart bomb blows up, if one of its children goes right towards a nearby tWall, it will
	//	blow up, blowing up all the children.  So I remove it.  MK, 09/11/94
	if (obj0P == objP)
		continue;
	if (obj0P->flags & OF_SHOULD_BE_DEAD)
		continue;
	if (t == OBJ_WEAPON) {
		if (!WeaponIsMine (obj0P->id)) 
		continue;
		}
	else if (t == OBJ_ROBOT) {
		if (nParent < 0)
			continue;
		if ((gameData.objs.objects [nParent].nType == OBJ_ROBOT) && (gameData.objs.objects [nParent].id == id))
			continue;
		}
	else if ((t != OBJ_REACTOR) && (t != OBJ_PLAYER))
		continue;
	dist = VmVecDistQuick (&obj0P->position.vPos, &explObjP->position.vPos);
	// Make damage be from 'xMaxDamage' to 0.0, where 0.0 is 'xMaxDistance' away;
	if (dist >= xMaxDistance)
		continue;
	if (!ObjectToObjectVisibility (explObjP, obj0P, FQ_TRANSWALL))
		continue;
	damage = xMaxDamage - FixMulDiv (dist, xMaxDamage, xMaxDistance);
	force = xMaxForce - FixMulDiv (dist, xMaxForce, xMaxDistance);
	// Find the force vector on the tObject
	VmVecNormalizedDirQuick (&vForce, &obj0P->position.vPos, &explObjP->position.vPos);
	VmVecScale (&vForce, force);
	// Find where the point of impact is... (pos_hit)
	VmVecSub (&pos_hit, &explObjP->position.vPos, &obj0P->position.vPos);
	VmVecScale (&pos_hit, FixDiv (obj0P->size, obj0P->size + dist));
	if (t == OBJ_WEAPON) {
		PhysApplyForce (obj0P, &vForce);
		if (WeaponIsMine (obj0P->id) && (FixMul (dist, force) > i2f (8000))) {	//prox bombs have chance of blowing up
			KillObject (obj0P);
			ExplodeBadassWeapon (obj0P, &obj0P->position.vPos);
			}
		}
	else if (t == OBJ_ROBOT) {
		vmsVector	vNegForce;
		fix			xScale = -2 * (7 - gameStates.app.nDifficultyLevel) / 8;

		PhysApplyForce (obj0P, &vForce);
		//	If not a boss, stun for 2 seconds at 32 force, 1 second at 16 force
		if (objP && (!ROBOTINFO (obj0P->id).bossFlag) && (gameData.weapons.info [objP->id].flash)) {
			tAIStatic	*aip = &obj0P->cType.aiInfo;
			int			force_val = f2i (FixDiv (VmVecMagQuick (&vForce) * gameData.weapons.info [objP->id].flash, gameData.time.xFrame)/128) + 2;

			if (explObjP->cType.aiInfo.SKIP_AI_COUNT * gameData.time.xFrame >= F1_0) 
				aip->SKIP_AI_COUNT--;
			else {
				aip->SKIP_AI_COUNT += force_val;
				obj0P->mType.physInfo.rotThrust.p.x = ((d_rand () - 16384) * force_val)/16;
				obj0P->mType.physInfo.rotThrust.p.y = ((d_rand () - 16384) * force_val)/16;
				obj0P->mType.physInfo.rotThrust.p.z = ((d_rand () - 16384) * force_val)/16;
				obj0P->mType.physInfo.flags |= PF_USES_THRUST;
				}
			}
		vNegForce.p.x = vForce.p.x * xScale;
		vNegForce.p.y = vForce.p.y * xScale;
		vNegForce.p.z = vForce.p.z * xScale;
		PhysApplyRot (obj0P, &vNegForce);
		if (obj0P->shields >= 0) {
			if (ROBOTINFO (obj0P->id).bossFlag &&
				 bossProps [gameStates.app.bD1Mission][ROBOTINFO (obj0P->id).bossFlag-BOSS_D2].bInvulKinetic)
				damage /= 4;
			if (ApplyDamageToRobot (obj0P, damage, nParent) && objP && (nParent == LOCALPLAYER.nObject))
				AddPointsToScore (ROBOTINFO (obj0P->id).scoreValue);
			}
		if (objP && (ROBOTINFO (obj0P->id).companion) && !gameData.weapons.info [objP->id].flash) {
			int	i, count;
			char	szOuch [6*4 + 2];

			count = f2i (damage / 8);
			if (count > 4)
				count = 4;
			else if (count <= 0)
				count = 1;
			szOuch [0] = 0;
			for (i = 0; i < count; i++) {
				strcat (szOuch, TXT_BUDDY_OUCH);
				strcat (szOuch, " ");
				}
			BuddyMessage (szOuch);
			}
		}
	else if (t == OBJ_REACTOR) {
		if (obj0P->shields >= 0)
			ApplyDamageToReactor (obj0P, damage, nParent);
		}
	else if (t == OBJ_PLAYER) {
		tObject		*killerP = NULL;
		vmsVector	vForce2;

		//	Hack!Warning!Test code!
		if (objP && gameData.weapons.info [objP->id].flash && obj0P->id==gameData.multiplayer.nLocalPlayer) {
			int fe = min (F1_0*4, force*gameData.weapons.info [objP->id].flash/32);	//	For four seconds or less
			if (objP->cType.laserInfo.nParentSig == gameData.objs.console->nSignature) {
				fe /= 2;
				force /= 2;
				}
			if (force > F1_0) {
				gameData.render.xFlashEffect = fe;
				PALETTE_FLASH_ADD (PK1 + f2i (PK2*force), PK1 + f2i (PK2*force), PK1 + f2i (PK2*force));
#if TRACE
				con_printf (CONDBG, "force = %7.3f, adding %i\n", f2fl (force), PK1 + f2i (PK2*force));
#endif
				}
			}
		if (objP && IsMultiGame && (objP->nType == OBJ_PLAYER))
			killerP = objP;
		vForce2 = vForce;
		if (nParent > -1) {
			killerP = gameData.objs.objects + nParent;
			if (killerP != gameData.objs.console)		// if someone else whacks you, cut force by 2x
				vForce2.p.x /= 2;
				vForce2.p.y /= 2;
				vForce2.p.z /= 2;
			}
		vForce2.p.x /= 2;
		vForce2.p.y /= 2;
		vForce2.p.z /= 2;
		PhysApplyForce (obj0P, &vForce);
		PhysApplyRot (obj0P, &vForce2);
		if (gameStates.app.nDifficultyLevel == 0)
			damage /= 4;
		if (obj0P->shields >= 0)
			ApplyDamageToPlayer (obj0P, killerP, damage);
		}
	}
return explObjP;
}