void DoMissileSmoke (CObject *objP) { int nParts, nSpeed, nLife, nObject, nSmoke; float nScale = 1.75f; tThrusterInfo ti; nObject = objP->Index (); if (!(SHOW_SMOKE && gameOpts->render.particles.bMissiles)) { if (particleManager.GetObjectSystem (nObject) >= 0) KillObjectSmoke (nObject); return; } if ((objP->info.xShields < 0) || (objP->info.nFlags & (OF_SHOULD_BE_DEAD | OF_DESTROYED))) nParts = 0; else { nSpeed = WI_speed (objP->info.nId, gameStates.app.nDifficultyLevel); nLife = gameOpts->render.particles.nLife [3] + 1; #if 1 nParts = int (MSL_MAX_PARTS * X2F (nSpeed) / (34.0f * (4 - nLife))); if ((objP->info.nId == EARTHSHAKER_MEGA_ID) || (objP->info.nId == ROBOT_SHAKER_MEGA_ID)) nParts /= 2; #else nParts = (objP->info.nId == EARTHSHAKER_ID) ? 1500 : (objP->info.nId == MEGAMSL_ID) ? 1400 : (objP->info.nId == SMARTMSL_ID) ? 1300 : 1200; #endif } if (nParts) { if (0 > (nSmoke = particleManager.GetObjectSystem (nObject))) { if (!gameOpts->render.particles.bSyncSizes) { nParts = -MAX_PARTICLES (nParts, gameOpts->render.particles.nDens [3]); nScale = PARTICLE_SIZE (gameOpts->render.particles.nSize [3], nScale); } nSmoke = particleManager.Create (&objP->info.position.vPos, NULL, NULL, objP->info.nSegment, 1, nParts, nScale, gameOpts->render.particles.bSyncSizes ? -1 : gameOpts->render.particles.nSize [3], 1, nLife * MSL_PART_LIFE, MSL_PART_SPEED, SMOKE_PARTICLES, nObject, smokeColors + 1, 1, -1); if (nSmoke < 0) return; particleManager.SetObjectSystem (nObject, nSmoke); } CalcThrusterPos (objP, &ti, 0); particleManager.SetPos (nSmoke, ti.vPos, NULL, objP->info.nSegment); } else KillObjectSmoke (nObject); }
void DoMissileSmoke (tObject *objP) { int nParts, nSpeed, nLife, i; float nScale = 1.5f; tThrusterInfo ti; i = OBJ_IDX (objP); if (!(SHOW_SMOKE && gameOpts->render.smoke.bMissiles)) { if (gameData.smoke.objects [i] >= 0) KillObjectSmoke (i); return; } if ((objP->info.xShields < 0) || (objP->info.nFlags & (OF_SHOULD_BE_DEAD | OF_DESTROYED))) nParts = 0; else { nSpeed = WI_speed (objP->info.nId, gameStates.app.nDifficultyLevel); nLife = gameOpts->render.smoke.nLife [3] + 1; #if 1 nParts = (int) (MSL_MAX_PARTS * X2F (nSpeed) / (40.0f * (4 - nLife))); if ((objP->info.nId == EARTHSHAKER_MEGA_ID) || (objP->info.nId == ROBOT_SHAKER_MEGA_ID)) nParts /= 2; #else nParts = (objP->info.nId == EARTHSHAKER_ID) ? 1500 : (objP->info.nId == MEGAMSL_ID) ? 1400 : (objP->info.nId == SMARTMSL_ID) ? 1300 : 1200; #endif } if (nParts) { if (gameData.smoke.objects [i] < 0) { if (!gameOpts->render.smoke.bSyncSizes) { nParts = -MAX_PARTICLES (nParts, gameOpts->render.smoke.nDens [3]); nScale = PARTICLE_SIZE (gameOpts->render.smoke.nSize [3], nScale); } SetSmokeObject (i, CreateSmoke (&objP->info.position.vPos, NULL, NULL, objP->info.nSegment, 1, nParts, nScale, gameOpts->render.smoke.bSyncSizes ? -1 : gameOpts->render.smoke.nSize [3], 1, nLife * MSL_PART_LIFE, MSL_PART_SPEED, SMOKE_PARTICLES, i, smokeColors + 1, 1, -1)); } CalcThrusterPos (objP, &ti, 0); SetSmokePos (gameData.smoke.objects [i], ti.vPos, NULL, objP->info.nSegment); } else KillObjectSmoke (i); }
CFixVector *TransformGunPoint (CObject *objP, CFixVector *vGunPoints, int nGun, fix xDelay, ubyte nLaserType, CFixVector *vMuzzle, CFixMatrix *mP) { int bSpectate = SPECTATOR (objP); tObjTransformation* posP = bSpectate ? &gameStates.app.playerPos : &objP->info.position; CFixMatrix m, *viewP; CFixVector v [2]; #if FULL_COCKPIT_OFFS int bLaserOffs = ((gameStates.render.cockpit.nType == CM_FULL_COCKPIT) && (objP->Index () == LOCALPLAYER.nObject)); #else int bLaserOffs = 0; #endif if (nGun < 0) { // use center between gunPoints nGun and nGun + 1 *v = vGunPoints [-nGun] + vGunPoints [-nGun - 1]; // VmVecScale (VmVecAdd (v, vGunPoints - nGun, vGunPoints - nGun - 1), I2X (1) / 2); *v *= (I2X (1) / 2); } else { v [0] = vGunPoints [nGun]; if (bLaserOffs) v [0] += posP->mOrient.UVec () * LASER_OFFS; } if (!mP) mP = &m; if (bSpectate) { viewP = mP; *viewP = posP->mOrient.Transpose (); } else viewP = objP->View (); v[1] = *viewP * v [0]; memcpy (mP, &posP->mOrient, sizeof (CFixMatrix)); if (nGun < 0) v[1] += (*mP).UVec () * (-2 * v->Mag ()); (*vMuzzle) = posP->vPos + v [1]; // If supposed to fire at a delayed time (xDelay), then move this point backwards. if (xDelay) *vMuzzle += mP->FVec () * (-FixMul (xDelay, WI_speed (nLaserType, gameStates.app.nDifficultyLevel))); return vMuzzle; }
void ReadFlyingControls (CObject *objP) { fix forwardThrustTime; CObject* gmObjP; int bMulti; if (gameData.time.xFrame <= 0) return; if (gameStates.app.bPlayerIsDead || gameStates.app.bEnterGame) { StopPlayerMovement (); FlushInput (); /* VmVecZero(&objP->mType.physInfo.rotThrust); VmVecZero(&objP->mType.physInfo.thrust); VmVecZero(&objP->mType.physInfo.velocity); */ gameStates.app.bEnterGame--; return; } if ((objP->info.nType != OBJ_PLAYER) || (objP->info.nId != gameData.multiplayer.nLocalPlayer)) return; //references to CPlayerShip require that this obj be the CPlayerData tGuidedMissileInfo *gmiP = gameData.objs.guidedMissile + gameData.multiplayer.nLocalPlayer; gmObjP = gmiP->objP; if (gmObjP && (gmObjP->info.nSignature == gmiP->nSignature)) { CAngleVector vRotAngs; CFixMatrix mRot, mOrient; fix speed; //this is a horrible hack. guided missile stuff should not be //handled in the middle of a routine that is dealing with the CPlayerData objP->mType.physInfo.rotThrust.SetZero (); vRotAngs [PA] = Controls [0].pitchTime / 2 + gameStates.gameplay.seismic.nMagnitude / 64; vRotAngs [BA] = Controls [0].bankTime / 2 + gameStates.gameplay.seismic.nMagnitude / 16; vRotAngs [HA] = Controls [0].headingTime / 2 + gameStates.gameplay.seismic.nMagnitude / 64; mRot = CFixMatrix::Create (vRotAngs); mOrient = gmObjP->info.position.mOrient * mRot; gmObjP->info.position.mOrient = mOrient; speed = WI_speed (gmObjP->info.nId, gameStates.app.nDifficultyLevel); gmObjP->mType.physInfo.velocity = gmObjP->info.position.mOrient.FVec () * speed; if(IsMultiGame) MultiSendGuidedInfo (gmObjP, 0); } else { #if DBG if (Controls [0].headingTime) Controls [0].headingTime = Controls [0].headingTime; #endif objP->mType.physInfo.rotThrust = CFixVector::Create (Controls [0].pitchTime, Controls [0].headingTime, //Controls [0].headingTime ? I2X (1) / 4 : 0; //Controls [0].headingTime; Controls [0].bankTime); } forwardThrustTime = Controls [0].forwardThrustTime; if ((LOCALPLAYER.flags & PLAYER_FLAGS_AFTERBURNER) && (d_rand () < OBJECTS [gameData.multiplayer.nLocalPlayer].DriveDamage ())) { if (Controls [0].afterburnerState) { //CPlayerData has key down fix afterburner_scale; int oldCount,newCount; //add in value from 0..1 afterburner_scale = I2X (1) + min (I2X (1) / 2, gameData.physics.xAfterburnerCharge) * 2; forwardThrustTime = FixMul (gameData.time.xFrame, afterburner_scale); //based on full thrust oldCount = (gameData.physics.xAfterburnerCharge / (DROP_DELTA_TIME / AFTERBURNER_USE_SECS)); if (!gameStates.gameplay.bAfterburnerCheat) gameData.physics.xAfterburnerCharge -= gameData.time.xFrame / AFTERBURNER_USE_SECS; if (gameData.physics.xAfterburnerCharge < 0) gameData.physics.xAfterburnerCharge = 0; newCount = (gameData.physics.xAfterburnerCharge / (DROP_DELTA_TIME / AFTERBURNER_USE_SECS)); if (gameStates.app.bNostalgia && (oldCount != newCount)) gameStates.render.bDropAfterburnerBlob = 1; //drop blob (after physics called) } else { fix xChargeUp = min (gameData.time.xFrame / 8, I2X (1) - gameData.physics.xAfterburnerCharge); //recharge over 8 seconds if (xChargeUp > 0) { fix xCurEnergy = LOCALPLAYER.energy - I2X (10); xCurEnergy = max (xCurEnergy, 0) / 10; //don't drop below 10 if (xCurEnergy > 0) { //maybe limit charge up by energy xChargeUp = min (xChargeUp, xCurEnergy / 10); if (xChargeUp > 0) { gameData.physics.xAfterburnerCharge += xChargeUp; LOCALPLAYER.energy -= xChargeUp * 100 / 10; //full charge uses 10% of energy } } } } } // Set CObject's thrust vector for forward/backward objP->mType.physInfo.thrust = objP->info.position.mOrient.FVec () * forwardThrustTime; // slide left/right objP->mType.physInfo.thrust += objP->info.position.mOrient.RVec () * Controls [0].sidewaysThrustTime; // slide up/down objP->mType.physInfo.thrust += objP->info.position.mOrient.UVec () * Controls [0].verticalThrustTime; objP->mType.physInfo.thrust *= 2 * objP->DriveDamage (); if (!gameStates.input.bSkipControls) memcpy (&gameData.physics.playerThrust, &objP->mType.physInfo.thrust, sizeof (gameData.physics.playerThrust)); bMulti = IsMultiGame; if ((objP->mType.physInfo.flags & PF_WIGGLE) && !gameData.objs.speedBoost [objP->Index ()].bBoosted) { #if 1//!DBG WiggleObject (objP); #endif } // As of now, objP->mType.physInfo.thrust & objP->mType.physInfo.rotThrust are // in units of time... In other words, if thrust==gameData.time.xFrame, that // means that the user was holding down the MaxThrust key for the // whole frame. So we just scale them up by the max, and divide by // gameData.time.xFrame to make them independant of framerate // Prevent divide overflows on high frame rates. // In a signed divide, you get an overflow if num >= div<<15 fix ft = gameData.time.xFrame; // Note, you must check for ft < I2X (1)/2, else you can get an overflow on the << 15. if ((ft < I2X (1)/2) && ((ft << 15) <= gameData.pig.ship.player->maxThrust)) ft = (gameData.pig.ship.player->maxThrust >> 15) + 1; objP->mType.physInfo.thrust *= FixDiv (gameData.pig.ship.player->maxThrust, ft); if ((ft < I2X (1)/2) && ((ft << 15) <= gameData.pig.ship.player->maxRotThrust)) ft = (gameData.pig.ship.player->maxThrust >> 15) + 1; objP->mType.physInfo.rotThrust *= FixDiv (gameData.pig.ship.player->maxRotThrust, ft); }
//------------------------------------------------------------------------------------------- //sequence this weapon object for this _frame_ (underscores added here to aid MK in his searching!) void DoWeaponSequence (CObject *objP) { CObject *gmObjP; fix xWeaponSpeed, xScaleFactor, xDistToTarget; Assert (objP->info.controlType == CT_WEAPON); // Ok, this is a big hack by MK. // If you want an CObject to last for exactly one frame, then give it a lifeleft of ONE_FRAME_TIME if (objP->info.xLifeLeft == ONE_FRAME_TIME) { if (IsMultiGame) objP->info.xLifeLeft = OMEGA_MULTI_LIFELEFT; else objP->info.xLifeLeft = 0; objP->info.renderType = RT_NONE; } if (objP->info.xLifeLeft < 0) { // We died of old age objP->Die (); if (WI_damage_radius (objP->info.nId)) objP->ExplodeBadassWeapon (objP->info.position.vPos); return; } //delete weapons that are not moving xWeaponSpeed = objP->mType.physInfo.velocity.Mag(); if (!((gameData.app.nFrameCount ^ objP->info.nSignature) & 3) && (objP->info.nType == OBJ_WEAPON) && (objP->info.nId != FLARE_ID) && (gameData.weapons.info [objP->info.nId].speed [gameStates.app.nDifficultyLevel] > 0) && (xWeaponSpeed < I2X (2))) { ReleaseObject (objP->Index ()); return; } if ((objP->info.nType == OBJ_WEAPON) && (objP->info.nId == FUSION_ID)) { //always set fusion weapon to max vel CFixVector::Normalize (objP->mType.physInfo.velocity); objP->mType.physInfo.velocity *= (WI_speed (objP->info.nId,gameStates.app.nDifficultyLevel)); } // For homing missiles, turn towards target. (unless it's the guided missile) if ((gameData.laser.xUpdateTime >= I2X (1) / 40) && (objP->info.nType == OBJ_WEAPON) && (gameStates.app.cheats.bHomingWeapons || WI_homingFlag (objP->info.nId)) && !(objP->info.nFlags & PF_HAS_BOUNCED) && !((objP->info.nId == GUIDEDMSL_ID) && (objP == (gmObjP = gameData.objs.guidedMissile [OBJECTS [objP->cType.laserInfo.parent.nObject].info.nId].objP)) && (objP->info.nSignature == gmObjP->info.nSignature))) { fix xFrameTime; for (xFrameTime = gameData.laser.xUpdateTime; xFrameTime >= I2X (1) / 40; xFrameTime -= I2X (1) / 40) { CFixVector vVecToObject, vNewVel; fix dot = I2X (1); fix speed, xMaxSpeed, xDist; int nObjId = objP->info.nId; // For first 1/2 second of life, missile flies straight. //if (objP->cType.laserInfo.xCreationTime + HomingMslStraightTime (nObjId) < gameData.time.xGame) { int nHomingTarget = objP->cType.laserInfo.nHomingTarget; // If it's time to do tracking, then it's time to grow up, stop bouncing and start exploding!. if ((nObjId == ROBOT_SMARTMINE_BLOB_ID) || (nObjId == ROBOT_SMARTMSL_BLOB_ID) || (nObjId == SMARTMINE_BLOB_ID) || (nObjId == SMARTMSL_BLOB_ID) || (nObjId == EARTHSHAKER_MEGA_ID)) objP->mType.physInfo.flags &= ~PF_BOUNCE; // Make sure the CObject we are tracking is still trackable. nHomingTarget = TrackHomingTarget (nHomingTarget, objP, &dot); if (nHomingTarget != -1) { if (nHomingTarget == LOCALPLAYER.nObject) { xDistToTarget = CFixVector::Dist (objP->info.position.vPos, OBJECTS [nHomingTarget].info.position.vPos); if ((xDistToTarget < LOCALPLAYER.homingObjectDist) || (LOCALPLAYER.homingObjectDist < 0)) LOCALPLAYER.homingObjectDist = xDistToTarget; } vVecToObject = OBJECTS [nHomingTarget].info.position.vPos - objP->info.position.vPos; xDist = CFixVector::Normalize (vVecToObject); vNewVel = objP->mType.physInfo.velocity; speed = CFixVector::Normalize (vNewVel); xMaxSpeed = WI_speed (objP->info.nId,gameStates.app.nDifficultyLevel); if (speed + I2X (1) < xMaxSpeed) { speed += FixMul (xMaxSpeed, I2X (1) / 80); if (speed > xMaxSpeed) speed = xMaxSpeed; } if (EGI_FLAG (bEnhancedShakers, 0, 0, 0) && (objP->info.nId == EARTHSHAKER_MEGA_ID)) { fix h = (objP->info.xLifeLeft + I2X (1) - 1) / I2X (1); if (h > 7) vVecToObject *= (I2X (1) / (h - 6)); } #if 0 vVecToObject *= HomingMslScale (); #endif vNewVel += vVecToObject; // The boss' smart children track better... if (gameData.weapons.info [objP->info.nId].renderType != WEAPON_RENDER_POLYMODEL) vNewVel += vVecToObject; CFixVector::Normalize (vNewVel); CFixVector vOldVel = objP->mType.physInfo.velocity; objP->mType.physInfo.velocity = vNewVel; objP->mType.physInfo.velocity *= speed; CFixVector vTest = objP->info.position.vPos + vNewVel * xDist; if (!CanSeePoint (NULL, &objP->info.position.vPos, &vTest, objP->info.nSegment, 3 * objP->info.xSize / 2)) objP->mType.physInfo.velocity = vOldVel; else { // Subtract off life proportional to amount turned. For hardest turn, it will lose 2 seconds per second. dot = abs (I2X (1) - dot); objP->info.xLifeLeft -= FixMul (dot * 32, I2X (1) / 40); } // Only polygon OBJECTS have visible orientation, so only they should turn. if (gameData.weapons.info [objP->info.nId].renderType == WEAPON_RENDER_POLYMODEL) HomingMissileTurnTowardsVelocity (objP, &vNewVel); // vNewVel is normalized velocity. } } } } // Make sure weapon is not moving faster than allowed speed. if ((objP->info.nType == OBJ_WEAPON) && (xWeaponSpeed > WI_speed (objP->info.nId, gameStates.app.nDifficultyLevel))) { // Only slow down if not allowed to move. Makes sense, huh? Allows proxbombs to get moved by physics force. --MK, 2/13/96 if (WI_speed (objP->info.nId, gameStates.app.nDifficultyLevel)) { xScaleFactor = FixDiv (WI_speed (objP->info.nId,gameStates.app.nDifficultyLevel), xWeaponSpeed); objP->mType.physInfo.velocity *= xScaleFactor; } } }