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 } }
//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); }
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 }
//------------------------------------------------------------------------------ //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 }
//------------------------------------------------------------------------------ //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(); }
//------------------------------------------------------------------------------ //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 (); }
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); }
//------------------------------------------------------------------------------ //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; }
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; }
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; }
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++; }
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); }
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); } }
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; }