// ----------------------------------------------------------------------------- // Fire Laser: Registers a laser fire, and performs special stuff for the fusion // cannon. void ChargeFusion (void) { if ((LOCALPLAYER.energy < I2X (2)) && (gameData.fusion.xAutoFireTime == 0)) { gameData.laser.nGlobalFiringCount = 0; } else { gameData.fusion.xFrameTime += gameData.time.xFrame; if (!gameData.FusionCharge ()) LOCALPLAYER.energy -= I2X (2); fix h = (gameData.fusion.xFrameTime <= LOCALPLAYER.energy) ? gameData.fusion.xFrameTime : LOCALPLAYER.energy; gameData.SetFusionCharge (gameData.FusionCharge () + h); LOCALPLAYER.energy -= h; if (LOCALPLAYER.energy > 0) gameData.fusion.xAutoFireTime = gameData.time.xGame + gameData.fusion.xFrameTime / 2 + 1; else { LOCALPLAYER.energy = 0; gameData.fusion.xAutoFireTime = gameData.time.xGame - 1; // Fire now! } if (gameStates.limitFPS.bFusion && !gameStates.app.tick40fps.bTick) return; float fScale = float (gameData.FusionCharge () >> 11) / 64.0f; tRgbaColorf* colorP = gameData.weapons.color + FUSION_ID; if (gameData.FusionCharge () < I2X (2)) paletteManager.BumpEffect (colorP->red * fScale, colorP->green * fScale, colorP->blue * fScale); else paletteManager.BumpEffect (colorP->blue * fScale, colorP->red * fScale, colorP->green * fScale); if (gameData.time.xGame < gameData.fusion.xLastSoundTime) //gametime has wrapped gameData.fusion.xNextSoundTime = gameData.fusion.xLastSoundTime = gameData.time.xGame; if (gameData.fusion.xNextSoundTime < gameData.time.xGame) { if (gameData.FusionCharge () > I2X (2)) { audio.PlaySound (11); gameData.objs.consoleP->ApplyDamageToPlayer (gameData.objs.consoleP, d_rand () * 4); } else { CreateAwarenessEvent (gameData.objs.consoleP, WEAPON_ROBOT_COLLISION); audio.PlaySound (SOUND_FUSION_WARMUP); if (IsMultiGame) MultiSendPlaySound (SOUND_FUSION_WARMUP, I2X (1)); } gameData.fusion.xLastSoundTime = gameData.time.xGame; gameData.fusion.xNextSoundTime = gameData.time.xGame + I2X (1) / 8 + d_rand () / 4; } gameData.fusion.xFrameTime = 0; } }
// -------------------------------------------------------------------------------------------------------------------- // Note: Parameter gameData.ai.vVecToPlayer is only passed now because guns which aren't on the forward vector from the // center of the robot will not fire right at the player. We need to aim the guns at the player. Barring that, we cheat. // When this routine is complete, the parameter gameData.ai.vVecToPlayer should not be necessary. void AIFireLaserAtPlayer (CObject *objP, CFixVector *vFirePoint, int nGun, CFixVector *vBelievedPlayerPos) { short nShot, nObject = objP->Index (); tAILocalInfo *ailP = gameData.ai.localInfo + nObject; tRobotInfo *botInfoP = &ROBOTINFO (objP->info.nId); CFixVector vFire; CFixVector bpp_diff; short nWeaponType; fix aim, dot; int count, i; Assert (nObject >= 0); // If this robot is only awake because a camera woke it up, don't fire. if (objP->cType.aiInfo.SUB_FLAGS & SUB_FLAGS_CAMERA_AWAKE) return; if (!gameStates.app.cheats.bRobotsFiring) return; if (objP->info.controlType == CT_MORPH) return; // If player is exploded, stop firing. if (gameStates.app.bPlayerExploded) return; if (objP->cType.aiInfo.xDyingStartTime) return; // No firing while in death roll. // Don't let the boss fire while in death roll. Sorry, this is the easiest way to do this. // If you try to key the boss off objP->cType.aiInfo.xDyingStartTime, it will hose the endlevel stuff. if (ROBOTINFO (objP->info.nId).bossFlag) { i = gameData.bosses.Find (nObject); if ((i < 0) || (gameData.bosses [i].m_nDyingStartTime)) return; } // If CPlayerData is cloaked, maybe don't fire based on how long cloaked and randomness. if (LOCALPLAYER.flags & PLAYER_FLAGS_CLOAKED) { fix xCloakTime = gameData.ai.cloakInfo [nObject % MAX_AI_CLOAK_INFO].lastTime; if ((gameData.time.xGame - xCloakTime > CLOAK_TIME_MAX/4) && (d_rand () > FixDiv (gameData.time.xGame - xCloakTime, CLOAK_TIME_MAX)/2)) { SetNextFireTime (objP, ailP, botInfoP, nGun); return; } } // Handle problem of a robot firing through a CWall because its gun tip is on the other // CSide of the CWall than the robot's center. For speed reasons, we normally only compute // the vector from the gun point to the player. But we need to know whether the gun point // is separated from the robot's center by a CWall. If so, don't fire! if (objP->cType.aiInfo.SUB_FLAGS & SUB_FLAGS_GUNSEG) { // Well, the gun point is in a different CSegment than the robot's center. // This is almost always ok, but it is not ok if something solid is in between. int nGunSeg = FindSegByPos (*vFirePoint, objP->info.nSegment, 1, 0); // See if these segments are connected, which should almost always be the case. short nConnSide = SEGMENTS [nGunSeg].ConnectedSide (&SEGMENTS [objP->info.nSegment]); if (nConnSide != -1) { // They are connected via nConnSide in CSegment objP->info.nSegment. // See if they are unobstructed. if (!(SEGMENTS [objP->info.nSegment].IsDoorWay (nConnSide, NULL) & WID_FLY_FLAG)) { // Can't fly through, so don't let this bot fire through! return; } } else { // Well, they are not directly connected, so use FindVectorIntersection to see if they are unobstructed. tFVIQuery fq; tFVIData hit_data; int fate; fq.startSeg = objP->info.nSegment; fq.p0 = &objP->info.position.vPos; fq.p1 = vFirePoint; fq.radP0 = fq.radP1 = 0; fq.thisObjNum = objP->Index (); fq.ignoreObjList = NULL; fq.flags = FQ_TRANSWALL; fate = FindVectorIntersection (&fq, &hit_data); if (fate != HIT_NONE) { Int3 (); // This bot's gun is poking through a CWall, so don't fire. MoveTowardsSegmentCenter (objP); // And decrease chances it will happen again. return; } } } // Set position to fire at based on difficulty level and robot's aiming ability aim = I2X (FIRE_K) - (FIRE_K-1)* (botInfoP->aim << 8); // I2X (1) in bitmaps.tbl = same as used to be. Worst is 50% more error. // Robots aim more poorly during seismic disturbance. if (gameStates.gameplay.seismic.nMagnitude) { fix temp = I2X (1) - abs (gameStates.gameplay.seismic.nMagnitude); if (temp < I2X (1)/2) temp = I2X (1)/2; aim = FixMul (aim, temp); } // Lead the CPlayerData half the time. // Note that when leading the CPlayerData, aim is perfect. This is probably acceptable since leading is so hacked in. // Problem is all robots will lead equally badly. if (d_rand () < 16384) { if (LeadPlayer (objP, vFirePoint, vBelievedPlayerPos, nGun, &vFire)) // Stuff direction to fire at in vFirePoint. goto player_led; } dot = 0; count = 0; // Don't want to sit in this loop foreverd:\temp\dm_test. i = (NDL - gameStates.app.nDifficultyLevel - 1) * 4; while ((count < 4) && (dot < I2X (1)/4)) { bpp_diff[X] = (*vBelievedPlayerPos)[X] + FixMul ((d_rand ()-16384) * i, aim); bpp_diff[Y] = (*vBelievedPlayerPos)[Y] + FixMul ((d_rand ()-16384) * i, aim); bpp_diff[Z] = (*vBelievedPlayerPos)[Z] + FixMul ((d_rand ()-16384) * i, aim); CFixVector::NormalizedDir(vFire, bpp_diff, *vFirePoint); dot = CFixVector::Dot (objP->info.position.mOrient.FVec (), vFire); count++; } player_led: nWeaponType = botInfoP->nWeaponType; if ((botInfoP->nSecWeaponType != -1) && ((nWeaponType < 0) || !nGun)) nWeaponType = botInfoP->nSecWeaponType; if (nWeaponType < 0) return; if (0 > (nShot = CreateNewLaserEasy (&vFire, vFirePoint, objP->Index (), (ubyte) nWeaponType, 1))) return; lightClusterManager.AddForAI (objP, nObject, nShot); objP->Shots ().nObject = nShot; objP->Shots ().nSignature = OBJECTS [nShot].info.nSignature; if (IsMultiGame) { AIMultiSendRobotPos (nObject, -1); MultiSendRobotFire (nObject, objP->cType.aiInfo.CURRENT_GUN, &vFire); } #if 1 if (++(objP->cType.aiInfo.CURRENT_GUN) >= botInfoP->nGuns) { if ((botInfoP->nGuns == 1) || (botInfoP->nSecWeaponType == -1)) objP->cType.aiInfo.CURRENT_GUN = 0; else objP->cType.aiInfo.CURRENT_GUN = 1; } #endif CreateAwarenessEvent (objP, PA_NEARBY_ROBOT_FIRED); SetNextFireTime (objP, ailP, botInfoP, nGun); }
int FireWeaponDelayedWithSpread ( CObject *objP, ubyte nLaserType, int nGun, fix xSpreadR, fix xSpreadU, fix xDelay, int bMakeSound, int bHarmless, short nLightObj) { short nLaserSeg; int nFate; CFixVector vLaserPos, vLaserDir, *vGunPoints; CHitQuery fq; CHitData hitData; int nObject; CObject* laserP; #if FULL_COCKPIT_OFFS int bLaserOffs = ((gameStates.render.cockpit.nType == CM_FULL_COCKPIT) && (objP->Index () == LOCALPLAYER.nObject)); #else int bLaserOffs = 0; #endif CFixMatrix m; int bSpectate = SPECTATOR (objP); tObjTransformation* posP = bSpectate ? &gameStates.app.playerPos : &objP->info.position; #if DBG if (nLaserType == SMARTMINE_BLOB_ID) nLaserType = nLaserType; #endif CreateAwarenessEvent (objP, PA_WEAPON_WALL_COLLISION); // Find the initial vPosition of the laser if (!(vGunPoints = GetGunPoints (objP, nGun))) return 0; TransformGunPoint (objP, vGunPoints, nGun, xDelay, nLaserType, &vLaserPos, &m); //--------------- Find vLaserPos and nLaserSeg ------------------ fq.p0 = &posP->vPos; fq.startSeg = bSpectate ? gameStates.app.nPlayerSegment : objP->info.nSegment; fq.p1 = &vLaserPos; fq.radP0 = fq.radP1 = 0x10; fq.thisObjNum = objP->Index (); fq.ignoreObjList = NULL; fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS; fq.bCheckVisibility = false; nFate = FindHitpoint (&fq, &hitData); nLaserSeg = hitData.hit.nSegment; if (nLaserSeg == -1) { //some sort of annoying error return -1; } //SORT OF HACK... IF ABOVE WAS CORRECT THIS WOULDNT BE NECESSARY. if (CFixVector::Dist (vLaserPos, posP->vPos) > 3 * objP->info.xSize / 2) { return -1; } if (nFate == HIT_WALL) { return -1; } #if 0 //as of 12/6/94, we don't care if the laser is stuck in an object. We //just fire away normally if (nFate == HIT_OBJECT) { if (OBJECTS [hitData.hitObject].nType == OBJ_ROBOT) OBJECTS [hitData.hitObject].Die (); if (OBJECTS [hitData.hitObject].nType != OBJ_POWERUP) return; } #endif // Now, make laser spread out. vLaserDir = m.FVec (); if (xSpreadR || xSpreadU) { vLaserDir += m.RVec () * xSpreadR; vLaserDir += m.UVec () * xSpreadU; } if (bLaserOffs) vLaserDir += m.UVec () * LASER_OFFS; nObject = CreateNewWeapon (&vLaserDir, &vLaserPos, nLaserSeg, objP->Index (), nLaserType, bMakeSound); // Omega cannon is a hack, not surprisingly. Don't want to do the rest of this stuff. if (nLaserType == OMEGA_ID) return -1; if (nObject == -1) return -1; //TrackWeaponObject (nObject, int (objP->info.nId)); laserP = OBJECTS + nObject; if ((nLaserType == GUIDEDMSL_ID) && gameData.multigame.bIsGuided) gameData.objs.guidedMissile [objP->info.nId].objP = laserP; gameData.multigame.bIsGuided = 0; if (gameData.objs.bIsMissile [nLaserType] && (nLaserType != GUIDEDMSL_ID)) { if (!gameData.objs.missileViewerP && (objP->info.nId == gameData.multiplayer.nLocalPlayer)) gameData.objs.missileViewerP = laserP; } // If this weapon is supposed to be silent, set that bit! if (!bMakeSound) laserP->info.nFlags |= OF_SILENT; // If this weapon is supposed to be silent, set that bit! if (bHarmless) laserP->info.nFlags |= OF_HARMLESS; // If the object firing the laser is the CPlayerData, then indicate the laser object so robots can dodge. // New by MK on 6/8/95, don't let robots evade proximity bombs, thereby decreasing uselessness of bombs. if ((objP == gameData.objs.consoleP) && !WeaponIsPlayerMine (laserP->info.nId)) gameStates.app.bPlayerFiredLaserThisFrame = nObject; if (gameStates.app.cheats.bHomingWeapons || gameData.weapons.info [nLaserType].homingFlag) { if (objP == gameData.objs.consoleP) { laserP->cType.laserInfo.nHomingTarget = FindHomingObject (&vLaserPos, laserP); gameData.multigame.laser.nTrack = laserP->cType.laserInfo.nHomingTarget; } else {// Some other CPlayerData shot the homing thing Assert (IsMultiGame); laserP->cType.laserInfo.nHomingTarget = gameData.multigame.laser.nTrack; } } lightClusterManager.Add (nObject, nLightObj); return nObject; }