Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --FTD Smart Missile Guidance System
- --Author: 6dwavenminer
- --Version: 2.1.2
- --User Config Variables
- --Launcher vehicle variables
- MainFrameID = 0; --Which MainFrame to connect to
- UseWeaponsSlots = false;
- --If 'true' will use all weapons control blocks using the weapons slot of the variable 'WeaponID'
- --If 'false' will use the weapons control block with the ID of the variable 'WeaponID'
- WeaponID = 0;
- MaxDistanceBetweenLuaTranscieverAndMissileControl = 10; --The maximum distance between a lua transciever and its corrisponding missile control block.
- --Missile variables
- MissileInterceptor = false; --Set true if using being used for missile interception
- TargetDetonationRange = -10; --If the distance between target and missile is below this value, blow up.
- --Target acquisition variables
- MultiTarget = true;
- MaxRange = 4000; --Targets above this range from the missile launcher will be ignored.
- MaxTargetHeight = 10000; --Targets above this height will be ignored.
- MinTargetHeight = -20; --Targets below this range will be ignored.
- --Safe detonation variables
- SafeDetonteAltitude = 500; --If there are no target, goto and detonte at this altitude.
- DetonateMissileUponEmptyFuel = true; --If the missile has run out of fuel, detonate?
- --Missile boost variables
- UseFuelDefinedThrustControl = true;
- --If true: Uses the predicted time to intercept and the amount of remaining fuel to calculate thrust value. More adaptable to different ranges.
- --If false: Uses the 'BaseMissileThrust' added to the target speed multiplied by 'MissileThrustTargetSpeedMultiplier' to calculate thrust.
- --In both: If target is below 'BoostInterceptionTime' then thrust is multiplied by 'BoostThrustMultiplier'
- BaseMissileThrust = 300; --Base thrust for missile.
- MissileThrustTargetSpeedMultiplier = 10.0; --Value is multiplied by target's speed then added to base thrust of missile, useful for fast targets. Not used is UseFuelDefinedThrustControl = true.
- BoostInterceptionTime = 6.0; --When the intercept time falls below this value,'BoostThrust' is added to missile thrust, useful for Thumpers.
- BoostThrustMultiplier = 1.2; --Value multiplied to missile thrust once within boost range.
- --Check angle to intercept variables
- InterceptionTurnAngleTollerance = 5; --Added tollerance when calculating whether to steer away from target as interception is deemed currently impossible due to too steep an angle.
- --Cruising variables
- BaseDirectEngagementGroundRange = 240; --Below this ground range to target, head directly to target.
- DirectEngagementGroundRangeSpeedMultipler = 0.6; --Added onto the engagment range after it has been multiplied by the target speed, used for fast targets.
- CruisingAltitude = 250; --While en route travel at this altitude.
- CruisingAltitudeAboveTerrain = 30; --If terrain nears cruising altitude, travel this heigh above the terrain.
- MaxAllowedCruisingAltitude = 10000; --Missile will try and aviod going above this altitude
- MinAllowedCruisingAltitude = 10; --Missile will try and aviod going below this altitude
- --SeaDive variables
- GroundRangeToTargetToSeaDive = 200;--Below this ground range to target, start diving underwater for explosives buff.
- MaxTargetHeightForSeaDive = 5; --Max height of enemy aimpoint above the water to use seadiving on.
- AdditionalSeaDiveDepth = 5; --Additional depth added to enemy aimpoint when seadriving.
- --Anti ally collsion variables
- AntiAlliedCollisionPredictionTimeLimit = 8; --Used to prevent missile-allied vessel collisions, number is seconds into the future predicted.
- AntiAlliedCollisionPredictionTimeStep = 1; --The time step in seconds used to prevent missile-allied vessel collisions.
- AntiAlliedCollisionAdditionalSafetyRange = 5; --Additional range to border of allies to avoid.
- --End of User Config Variables
- --Global Variables
- MissileLauncherPosList = {};
- PreviousTimeForTargets = 0;
- PreviousTimeForMissiles = 0;
- UseableLuaTransceiverList = {};
- UpdateThrust = true;
- NumOfMissiles = 0;
- Missiles = {};
- PreviousTimeForMissiles = 0;
- NumOfTargets = 0;
- Targets = {};
- function GetUseableLuaTranscivers(I)
- MissileLauncherPosList = {};
- for MissileLauncherID, MissileLauncherPos in pairs(MissileLauncherPosList) do
- MissileLauncherPos[MissileLauncherID] = nil;
- end
- if UseWeaponsSlots == true then
- for i = 0, I:GetWeaponCount()-1 do
- local WeaponInfo = I:GetWeaponInfo(i);
- if WeaponInfo.WeaponSlot == WeaponID then
- MissileLauncherPosList[i] = WeaponInfo.GlobalPosition;
- end
- end
- else
- local WeaponInfo = I:GetWeaponInfo(WeaponID);
- MissileLauncherPosList[0] = WeaponInfo.GlobalPosition;
- end
- local t;
- local ValidTransceiverList = {};
- local UseableLuaTransceiverCount = 0;
- for t = 0, I:GetLuaTransceiverCount()-1 do
- local MissileLauncher2LuaTransceiverDistance = 64000;
- for MissileLauncherID, MissileLauncherPos in pairs(MissileLauncherPosList) do
- local MissileLauncher2LuaTransceiverDistanceBuffer = Vector3.Distance(MissileLauncherPos,I:GetLuaTransceiverInfo(t).Position);
- if MissileLauncher2LuaTransceiverDistance > MissileLauncher2LuaTransceiverDistanceBuffer then
- MissileLauncher2LuaTransceiverDistance = MissileLauncher2LuaTransceiverDistanceBuffer;
- end
- end
- if MissileLauncher2LuaTransceiverDistance < MaxDistanceBetweenLuaTranscieverAndMissileControl then
- UseableLuaTransceiverList[UseableLuaTransceiverCount] = t;
- ValidTransceiverList[UseableLuaTransceiverCount] = true;
- UseableLuaTransceiverCount = UseableLuaTransceiverCount + 1;
- end
- end
- for TransceiverID, Transceiver in pairs(UseableLuaTransceiverList) do
- if (ValidTransceiverList[TransceiverID] ~= true)then
- UseableLuaTransceiverList[TransceiverID] = nil;
- end
- ValidTransceiverList[TransceiverID] = false;
- end
- ValidTransceiverList = nil;
- end
- --Check that the WeaponID corrisponds to a missile launcher
- function WeaponsCheck(I)
- if UseWeaponsSlots == false then
- if I:GetWeaponInfo(WeaponID).Valid == false then
- I:LogToHud("Invalid WeaponID, change WeaponID in LUA code");
- else
- if I:GetWeaponInfo(WeaponID).WeaponType == 4 then --Turret check
- if not I:GetWeaponInfoOnTurretOrSpinner(WeaponID, 0).WeaponType == 5 then --Weapons check, is it a missile launcher?
- I:LogToHud("Weapon is not missile launcher, change WeaponID in LUA code, note this weapon is on a turret or spinner,further debugging maybe required");
- else
- GetUseableLuaTranscivers(I);
- end
- else
- if not I:GetWeaponInfo(WeaponID).WeaponType == 5 then --Weapons check, is it a missile launcher?
- I:LogToHud("Weapon is not missile launcher, change WeaponID in LUA code");
- else
- GetUseableLuaTranscivers(I);
- end
- end
- end
- else
- if WeaponID > 5 then
- I:LogToHud("WeaponsSlots only go upto 5, either change the variable 'WeaponID' or change 'UseWeaponsSlots' to false ");
- else
- GetUseableLuaTranscivers(I);
- end
- end
- end
- --Creates a list of all targets within range
- function GenerateTargetList(I)
- local TargetPos;
- local n;
- local ValidTargetList = {};
- NumOfTargets = 0;
- for n=0,I:GetNumberOfTargets(MainFrameID)-1 do
- TargetPos = I:GetTargetInfo(MainFrameID,n).AimPointPosition;
- local TargetRange = 64000;
- for MissileLauncherID, MissileLauncherPos in pairs(MissileLauncherPosList) do
- local TargetRangeBuffer = Vector3.Distance(MissileLauncherPos,TargetPos);
- if TargetRange > TargetRangeBuffer then
- TargetRange = TargetRangeBuffer;
- end
- end
- if (TargetRange < MaxRange) and (TargetPos.y < MaxTargetHeight) and (TargetPos.y > MinTargetHeight) then
- local TargetInfo = I:GetTargetInfo(MainFrameID,n);
- if TargetInfo.Valid == true then
- local TargetID = TargetInfo.Id;
- --If we've not seen the target before, collect data on it
- if Targets[TargetID] == nil then
- Targets[TargetID] = {};
- Targets[TargetID].Index = n;
- Targets[TargetID].Velocity = TargetInfo.Velocity;
- Targets[TargetID].Acceleration = Vector3(0,0,0);
- end
- --Update target values
- Targets[TargetID].Score = TargetInfo.Score;
- Targets[TargetID].Position = TargetInfo.AimPointPosition;
- Targets[TargetID].PreviousVelocity = Targets[TargetID].Velocity;
- Targets[TargetID].Velocity = TargetInfo.Velocity;
- Targets[TargetID].Speed = TargetInfo.Velocity.magnitude;
- ValidTargetList[TargetID] = true;
- local TimeDifference = I:GetTime() - PreviousTimeForTargets;
- if TimeDifference > 0.1 then
- Targets[TargetID].Acceleration = (Targets[TargetID].Velocity - Targets[TargetID].PreviousVelocity) / TimeDifference;
- PreviousTimeForTargets = I:GetTime();
- end
- NumOfTargets = NumOfTargets + 1;
- end
- end
- end
- --Cycle through all known missiles to remove the dead ones
- for TargetID, Target in pairs(Targets) do
- if (ValidTargetList[TargetID] ~= true)then
- Targets[TargetID] = nil;
- end
- end
- ValidTargetList = nil;
- end
- --Creates a list of all targets within range
- function GenerateTargetListMissileInterceptor(I)
- local TargetPos;
- local n;
- local ValidTargetList = {};
- NumOfTargets = 0;
- for n=0,I:GetNumberOfWarnings(MainFrameID)-1 do
- TargetPos = I:GetMissileWarning(MainFrameID,n).Position;
- local TargetRange = 64000;
- for MissileLauncherID, MissileLauncherPos in pairs(MissileLauncherPosList) do
- local TargetRangeBuffer = Vector3.Distance(MissileLauncherPos,TargetPos);
- if TargetRange > TargetRangeBuffer then
- TargetRange = TargetRangeBuffer;
- end
- end
- TargetRange = Vector3.Distance(I:GetWeaponInfo(WeaponID).GlobalPosition,TargetPos);
- if (TargetRange < MaxRange) and (TargetPos.y < MaxTargetHeight) and (TargetPos.y > MinTargetHeight) then
- local TargetInfo = I:GetMissileWarning(MainFrameID,n);
- if TargetInfo.Valid == true then
- local TargetID = TargetInfo.Id;
- --If we've not seen the target before, collect data on it
- if Targets[TargetID] == nil then
- Targets[TargetID] = {};
- Targets[TargetID].Index = n;
- Targets[TargetID].Velocity = TargetInfo.Velocity;
- Targets[TargetID].Acceleration = Vector3(0,0,0);
- end
- --Update target values
- Targets[TargetID].Position = TargetInfo.Position;
- Targets[TargetID].PreviousVelocity = Targets[TargetID].Velocity;
- Targets[TargetID].Velocity = TargetInfo.Velocity;
- Targets[TargetID].Speed = TargetInfo.Velocity.magnitude;
- local VectorToInterceptionAimPos = I:GetConstructPosition() - Targets[TargetID].Position;
- local TargetAngleToInterceptionAimPos;
- if (VectorToInterceptionAimPos.magnitude ~= 0) and (Targets[TargetID].Speed ~= 0) then
- TargetAngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos, Targets[TargetID].Velocity) / (VectorToInterceptionAimPos.magnitude * Targets[TargetID].Speed)));
- else
- TargetAngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos, Targets[TargetID].Velocity) / 0.001));
- end
- Targets[TargetID].Score = (100 / (Vector3.Distance(TargetInfo.Position, I:GetConstructPosition()) + 1)) * (1 - (TargetAngleToInterceptionAimPos / 180.1)); --Missiles closer to construct, and that are also pointing towards it, get a higher score
- ValidTargetList[TargetID] = true;
- local TimeDifference = I:GetTime() - PreviousTimeForTargets;
- if TimeDifference > 0.5 then
- Targets[TargetID].Acceleration = (Targets[TargetID].Velocity - Targets[TargetID].PreviousVelocity) / TimeDifference;
- PreviousTimeForTargets = I:GetTime();
- end
- NumOfTargets = NumOfTargets + 1;
- end
- end
- end
- --Cycle through all known missiles to remove the dead ones
- for TargetID, Target in pairs(Targets) do
- if (ValidTargetList[TargetID] ~= true)then
- Targets[TargetID] = nil;
- end
- end
- ValidTargetList = nil;
- end
- --Creates a list of all useable missiles
- function GenerateMissileList(I)
- NumOfMissiles = 0;
- local ValidMissileList = {};
- local i;
- local m;
- for TransceiverID, Transceiver in pairs( UseableLuaTransceiverList) do
- for m=0, I:GetLuaControlledMissileCount(Transceiver)-1 do
- local MissileInfo = I:GetLuaControlledMissileInfo(Transceiver,m);
- if MissileInfo.Valid == true then
- local MissileID = MissileInfo.Id;
- --If we've not seen the missile before, collect data on it
- if Missiles[MissileID] == nil then
- Missiles[MissileID] = {};
- Missiles[MissileID].TransceiverIndex = Transceiver;
- Missiles[MissileID].MissileIndex = m;
- Missiles[MissileID].Velocity = MissileInfo.Velocity;
- Missiles[MissileID].AimPoint = Vector3(0, 0, 0);
- Missiles[MissileID].InterceptionTime = 0;
- PreviousTimeForMissiles = I:GetTime();
- Missiles[MissileID].Acceleration = Vector3(0,0,0);
- Missiles[MissileID].NumOfThrusters = 0;
- Missiles[MissileID].NumOfTorpedoPropellers = 0;
- Missiles[MissileID].NumOfShortRangeThrusters = 0;
- Missiles[MissileID].NumOfFins = 0;
- Missiles[MissileID].NumOfRegulators = 0;
- Missiles[MissileID].Fuel = 0;
- Missiles[MissileID].AngleToInterceptionAimPos = 0;
- Missiles[MissileID].MaxTurnSpeed = 0;
- Missiles[MissileID].MinimumTurnRadius = 1;
- Missiles[MissileID].FlightTimeLeft = 60;
- Missiles[MissileID].MissileLength = 0;
- local MissileExtraInfo = I:GetMissileInfo(Transceiver, m);--Lagfest
- if (MissileExtraInfo ~= nil) then
- --Find out about the variable thrusters
- local ThrusterIndexes = {};
- for j,part in pairs(MissileExtraInfo.Parts) do
- if (string.find(part.Name, 'variable')) then
- table.insert(ThrusterIndexes, j);
- Missiles[MissileID].NumOfThrusters = Missiles[MissileID].NumOfThrusters + 1;
- end
- if (string.find(part.Name, 'short')) then
- Missiles[MissileID].NumOfShortRangeThrusters = Missiles[MissileID].NumOfShortRangeThrusters + 1;
- end
- if (string.find(part.Name, 'propeller')) then
- Missiles[MissileID].NumOfTorpedoPropellers = Missiles[MissileID].NumOfTorpedoPropellers + 1;
- end
- if (string.find(part.Name, 'fuel')) then
- Missiles[MissileID].Fuel = Missiles[MissileID].Fuel + 5000;
- end
- if (string.find(part.Name, 'fins')) then
- Missiles[MissileID].NumOfFins = Missiles[MissileID].NumOfFins + 1;
- end
- if (string.find(part.Name, 'regulator')) then
- Missiles[MissileID].NumOfRegulators = Missiles[MissileID].NumOfRegulators + 1;
- end
- if (string.find(part.Name, 'thumper')) then
- Missiles[MissileID].MissileLength = Missiles[MissileID].MissileLength + 1;
- end
- Missiles[MissileID].MissileLength = Missiles[MissileID].MissileLength + 0.5;
- end
- Missiles[MissileID].ThrusterIndexes = ThrusterIndexes;
- ThrusterIndexes = nil;
- end
- Missiles[MissileID].Thrust = BaseMissileThrust;
- end
- --Update missile values
- Missiles[MissileID].TimeSinceLaunch = MissileInfo.TimeSinceLaunch;
- Missiles[MissileID].TimeLeftTillDespawn = 60 + (Missiles[MissileID].NumOfRegulators * 180) - Missiles[MissileID].TimeSinceLaunch; --Time left before despawn
- Missiles[MissileID].Position = MissileInfo.Position;
- Missiles[MissileID].PreviousVelocity = Missiles[MissileID].Velocity;
- Missiles[MissileID].Velocity = MissileInfo.Velocity;
- Missiles[MissileID].Speed = MissileInfo.Velocity.magnitude;
- local TimeDifference = I:GetTime() - PreviousTimeForMissiles;
- if TimeDifference > 0.3 then
- UpdateThrust = true;
- Missiles[MissileID].Acceleration = (Missiles[MissileID].Velocity - Missiles[MissileID].PreviousVelocity) / TimeDifference;
- local VariableThrusterFuelUse = 0;
- local ShortRangeThrusterFuelUse = 0;
- local TorpedoPropellerFuelUse = 0;
- if Missiles[MissileID].NumOfThrusters ~= 0 then
- VariableThrusterFuelUse = (Missiles[MissileID].NumOfThrusters * Missiles[MissileID].Thrust);
- end
- if Missiles[MissileID].NumOfShortRangeThrusters ~= 0 then
- ShortRangeThrusterFuelUse = (Missiles[MissileID].NumOfShortRangeThrusters * 1000);
- end
- if Missiles[MissileID].NumOfTorpedoPropellers ~= 0 then
- TorpedoPropellerFuelUse = (Missiles[MissileID].NumOfTorpedoPropellers * 37.5);
- end
- if Missiles[MissileID].Fuel > 0 then
- if Missiles[MissileID].Position.y > 0 then --Above water
- Missiles[MissileID].Fuel = Missiles[MissileID].Fuel - (TimeDifference * (VariableThrusterFuelUse + ShortRangeThrusterFuelUse));
- else --Below the water
- Missiles[MissileID].Fuel = Missiles[MissileID].Fuel - (TimeDifference * TorpedoPropellerFuelUse);
- end
- else
- Missiles[MissileID].Fuel = 0;
- end
- Missiles[MissileID].MaxTurnSpeed = 10 * math.sqrt(Missiles[MissileID].Speed) * (Missiles[MissileID].NumOfFins / Missiles[MissileID].MissileLength);
- Missiles[MissileID].MinimumTurnRadius = 5.73 * math.sqrt(Missiles[MissileID].Speed) * (Missiles[MissileID].MissileLength / Missiles[MissileID].NumOfFins);
- if Missiles[MissileID].Position.y > 0 then --Above water
- if (Missiles[MissileID].NumOfThrusters ~= 0) or (Missiles[MissileID].NumOfShortRangeThrusters ~= 0)then
- Missiles[MissileID].FlightTimeLeft = Missiles[MissileID].Fuel / (VariableThrusterFuelUse + ShortRangeThrusterFuelUse);
- end
- else --Below the water
- if Missiles[MissileID].NumOfTorpedoPropellers ~= 0 then
- Missiles[MissileID].FlightTimeLeft = Missiles[MissileID].Fuel / (Missiles[MissileID].NumOfTorpedoPropellers * 37.5);
- end
- end
- if Missiles[MissileID].FlightTimeLeft > Missiles[MissileID].TimeLeftTillDespawn then
- Missiles[MissileID].FlightTimeLeft = Missiles[MissileID].TimeLeftTillDespawn;
- end
- PreviousTimeForMissiles = I:GetTime();
- else
- UpdateThrust = false;
- end
- ValidMissileList[MissileID] = true;
- NumOfMissiles = NumOfMissiles + 1;
- end
- end
- IncrementalListOfTargetIDs = nil;
- end
- --Cycle through all known missiles to remove the dead ones
- for MissileID, Missile in pairs(Missiles) do
- if (ValidMissileList[MissileID] ~= true)then
- Missiles[MissileID] = nil;
- end
- end
- ValidMissileList = nil;
- end
- --If theres no targets go up to safe distance to detonte the missle
- function NoTargetSafelyDetonte(I)
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- Missile.AimPoint = Vector3(Missile.Position.x, SafeDetonteAltitude, Missile.Position.z);
- if ((Missile.Position.y > SafeDetonteAltitude) and (SafeDetonteAltitude > 0)) or ((Missile.Position.y < SafeDetonteAltitude) and (SafeDetonteAltitude <= 0)) then
- I:DetonateLuaControlledMissile(Missile.TransceiverIndex,Missile.MissileIndex);
- end
- end
- end
- end
- --Calculate how many to missiles each target
- function CalculateMissilesPerTarget(I,EnemyFleetTotalScore,ScoreOffset)
- for ID, Target in pairs(Targets) do
- if NumOfTargets == 1 then
- Target.MissilesPerTarget = NumOfMissiles;
- else
- if EnemyFleetTotalScore ~= 0 then
- Target.MissilesPerTarget = ((Target.Score + ScoreOffset) / EnemyFleetTotalScore) * NumOfMissiles;
- else
- Target.MissilesPerTarget = 1;
- end
- Target.MissilesPerTarget = math.floor(Target.MissilesPerTarget + 0.5);
- end
- end
- end
- function CalculateEnemyFleetScore(I)
- local EnemyFleetTotalScore = 0;
- local LowestScore = 100000;
- local HighestScore = -100000;
- local ScoreOffset = 0;
- --Find highest and lowest enemy scores
- for ID, Target in pairs(Targets) do
- if (Target.Score) < LowestScore then
- LowestScore = Target.Score;
- end
- if (Target.Score) > HighestScore then
- HighestScore = Target.Score;
- end
- end
- --Calculate score offset
- ScoreOffset = (HighestScore / 2) - LowestScore;
- --Calculate enemy fleet score
- for ID, Target in pairs(Targets) do
- EnemyFleetTotalScore = EnemyFleetTotalScore + ScoreOffset + Target.Score;
- end
- CalculateMissilesPerTarget(I,EnemyFleetTotalScore,ScoreOffset);
- end
- function AssignTargetForMissile(I)
- --Generate a incremental list of target IDs
- local IncrementalListOfTargetIDs = {};
- local IncrementalIndex = 0;
- local MaxScore = -65536
- for ID, Target in pairs(Targets) do
- if Target ~= nil then --Check we have a target
- if MultiTarget == true then
- IncrementalListOfTargetIDs[IncrementalIndex] = ID;
- IncrementalIndex = IncrementalIndex + 1;
- else
- --Target enemy with highest score
- if Target.Score > MaxScore then
- IncrementalListOfTargetIDs[IncrementalIndex] = ID;
- MaxScore = Target.Score
- end
- end
- end
- end
- --Cycle through every missile and assign it a target
- local MissileNum = 0;
- local TargetNum = 0;
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- if MultiTarget == true then
- MissileNum = MissileNum + 1;
- if MissileNum > Targets[IncrementalListOfTargetIDs[TargetNum]].MissilesPerTarget then
- if TargetNum < NumOfTargets - 1 then
- TargetNum = TargetNum + 1;
- MissileNum = 0;
- end
- end
- Missile.TargetID = IncrementalListOfTargetIDs[TargetNum];
- else
- --Only target highest scoring enemy
- Missile.TargetID = IncrementalListOfTargetIDs[TargetNum];
- end
- end
- end
- IncrementalListOfTargetIDs = nil;
- end
- --Calculate distance ignoring vertical component
- function CalculateGroundDistanceToTarget(I, MissileID)
- local GroundDistanceToTarget = 1000;
- if Missiles[MissileID] ~= nil then
- if I:GetLuaControlledMissileInfo(Missiles[MissileID].TransceiverIndex, Missiles[MissileID].MissileIndex).Valid == true then --Double check the missile is still live
- if Targets[Missiles[MissileID].TargetID] ~= nil then
- local TwoDMissilePos = Missiles[MissileID].Position * 1; --Multiply by one to only copy the data, not the reference!
- local TwoDTargetPos = Targets[Missiles[MissileID].TargetID].Position * 1; --Multiply by one to only copy the data, not the reference!
- TwoDMissilePos.y = 0;
- TwoDTargetPos.y = 0;
- GroundDistanceToTarget = Vector3.Distance(TwoDMissilePos,TwoDTargetPos);
- end
- end
- end
- return GroundDistanceToTarget;
- end
- function PredictInterceptPosition(I)
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- if Targets[Missile.TargetID] ~= nil then --Check we have a target
- local TargetPosition = Targets[Missile.TargetID].Position;
- local TargetVelocity = Targets[Missile.TargetID].Velocity;
- local TargetSpeed = Vector3.Magnitude(TargetVelocity);
- local TargetAcceleration = Targets[Missile.TargetID].Acceleration;
- local MissilePosition = Missile.Position;
- local MissileSpeed = Missile.Speed;
- local MissileAcceleration = Missile.Acceleration;
- local SummedSpeedAcceleration = Vector3.Magnitude(MissileAcceleration + TargetAcceleration);
- local SummedSpeed = MissileSpeed + TargetSpeed;
- local IntecpetionRange;
- local InterceptionTime;
- local InterceptionAimPos = TargetPosition;
- for i=0, 12 do
- IntecpetionRange = Vector3.Distance(MissilePosition, InterceptionAimPos);
- if SummedSpeedAcceleration ~= 0 then
- InterceptionTime = (-SummedSpeed + math.sqrt(math.abs(math.pow(SummedSpeed, 2) + (2 * SummedSpeedAcceleration * IntecpetionRange)))) / SummedSpeedAcceleration;
- elseif SummedSpeed ~= 0 then
- InterceptionTime = IntecpetionRange / SummedSpeed;
- else
- InterceptionTime = Missile.FlightTimeLeft;
- end
- --Sanity check InterceptionTime
- if InterceptionTime > Missile.FlightTimeLeft then
- InterceptionTime = Missile.FlightTimeLeft;
- end
- InterceptionAimPos = TargetPosition + (TargetVelocity * InterceptionTime) + (TargetAcceleration * 0.5 * math.pow(InterceptionTime,2));
- end
- Missile.InterceptionTime = InterceptionTime;
- Missile.AimPoint = Vector3(InterceptionAimPos.x,InterceptionAimPos.y,InterceptionAimPos.z);
- end
- end
- end
- end
- --Check if the missile can actually aim at the correct point fast enough
- function CheckAngleToIntercept(I)
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- if Targets[Missile.TargetID] ~= nil then --Check we have a target
- local VectorToInterceptionAimPos = Missile.AimPoint - Missile.Position;--Targets[Missile.TargetID].Position --Missile.AimPoint
- if (VectorToInterceptionAimPos.magnitude ~= 0) and (Missile.Speed ~= 0) then
- Missile.AngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos, Missile.Velocity) / (VectorToInterceptionAimPos.magnitude * Missile.Speed)));
- else
- Missile.AngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos, Missile.Velocity) / 0.001));
- end
- if ((Missile.AngleToInterceptionAimPos > ((Missile.MaxTurnSpeed * Missile.InterceptionTime) + InterceptionTurnAngleTollerance)) and (Vector3.Distance(Missile.Position, Targets[Missile.TargetID].Position) > 10)) then
- --Missile can't turn in time, change trajectory
- Missile.AimPoint = Targets[Missile.TargetID].Position + (Missile.Velocity * 4); --Purposly aim the missile behind the target so it can try for another pass
- Missile.AimPoint.y = CruisingAltitude;
- end
- end
- end
- end
- end
- --To allow quick change in height, use a closer aimpoint than target, but still in the same direction.
- function TargetCloserThanTarget(I, MissileID, GroundDistanceToTarget)
- if I:GetLuaControlledMissileInfo(Missiles[MissileID].TransceiverIndex, Missiles[MissileID].MissileIndex).Valid == true then --Double check the missile is still live
- if Targets[Missiles[MissileID].TargetID] ~= nil then
- if GroundDistanceToTarget ~= 0 then
- local CoordinateMultiplier = (Vector3.Magnitude(Missiles[MissileID].Velocity) * 2) / GroundDistanceToTarget;
- local Missle_TargetDifferentialPosX = Missiles[MissileID].Position.x - Missiles[MissileID].AimPoint.x;
- local Missle_TargetDifferentialPosZ = Missiles[MissileID].Position.z - Missiles[MissileID].AimPoint.z;
- Missiles[MissileID].AimPoint.x = Missiles[MissileID].Position.x - (Missle_TargetDifferentialPosX * CoordinateMultiplier);
- Missiles[MissileID].AimPoint.z = Missiles[MissileID].Position.z - (Missle_TargetDifferentialPosZ * CoordinateMultiplier);
- end
- end
- end
- end
- --En route go to attlitude till near target
- function CruiseTowardsTarget(I)
- for MissileID, Missile in pairs(Missiles) do
- if Missile ~= nil then
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- if Targets[Missile.TargetID] ~= nil then
- local GroundDistanceToTarget = CalculateGroundDistanceToTarget(I, MissileID);
- local MissileFuturePosition = Missile.Position + (Missile.Velocity * 2);
- local MissileFutureTerrainHeight = I:GetTerrainAltitudeForPosition(MissileFuturePosition);
- --Ground avoidance till near target
- if (GroundDistanceToTarget > 50 + (2 * Vector3.Magnitude(Targets[Missile.TargetID].Velocity))) and (MissileFutureTerrainHeight + CruisingAltitudeAboveTerrain > MissileFuturePosition.y) then
- Missile.AimPoint.y = MissileFutureTerrainHeight + CruisingAltitudeAboveTerrain;
- TargetCloserThanTarget(I, MissileID, GroundDistanceToTarget);
- --Goto crusing height till near target
- elseif GroundDistanceToTarget > BaseDirectEngagementGroundRange + (DirectEngagementGroundRangeSpeedMultipler * Vector3.Magnitude(Targets[Missile.TargetID].Velocity)) then
- Missile.AimPoint.y = CruisingAltitude;
- TargetCloserThanTarget(I, MissileID, GroundDistanceToTarget);
- end
- end
- end
- end
- end
- end
- --If target is near the surface of the sea, dive to get explosive buff
- function MissileSeaDive(I)
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- if Targets[Missiles[MissileID].TargetID] ~= nil then
- local GroundDistanceToTarget = CalculateGroundDistanceToTarget(I, MissileID);
- if (GroundDistanceToTarget < GroundRangeToTargetToSeaDive) and (Targets[Missiles[MissileID].TargetID].Position.y < MaxTargetHeightForSeaDive) then
- Missiles[MissileID].AimPoint.y = Targets[Missiles[MissileID].TargetID].Position.y - AdditionalSeaDiveDepth;
- end
- end
- end
- end
- end
- --Checks missiles curret appoximate trajectory for any possible collisions with predicted allied postions and attempts to aviod them
- function AllyCollisionAviodance(I)
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- local NumOfFriendlies = I:GetFriendlyCount();
- if NumOfFriendlies > 0 then
- --Find all possible allied collsions
- local n;
- local t;
- local MissileTimeToReachAimPos;
- local MissileFuturePos;
- local FutureAllyReferencePosition;
- local FutureAllyReferencePositionPositiveSize;
- local FutureAllyReferencePositionNegativeSize;
- local PositiveAviodCoOrdinates;
- local NegativeAviodCoOrdinates;
- local MissileFutureCollisionPos;
- local PositiveEvadeDistance;
- local NegativeEvadeDistance;
- local NearestTimeCollsion = AntiAlliedCollisionPredictionTimeLimit;
- local NearestTimeCollsionAlliedIndex = nil;
- local FriendlyInfo;
- for n=0, NumOfFriendlies-1 do
- FriendlyInfo = I:GetFriendlyInfo(n);
- if FriendlyInfo.Valid == true then
- if Missile.Velocity.magnitude ~= 0 then
- MissileTimeToReachAimPos = Vector3.Distance(Missile.Position, Missile.AimPoint) / Missile.Velocity.magnitude;
- else
- MissileTimeToReachAimPos = Vector3.Distance(Missile.Position, Missile.AimPoint) / 0.001;
- end
- for t=0, AntiAlliedCollisionPredictionTimeLimit-1, AntiAlliedCollisionPredictionTimeStep do
- if MissileTimeToReachAimPos > t then --Don't care is the ally is further away than the enemy target
- if t ~= 0 then
- MissileFuturePos = Missile.Position + ((Missile.AimPoint - Missile.Position) / t);
- else
- MissileFuturePos = Missile.Position + ((Missile.AimPoint - Missile.Position) / 0.001);
- end
- FutureAllyReferencePosition = FriendlyInfo.ReferencePosition + (FriendlyInfo.Velocity * t);
- FutureAllyReferencePositionPositiveSize = FutureAllyReferencePosition + FriendlyInfo.PositiveSize + Vector3(AntiAlliedCollisionAdditionalSafetyRange,AntiAlliedCollisionAdditionalSafetyRange,AntiAlliedCollisionAdditionalSafetyRange);
- FutureAllyReferencePositionNegativeSize = FutureAllyReferencePosition + FriendlyInfo.NegativeSize - Vector3(AntiAlliedCollisionAdditionalSafetyRange,AntiAlliedCollisionAdditionalSafetyRange,AntiAlliedCollisionAdditionalSafetyRange);
- if (MissileFuturePos.x <= FutureAllyReferencePositionPositiveSize.x)
- and (MissileFuturePos.x >= FutureAllyReferencePositionNegativeSize.x)
- and (MissileFuturePos.y <= FutureAllyReferencePositionPositiveSize.y)
- and (MissileFuturePos.y >= FutureAllyReferencePositionNegativeSize.y)
- and (MissileFuturePos.z <= FutureAllyReferencePositionPositiveSize.z)
- and (MissileFuturePos.z >= FutureAllyReferencePositionNegativeSize.z)
- then
- --Will probably collide with ally, record time and friendly Index
- if t < NearestTimeCollsion then
- NearestTimeCollsion = t;
- NearestTimeCollsionAlliedIndex = FriendlyInfo.Id;
- end
- break --No use checking if it will collide later with the same target...
- end
- end
- end
- end
- end
- --Calculate where the missile can't go
- PositiveAviodCoOrdinates = Vector3(-100000,100000,-100000);
- NegativeAviodCoOrdinates = Vector3(100000,100000,100000);
- if NearestTimeCollsionAlliedIndex ~= nil then
- FriendlyInfo = I:GetFriendlyInfo(NearestTimeCollsionAlliedIndex);
- FutureAllyReferencePosition = FriendlyInfo.ReferencePosition + (FriendlyInfo.Velocity * NearestTimeCollsion);
- FutureAllyReferencePositionPositiveSize = FutureAllyReferencePosition + FriendlyInfo.PositiveSize + Vector3(AntiAlliedCollisionAdditionalSafetyRange, AntiAlliedCollisionAdditionalSafetyRange, AntiAlliedCollisionAdditionalSafetyRange);
- FutureAllyReferencePositionNegativeSize = FutureAllyReferencePosition + FriendlyInfo.NegativeSize - Vector3(AntiAlliedCollisionAdditionalSafetyRange, AntiAlliedCollisionAdditionalSafetyRange, AntiAlliedCollisionAdditionalSafetyRange);
- if (PositiveAviodCoOrdinates.x <= FutureAllyReferencePositionPositiveSize.x) then
- PositiveAviodCoOrdinates.x = FutureAllyReferencePositionPositiveSize.x;
- end
- if (NegativeAviodCoOrdinates.x >= FutureAllyReferencePositionPositiveSize.x) then
- NegativeAviodCoOrdinates.x = FutureAllyReferencePositionNegativeSize.x;
- end
- if (PositiveAviodCoOrdinates.y <= FutureAllyReferencePositionPositiveSize.y) then
- PositiveAviodCoOrdinates.y = FutureAllyReferencePositionPositiveSize.y;
- end
- if (NegativeAviodCoOrdinates.y >= FutureAllyReferencePositionPositiveSize.y) then
- NegativeAviodCoOrdinates.y = FutureAllyReferencePositionNegativeSize.y;
- end
- if (PositiveAviodCoOrdinates.z <= FutureAllyReferencePositionPositiveSize.z) then
- PositiveAviodCoOrdinates.z = FutureAllyReferencePositionPositiveSize.z;
- end
- if (NegativeAviodCoOrdinates.z >= FutureAllyReferencePositionPositiveSize.z) then
- NegativeAviodCoOrdinates.z = FutureAllyReferencePositionNegativeSize.z;
- end
- end
- --Find the nearest non-collsion point while trying to maintain route to target
- if NearestTimeCollsionAlliedIndex ~= nil then
- if NearestTimeCollsion ~= 0 then
- MissileFutureCollisionPos = Missile.Position + ((Missile.AimPoint - Missile.Position) / NearestTimeCollsion);
- else
- MissileFutureCollisionPos = Missile.Position + ((Missile.AimPoint - Missile.Position) / 0.0001);
- end
- PositiveEvadeDistance = Vector3.Magnitude(MissileFutureCollisionPos - PositiveAviodCoOrdinates);
- NegativeEvadeDistance = Vector3.Magnitude(MissileFutureCollisionPos - NegativeAviodCoOrdinates);
- --Now we try and steer the missile away from the possible collisions
- if PositiveEvadeDistance < NegativeEvadeDistance then
- Missile.AimPoint = PositiveAviodCoOrdinates;
- if PositiveEvadeDistance.y > MaxAllowedCruisingAltitude then
- Missile.AimPoint.y = MaxAllowedCruisingAltitude;
- end
- else
- Missile.AimPoint = NegativeAviodCoOrdinates;
- if NegativeAviodCoOrdinates.y < MinAllowedCruisingAltitude then
- Missile.AimPoint.y = MinAllowedCruisingAltitude;
- end
- end
- end
- end
- end
- end
- end
- --Finally command the missiles to aim at thier aimpoints
- function AimMissiles(I)
- for MissileID, Missile in pairs(Missiles) do
- if I:IsLuaControlledMissileAnInterceptor(Missile.TransceiverIndex, Missile.MissileIndex) then
- I:SetLuaControlledMissileInterceptorStandardGuidanceOnOff(Missile.TransceiverIndex, Missile.MissileIndex, false);
- end
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- I:SetLuaControlledMissileAimPoint(Missile.TransceiverIndex, Missile.MissileIndex, Missile.AimPoint.x, Missile.AimPoint.y, Missile.AimPoint.z);
- end
- end
- end
- function ControlMissileThrust(I)
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- if UseFuelDefinedThrustControl == true then
- if Missile.InterceptionTime ~= 0 then
- Missile.Thrust = BaseMissileThrust * (Missile.FlightTimeLeft / Missile.InterceptionTime) * (1 - (Missile.AngleToInterceptionAimPos / 180.1));
- else
- Missile.Thrust = BaseMissileThrust * (Missile.FlightTimeLeft / 0.001) * (1 - (Missile.AngleToInterceptionAimPos / 180.1));
- end
- else
- local TargetSpeed = 0;
- if Targets[Missile.TargetID] ~= nil then --Check we have a target
- TargetSpeed = Vector3.Magnitude(Targets[Missile.TargetID].Velocity);
- else
- TargetSpeed = 0;
- end
- local VectorToInterceptionAimPos = Missile.AimPoint - Missile.Position
- local AngleToInterceptionAimPos;
- if (VectorToInterceptionAimPos.magnitude ~= 0) and (Missile.Velocity.magnitude ~= 0) then
- AngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos, Missile.Velocity) / (VectorToInterceptionAimPos.magnitude * Missile.Velocity.magnitude)));
- else
- AngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos, Missile.Velocity) / 0.001));
- end
- Missile.Thrust = MissileThrustTargetSpeedMultiplier * TargetSpeed * (1 - (AngleToInterceptionAimPos / 180.1)) + BaseMissileThrust;
- end
- if Missile.InterceptionTime < BoostInterceptionTime then
- Missile.Thrust = Missile.Thrust * BoostThrustMultiplier;
- end
- if Missile.Thrust > 10000 then --Clamp result
- Missile.Thrust = 10000;
- end
- end
- end
- end
- --Changes the missile thrust every time the fuel amount is updated
- function SetMissileThrust(I)
- if UpdateThrust == true then
- ControlMissileThrust(I);
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- local MissileExtraInfo = I:GetMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex);--Lagfest
- if (Missile.NumOfThrusters > 0) and (MissileExtraInfo ~= nil) then
- for i,Index in pairs(Missile.ThrusterIndexes) do
- if (MissileExtraInfo.Parts[Index] ~= nil) and (Missile.Thrust ~= nil) then
- MissileExtraInfo.Parts[Index]:SendRegister(2, Missile.Thrust);
- end
- end
- end
- MissileExtraInfo = nil;
- end
- end
- end
- end
- function ProximityDetonation(I)
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- if Targets[Missile.TargetID] ~= nil then
- if Vector3.Distance(Missile.Position, Targets[Missile.TargetID].Position) < TargetDetonationRange then
- I:DetonateLuaControlledMissile(Missile.TransceiverIndex, Missile.MissileIndex);
- end
- end
- end
- end
- end
- function EmptyFuelDetonation(I)
- for MissileID, Missile in pairs(Missiles) do
- if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
- if (Missile.Fuel <= 0) and (DetonateMissileUponEmptyFuel == true) and (Missile.TimeSinceLaunch > 1.0) then
- I:DetonateLuaControlledMissile(Missile.TransceiverIndex, Missile.MissileIndex);
- end
- end
- end
- end
- --Main
- function Update(I)
- --Startup
- WeaponsCheck(I);
- if MissileInterceptor == true then
- GenerateTargetListMissileInterceptor(I);
- else
- GenerateTargetList(I);
- end
- GenerateMissileList(I);
- --End Startup
- if NumOfTargets <=0 then
- NoTargetSafelyDetonte(I);
- else
- CalculateEnemyFleetScore(I); --Also calculates how many missiles are to be fired at each target
- AssignTargetForMissile(I);
- --Guide missiles towards thier targets
- PredictInterceptPosition(I);
- CheckAngleToIntercept(I);
- CruiseTowardsTarget(I);
- MissileSeaDive(I);
- end
- AllyCollisionAviodance(I); --Check to make sure we don't blow up any allies
- AimMissiles(I); --Now aim at target
- SetMissileThrust(I);
- ProximityDetonation(I);
- EmptyFuelDetonation(I);
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement