Created
June 11, 2013 14:44
-
-
Save TDC-bob/5757427 to your computer and use it in GitHub Desktop.
Artillery support fire
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--------------------------------------- | |
---- Artillery Enhancement Scrip ---- | |
--------------------------------------- | |
-- | |
-- v1.0 - 26. May 2013 | |
-- By Marc "MBot" Marbot | |
-- | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
-- Control functions: | |
-- | |
-- AddFiringBattery(GroupName, mission, level, priority, displacement) | |
-- GroupName: string, name of group | |
-- mission: string, "DS", "CF" or "All" | |
-- level: number, position in the chain-of-command, digit-digit-digit-digit-digit | |
-- priority: number, minimum priority of firemission the battery will accept | |
-- displacement: number, 1 = no displacement, 2 = displacement under fire, 3 = displacement after every firemission | |
-- | |
-- AdjustFiringBattery(GroupName, mission, level, priority, displacement) | |
-- GroupName: string, name of group | |
-- mission: string, "None", "DS", "CF" or "All" | |
-- level: number, position in the chain-of-command, digit-digit-digit-digit-digit | |
-- priority: number, minimum priority of firemission the battery will accept | |
-- displacement: number, 1 = no displacement, 2 = displacement under fire, 3 = displacement after every firemission | |
-- | |
-- BatteryAddNuke(GroupName, quantity, priority) | |
-- GroupName: string, name of group | |
-- quantity: number, amount of nuclear rounds to add to battery | |
-- priority: number, minimum priority of firemission to authorize release of nuclear shell | |
-- | |
-- BatterySetNuke(GroupName, quantity, priority) | |
-- GroupName: string, name of group | |
-- quantity: number, set the amount of nuclear rounds in battery | |
-- priority: number, minimum priority of firemission to authorize release of nuclear shell | |
-- | |
-- AddSpotter(UnitName, range, level) | |
-- UnitName: string, name of unit | |
-- range: number, target detection range of spotter in meters | |
-- level: number, position in the chain-of-command, digit-digit-digit-digit-digit | |
-- | |
-- RemoveSpotter(UnitName) | |
-- UnitName: string, name of unit | |
-- | |
-- AddCounterfireRadar(UnitName, type, level) | |
-- UnitName: string, name of unit | |
-- type: string, "Q36", "Q36-360", "Q37" , "ARK1" or "Omni" | |
-- level: number, position in the chain-of-command, digit-digit-digit-digit-digit | |
-- | |
-- RemoveCounterfieRadar(UnitName) | |
-- UnitName: string, name of unit | |
-- | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
-- Table of content: | |
-- | |
-- General functions | |
-- Management of Firing Batteries | |
-- Displacement of Firing Batteries | |
-- Nuclear Shells | |
-- Management of Spotters | |
-- Management of Counterfire Radars | |
-- Management of Counterfire Radar Detection | |
-- Management of Counterfire Tracks | |
-- Detection of shot events, counting shots of batteries, manage counterfire radar detection, add shots to CFtracks | |
-- Management of Fire Missions | |
-- Debug | |
-- | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
do | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--General functions | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Function to return the heading in degrees of a Pos3 position | |
local function GetHeading(Pos3) | |
if (Pos3.x.x > 0) and (Pos3.x.z == 0) then | |
return 360 | |
elseif (Pos3.x.x > 0) and (Pos3.x.z > 0) then | |
return math.deg(math.atan(Pos3.x.z / Pos3.x.x)) | |
elseif (Pos3.x.x == 0) and (Pos3.x.z > 0) then | |
return 90 | |
elseif (Pos3.x.x < 0) and (Pos3.x.z > 0) then | |
return 90 - math.deg(math.atan(Pos3.x.x / Pos3.x.z)) --90 minus ... because Pos3.x.x / Pos3.x.z is a negative value | |
elseif (Pos3.x.x < 0) and (Pos3.x.z == 0) then | |
return 180 | |
elseif (Pos3.x.x < 0) and (Pos3.x.z < 0) then | |
return 180 + math.deg(math.atan(Pos3.x.z / Pos3.x.x)) | |
elseif (Pos3.x.x == 0) and (Pos3.x.z < 0) then | |
return 270 | |
elseif (Pos3.x.x > 0) and (Pos3.x.z < 0) then | |
return 270 - math.deg(math.atan(Pos3.x.x / Pos3.x.z)) --270 minus ... because Pos3.x.x / Pos3.x.z is a negative value | |
end | |
end | |
--Function to return the heading from Vec2a to Vec2b | |
local function GetHeadingBetween(Vec2a, Vec2b) | |
local deltax = Vec2b.x - Vec2a.x | |
local deltay = Vec2b.y - Vec2a.y | |
if (deltax > 0) and (deltay == 0) then | |
return 360 | |
elseif (deltax > 0) and (deltay > 0) then | |
return math.deg(math.atan(deltay / deltax)) | |
elseif (deltax == 0) and (deltay > 0) then | |
return 90 | |
elseif (deltax < 0) and (deltay > 0) then | |
return 90 - math.deg(math.atan(deltax / deltay)) --90 minus ... becaue Pos3.x.x / Pos3.x.z is a negative value | |
elseif (deltax < 0) and (deltay == 0) then | |
return 180 | |
elseif (deltax < 0) and (deltay < 0) then | |
return 180 + math.deg(math.atan(deltay / deltax)) | |
elseif (deltax == 0) and (deltay < 0) then | |
return 270 | |
elseif (deltax > 0) and (deltay < 0) then | |
return 270 - math.deg(math.atan(deltax / deltay)) --270 minus ... becaue Pos3.x.x / Pos3.x.z is a negative value | |
end | |
end | |
--Function to return the distance in meters between two Vec2 positions | |
local function GetDistance(Vec2a, Vec2b) | |
local deltax = Vec2b.x - Vec2a.x | |
local deltay = Vec2b.y - Vec2a.y | |
return math.sqrt(math.pow(deltax, 2) + math.pow(deltay, 2)) | |
end | |
--Function to return the Vec2 position through heading and distance from a initial position | |
local function GetOffsetPosition(InitVec2, heading, distance) | |
return { | |
x = InitVec2.x + math.cos(heading) * distance, | |
y = InitVec2.y + math.sin(heading) * distance | |
} | |
end | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Management of Firing Batteries | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
BlueFiringBattery = {} --Table to store all blue artillery groups | |
RedFiringBattery = {} --Table to store all red artillery groups | |
--Function to return the type of artillery group | |
local function getGroupType(GroupName) --Groups can consist of multiple units of different types. We will assign the type of the first artillery unit in the group as a property of the whole group. | |
local group = Group.getByName(GroupName) --Get the group by its name provided as function argument | |
local units = group:getUnits() --Get an array of all units in the group | |
for n = 1, #units do --Iterate through all units of the group | |
local type = units[n]:getTypeName() --Get the type of the n-th unit | |
if (type == "2B11 mortar") or (type == "SAU 2-C9") or (type == "M-109") or (type == "SAU Gvozdika") or (type == "SAU Akatsia" ) or (type == "SAU Msta") or (type == "MLRS") or (type == "Grad-URAL") or (type == "Smerch") then --Check if the unit is an artillery system | |
return type --If the unit is an artillery system, return its type | |
end | |
end | |
end | |
--Table that holds the properties of all artillery types | |
local ArtilleryProperties = { | |
["2B11 mortar"] = { | |
minrange = 500, --Minimal firing range | |
maxrange = 7000, --Maximal firing range | |
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type | |
minAmmo = 0, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire | |
displacementTime = 0 --Time the battery waits between firing and moving | |
}, | |
["SAU 2-C9"] = { | |
minrange = 500, --Minimal firing range | |
maxrange = 7000, --Maximal firing range | |
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type | |
minAmmo = 0, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire | |
displacementTime = 10 --Time the battery waits between firing and moving | |
}, | |
["M-109"] = { | |
minrange = 300, --Minimal firing range | |
maxrange = 22000, --Maximal firing range | |
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type | |
minAmmo = 160, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire | |
displacementTime = 10 --Time the battery waits between firing and moving | |
}, | |
["SAU Gvozdika"] = { | |
minrange = 300, --Minimal firing range | |
maxrange = 15000, --Maximal firing range | |
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type | |
minAmmo = 0, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire | |
displacementTime = 10 --Time the battery waits between firing and moving | |
}, | |
["SAU Akatsia"] = { | |
minrange = 300, --Minimal firing range | |
maxrange = 17000, --Maximal firing range | |
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type | |
minAmmo = 0, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire | |
displacementTime = 10 --Time the battery waits between firing and moving | |
}, | |
["SAU Msta"] = { | |
minrange = 300, --Minimal firing range | |
maxrange = 23500, --Maximal firing range | |
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type | |
minAmmo = 300, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire | |
displacementTime = 10 --Time the battery waits between firing and moving | |
}, | |
["MLRS"] = { | |
minrange = 10000, --Minimal firing range | |
maxrange = 32000, --Maximal firing range | |
FM_rounds = 12, --The total amount of shots of a fire mission for a battery of this unit type | |
minAmmo = 12, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire | |
displacementTime = 10 --Time the battery waits between firing and moving | |
}, | |
["Grad-URAL"] = { | |
minrange = 5000, --Minimal firing range | |
maxrange = 19000, --Maximal firing range | |
FM_rounds = 120, --The total amount of shots of a fire mission for a battery of this unit type | |
minAmmo = 40, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire | |
displacementTime = 120 --Time the battery waits between firing and moving | |
}, | |
["Smerch"] = { | |
minrange = 20000, --Minimal firing range | |
maxrange = 70000, --Maximal firing range | |
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type | |
minAmmo = 12, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire | |
displacementTime = 120 --Time the battery waits between firing and moving | |
} | |
} | |
--Function to add an artillery battery to table | |
function AddFiringBattery(GroupName, mis, lvl, prio, disp) --Arguments: GroupName: string name of group; mis: string "DS", "CF" or "All"; lvl: digit-digit-digit-digit-digit (i.e. 11111); prio: number, minimum priority of FM for this battery; disp: number, shoot-and-scoot level 1-3 | |
local coal = Group.getByName(GroupName):getCoalition() --Get coalition of group | |
local GroupType = getGroupType(GroupName) --Get the type of battery. Battery is of type as first artillery unit found in group. Function getGroupType is a custom function. | |
if coal == 1 then --If group is red | |
RedFiringBattery[#RedFiringBattery + 1] = { | |
name = GroupName, --Name of the group | |
type = GroupType, --Type of battery (same as first arty unit found in group) | |
minrange = ArtilleryProperties[GroupType].minrange, --Minimal firing range | |
maxrange = ArtilleryProperties[GroupType].maxrange, --Maximal firing range | |
FM_rounds = ArtilleryProperties[GroupType].FM_rounds, --The total amount of shots of a fire mission for a battery of this unit type | |
displacementTime = ArtilleryProperties[GroupType].displacementTime, --Time the battery needs to transition from firing to moving | |
mission = mis or "All", --Mission that the battery supports. String. Valid entries: "DS" = Direct Support, "CF" = Counterfire, "All" = DS + CF. Default "All" if no argument is specified. | |
level = lvl or 11111, --hierarchy level of battery. Format digit-digit-digit-digit-digit. Default 11111 if no argument is specified. | |
priority = prio or 0, --The minimum priority a fire mission has to have for this battery. Default 0 if no argument is specified. | |
displace = disp or 1, --The shoot-and-scoot level of the battery. 1: stay in place, 2: displace under fire, 3: displace after every firemission. Default 1 if no argument is specified. | |
status = "ready", --Status of the battery. String. Valid entries: "ready", "firing", "displacing". Initial state is "ready". | |
shotcounter = 0, --Counter to track the number of shots when executing a fire mission | |
displacementcounter = 0, --Counter to track the number of displacements done by the battery | |
nukes = 0, --Amount if nuclear rounds in battery | |
nukepriority = 100, --Minimum firemission priority to engage with nuclear round | |
} | |
elseif coal == 2 then --If group is blue | |
BlueFiringBattery[#BlueFiringBattery + 1] = { | |
name = GroupName, --Name of the group | |
type = GroupType, --Type of battery (same as first arty unit found in group) | |
minrange = ArtilleryProperties[GroupType].minrange, --Minimal firing range | |
maxrange = ArtilleryProperties[GroupType].maxrange, --Maximal firing range | |
FM_rounds = ArtilleryProperties[GroupType].FM_rounds, --The total amount of shots of a fire mission for a battery of this unit type | |
displacementTime = ArtilleryProperties[GroupType].displacementTime, --Time the battery needs to transition from firing to moving | |
mission = mis or "All", --Mission that the battery supports. String. Valid entries: "DS" = Direct Support, "CF" = Counterfire, "All" = DS + CF | |
level = lvl or 11111, --hierarchy level of battery. Format digit-digit-digit-digit-digit | |
priority = prio or 0, --The minimum priority a fire mission has to have for this battery | |
displace = disp or 1, --The shoot-and-scoot level of the battery. 1: stay in place, 2: displace under fire, 3: displace after every firemission | |
status = "ready", --Status of the battery. String. Valid entries: "ready", "firing", "displacing". Initial state is "ready". | |
shotcounter = 0, --Counter to track the number of shots when executing a fire mission | |
displacementcounter = 0, --Counter to track the number of displacements done by the battery | |
nukes = 0, --Amount if nuclear rounds in battery | |
nukepriority = 100, --Minimum firemission priority to engage with nuclear round | |
} | |
end | |
end | |
--Function to adjust the settings of an artillery group | |
function AdjustFiringBattery(GroupName, mis, lvl, prio, disp) --Arguments: GroupName: string name of group; mis: string "DS", "CF" or "All"; lvl: digit-digit-digit-digit-digit (i.e. 11111) | |
local coal = Group.getCoalition(GroupName) --Get coalition of group | |
if coal == 1 then --If group is red | |
for n = 1, #RedFiringBattery do --Iterate through all groups | |
if RedFiringBattery[n].name == GroupName then --Find n-th entry that contains group we want to remove | |
RedFiringBattery[n].mission = mis or "All" --Set new mission. Valid entries: "DS" = Direct Support, "CF" = Counterfire, "All" = DS + CF. To disable group, set mission to anything not valid. | |
RedFiringBattery[n].level = lvl or 11111 --hierarchy level of battery. Format digit-digit-digit-digit-digit | |
RedFiringBattery[n].priority = prio or 0 --Set minimum priority of firemission for battery to accept | |
RedFiringBattery[n].displace = disp or 1 --Set displacement level | |
break --Quit for loop | |
end | |
end | |
elseif coal == 2 then --If group is blue | |
for n = 1, #BlueFiringBattery do --Iterate through all groups | |
if BlueFiringBattery[n].name == GroupName then --Find n-th entry that contains group we want to remove | |
BlueFiringBattery[n].mission = mis or "All" --Set new mission. Valid entries: "DS" = Direct Support, "CF" = Counterfire, "All" = DS + CF. To disable group, set mission to anything not valid. | |
BlueFiringBattery[n].level = lvl or 11111 --hierarchy level of battery. Format digit-digit-digit-digit-digit | |
BlueFiringBattery[n].priority = prio or 0 --Set minimum priority of firemission for battery to accept | |
BlueFiringBattery[n].displace = disp or 1 --Set displacement level | |
break --Quit for loop | |
end | |
end | |
end | |
end | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Displacement of Firing Batteries | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
function BlueDisplacement(n, underFire) | |
if BlueFiringBattery[n].type == "2B11 mortar" then --Check if the battery is a 2B11 mortar battery. 2B11 is static and cannot displace | |
BlueFiringBattery[n].status = "ready" --Make the battery ready to fire again | |
else | |
if underFire == "under fire" or BlueFiringBattery[n].displace == 3 then --Check if the battery is under fire or set to displace after each fire mission | |
BlueFiringBattery[n].status = "displacing" --Set status of battery to displacing | |
local grp = Group.getByName(BlueFiringBattery[n].name) --Get group | |
local units = grp:getUnits() --Get uits of group | |
local grpP = units[1]:getPoint() --Get Vec3 position of first unit of group | |
if BlueFiringBattery[n].displacementcounter == 0 then --Check if this is the first displacement of the battery | |
BlueFiringBattery[n].initpos = grpP --If yes, store the initial position of the battery | |
end | |
local dest = {} --New destination position | |
repeat | |
dest.x = BlueFiringBattery[n].initpos.x + math.random(-400, 400) --Find a new destination x coordinate in a 800x800m square centered on the initial group position | |
dest.y = BlueFiringBattery[n].initpos.z + math.random(-400, 400) --Find a new destination y coordinate in a 800x800m square centered on the initial group position | |
until GetDistance(dest, {x = grpP.x, y = grpP.z}) > 300 and land.getSurfaceType(dest) == 1 --Try to find new destination coordinates until dest is more than 300m away from the current group position and dest is on land surface | |
local DisplaceTask = { --Define new mission-task | |
id = 'Mission', | |
params = { | |
route = { | |
points = { | |
[1] = { --Initial waypoint | |
action = "Custom", | |
x = grpP.x, --Current position of first unit of group | |
y = grpP.z, --Current position of first unit of group | |
speed = 7, --Speed in m/s | |
ETA = 0, | |
ETA_locked = false, | |
name = "Initial Position", | |
task = { | |
id = "ComboTask", | |
params = { | |
tasks = {} | |
} | |
} | |
}, | |
[2] = { --Destination waypont | |
action = "Custom", | |
x = dest.x, --Destination position | |
y = dest.y, --Destination position | |
speed = 7, | |
ETA = 0, | |
ETA_locked = false, | |
name = "Displacement Position", | |
task = { --Perform the following script when arriving | |
id = "WrappedAction", | |
params = { | |
action = { | |
id = "Script", | |
params = { | |
command = "BlueFiringBattery[" .. n .. "].status = 'ready'", --Set status of the battery to ready when arriving at new location | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
local ctrl = grp:getController() --Get controller of group | |
local function DisplaceDelay() --This is a scheduled function | |
Controller.setTask(ctrl, DisplaceTask) --Replace current task of firing battery and assign task to displace. This will remove the group of any further control by the mission designer! | |
return nil --Return nil means that this function is not repeated | |
end | |
timer.scheduleFunction(DisplaceDelay, arg, timer.getTime() + BlueFiringBattery[n].displacementTime) --Do displacement task after waiting the dispacement time of the battery | |
else | |
BlueFiringBattery[n].status = "ready" --If the battery is not set to displace after a firemission, reset status to ready. | |
end | |
end | |
BlueFiringBattery[n].displacementcounter = BlueFiringBattery[n].displacementcounter + 1 --Increase the displacement counter by 1 | |
end | |
function RedDisplacement(n, underFire) | |
if RedFiringBattery[n].type == "2B11 mortar" then --Check if the battery is a 2B11 mortar battery. 2B11 is static and cannot displace | |
RedFiringBattery[n].status = "ready" --Make the battery ready to fire again | |
else | |
if underFire == "under fire" == 2 or RedFiringBattery[n].displace == 3 then --Check if the battery is under fire or set to displace after each fire mission | |
RedFiringBattery[n].status = "displacing" --Set status of battery to displacing | |
local grp = Group.getByName(RedFiringBattery[n].name) --Get group | |
local units = grp:getUnits() --Get uits of group | |
local grpP = units[1]:getPoint() --Get Vec3 position of first unit of group | |
if RedFiringBattery[n].displacementcounter == 0 then --Check if this is the first displacement of the battery | |
RedFiringBattery[n].initpos = grpP --If yes, store the initial position of the battery | |
end | |
local dest = {} --New destination position | |
repeat | |
dest.x = RedFiringBattery[n].initpos.x + math.random(-400, 400) --Find a new destination x coordinate in a 800x800m square centered on the initial group position | |
dest.y = RedFiringBattery[n].initpos.z + math.random(-400, 400) --Find a new destination y coordinate in a 800x800m square centered on the initial group position | |
until GetDistance(dest, {x = grpP.x, y = grpP.z}) > 300 and land.getSurfaceType(dest) == 1 --Try to find new destination coordinates until dest is more than 300m away from the current group position and dest is on land surface | |
local DisplaceTask = { --Define new mission-task | |
id = 'Mission', | |
params = { | |
route = { | |
points = { | |
[1] = { --Initial waypoint | |
action = "Custom", | |
x = grpP.x, --Current position of first unit of group | |
y = grpP.z, --Current position of first unit of group | |
speed = 7, --Speed in m/s | |
ETA = 0, | |
ETA_locked = false, | |
name = "Initial Position", | |
task = { | |
id = "ComboTask", | |
params = { | |
tasks = {} | |
} | |
} | |
}, | |
[2] = { --Destination waypont | |
action = "Custom", | |
x = dest.x, --Destination position | |
y = dest.y, --Destination position | |
speed = 7, | |
ETA = 0, | |
ETA_locked = false, | |
name = "Displacement Position", | |
task = { --Perform the following script when arriving | |
id = "WrappedAction", | |
params = { | |
action = { | |
id = "Script", | |
params = { | |
command = "RedFiringBattery[" .. n .. "].status = 'ready'", --Set status of the battery to ready when arriving at new location | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
local ctrl = grp:getController() --Get controller of group | |
local function DisplaceDelay() --This is a scheduled function | |
Controller.setTask(ctrl, DisplaceTask) --Replace current task of firing battery and assign task to displace. This will remove the group of any further control by the mission designer! | |
return nil --Return nil means that this function is not repeated | |
end | |
timer.scheduleFunction(DisplaceDelay, arg, timer.getTime() + RedFiringBattery[n].displacementTime) --Do displacement task after waiting the dispacement time of the battery | |
else | |
RedFiringBattery[n].status = "ready" --If the battery is not set to displace after a firemission, reset status to ready. | |
end | |
end | |
RedFiringBattery[n].displacementcounter = RedFiringBattery[n].displacementcounter + 1 --Increase the displacement counter by 1 | |
end | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Nuclear Shells | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--function to add nuclear rounds to batteries | |
function BatteryAddNuke(GroupName, q, p) --Arguments: GroupName string, q: quantity of nuclear shells to add, p: minimum priority level for nuclear attack | |
local coal = Group.getByName(GroupName):getCoalition() --Get coalition of group | |
if coal == 1 then --If group is red | |
for n = 1, #RedFiringBattery do --Iterate through all groups | |
if RedFiringBattery[n].name == GroupName then --Find the battery that is being refered to | |
if RedFiringBattery[n].type == "M-109" or RedFiringBattery[n].type == "SAU Akatsia" or RedFiringBattery[n].type == "SAU Msta" then --Check if the group is of nuclear capable type | |
RedFiringBattery[n].nukes = RedFiringBattery[n].nukes + q --Add nuclear shells to battery | |
RedFiringBattery[n].nukepriority = p or 100 --Set mimimum priority of firemission for nuclear attack; set to 100 if not specified | |
break | |
end | |
end | |
end | |
elseif coal == 2 then --If group is blue | |
for n = 1, #BlueFiringBattery do --Iterate through all groups | |
if BlueFiringBattery[n].name == GroupName then --Find the battery that is being refered to | |
if BlueFiringBattery[n].type == "M-109" or BlueFiringBattery[n].type == "SAU Akatsia" or BlueFiringBattery[n].type == "SAU Msta" then --Check if the group is of nuclear capable type | |
BlueFiringBattery[n].nukes = BlueFiringBattery[n].nukes + q --Add nuclear shells to battery | |
BlueFiringBattery[n].nukepriority = p or 100 --Set mimimum priority of firemission for nuclear attack; set to 100 if not specified | |
break | |
end | |
end | |
end | |
end | |
end | |
--function to set the amount of nuclear rounds of a battery | |
function BatterySetNuke(GroupName, q, p) | |
local coal = Group.getByName(GroupName):getCoalition() --Get coalition of group | |
if coal == 1 then --If group is red | |
for n = 1, #RedFiringBattery do --Iterate through all groups | |
if RedFiringBattery[n].name == GroupName then --Find the battery that is being refered to | |
if RedFiringBattery[n].type == "M-109" or RedFiringBattery[n].type == "SAU Akatsia" or RedFiringBattery[n].type == "SAU Msta" then --Check if the group is of nuclear capable type | |
RedFiringBattery[n].nukes = q --Set number of nuclear shells of battery | |
RedFiringBattery[n].nukepriority = p or 100 --Set mimimum priority of firemission for nuclear attack; set to 100 if not specified | |
break | |
end | |
end | |
end | |
elseif coal == 2 then --If group is blue | |
for n = 1, #BlueFiringBattery do --Iterate through all groups | |
if BlueFiringBattery[n].name == GroupName then --Find the battery that is being refered to | |
if RedFiringBattery[n].type == "M-109" or RedFiringBattery[n].type == "SAU Akatsia" or RedFiringBattery[n].type == "SAU Msta" then --Check if the group is of nuclear capable type | |
BlueFiringBattery[n].nukes = q --Set number of nuclear shells of battery | |
BlueFiringBattery[n].nukepriority = p or 100 --Set mimimum priority of firemission for nuclear attack; set to 100 if not specified | |
break | |
end | |
end | |
end | |
end | |
end | |
--Function to detonate a nuclear shell | |
local function NuclearShell(GroupName) | |
local firstshot = true --Local variable to only allow the first shot in a firemission to be nuclear | |
NuclearShellHandler = {} --Event handler to capture the shot of a nuclear artillery shell | |
function NuclearShellHandler:onEvent(event) | |
if event.id == world.event.S_EVENT_SHOT then --Check if event is a shot event | |
local Init = event.initiator --Get initiator unit of shot event | |
local InitGroup = Init:getGroup() --Get group that shot | |
if InitGroup == Group.getByName(GroupName) then --Check if the group that shot is the group that is provided as argument | |
local Wep = event.weapon --Get weapon of the shot event | |
local InitWep = Wep:getTypeName() --Get type of the weapon | |
if (InitWep == "weapons.shells.M185_155") then --Check if the weapon that is shot is a W48 warhead (M109) | |
if firstshot == true then --Check that this is the first round in a nuclear firemission | |
firstshot = false | |
local function TrackShell() --Scheduled function | |
local ShellPos = Wep:getPoint() --Get position of shell | |
if ShellPos.y < land.getHeight({x = ShellPos.x, y = ShellPos.z}) + 15 then --If shell is less than 15m AGL | |
trigger.action.explosion(ShellPos, 75000) --Detonate warhead. Yield for W48 warhead 0.075kt | |
Wep:destroy() --Remove shell | |
world.removeEventHandler(NuclearShellHanlder) --Remove event handler | |
return | |
end | |
return timer.getTime() + 0.05 --Repeat 20 times a second | |
end | |
timer.scheduleFunction(TrackShell, nil, timer.getTime() + 10) --Start scheduled function TrackShell in 5 seconds | |
end | |
elseif (InitWep == "weapons.shells.2A33_152" ) or (InitWep == "weapons.shells.2A64_152") then --Check if the weapon that is shot is a ZBV3 warhead (2S3 or 2S19) | |
if firstshot == true then --Check that this is the first round in a nuclear firemission | |
firstshot = false | |
local function TrackShell() --Scheduled function | |
local ShellPos = Wep:getPoint() --Get position of shell | |
if ShellPos.y < land.getHeight({x = ShellPos.x, y = ShellPos.z}) + 15 then --If shell is less than 15m AGL | |
trigger.action.explosion(ShellPos, 1000000) --Detonate warhead. Yield for ZBV3 warhead 1kt | |
Wep:destroy() --Remove shell | |
world.removeEventHandler(NuclearShellHanlder) --Remove event handler | |
return | |
end | |
return timer.getTime() + 0.05 --Repeat 20 times a second | |
end | |
timer.scheduleFunction(TrackShell, nil, timer.getTime() + 10) --Start scheduled function TrackShell in 5 seconds | |
end | |
end | |
end | |
end | |
end | |
world.addEventHandler(NuclearShellHandler) | |
end | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Management of Spotters | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
BlueSpotter = {} --Array holding all blue spotter units | |
RedSpotter = {} --Array holding all red spotter units | |
--Function to add a spotter unit | |
function AddSpotter(UnitName, rng, lvl) | |
local coal = Unit.getByName(UnitName):getCoalition() --Get coalition of unit | |
if coal == 1 then --If unit is red | |
RedSpotter[#RedSpotter + 1] = { --Add new red spotter to array | |
name = UnitName, --Name of spotter unit | |
range = rng or 5000, --Detection range of spotter unit. Default to 5000m if no argument is provided | |
level = lvl or 1111, --hierarchy level of spotter unit. Format digit-digit-digit-digit-digit. Default to 11111 if no argument is provided | |
target = {}, --Table to store targets of this spotter | |
track = {} --Array to store tracks of this spotter | |
} | |
elseif coal == 2 then --If unit is blue | |
BlueSpotter[#BlueSpotter + 1] = { --Add new blue spotter to array | |
name = UnitName, --Name of spotter unit | |
range = rng or 5000, --Detection range of spotter unit. Default to 5000m if no argument is provided | |
level = lvl or 1111, --hierarchy level of spotter unit. Format digit-digit-digit-digit-digit. Default to 11111 if no argument is provided | |
target = {}, --Table to store targets of this spotter | |
track = {} --Array to store tracks of this spotter | |
} | |
end | |
end | |
--Function to remove a counterfire radar from table | |
function RemoveSpotter(UnitName) | |
local coal = Unit.getByName(UnitName):getCoalition() --Get coalition of unit | |
if coal == 1 then --If units is red | |
for n = 1, #RedSpotter do --Iterate through all units | |
if RedSpotter[n].name == UnitName then --Find n-th entry that contains unit we want to remove | |
table.remove(RedSpotter, n) --Remove unit | |
break --Quit for loop | |
end | |
end | |
elseif coal == 2 then --If unit is blue | |
for n = 1, #BlueSpotter do --Iterate through all unit | |
if BlueSpotter[n].name == UnitName then --Find n-th entry that contains unit we want to remove | |
table.remove(BlueSpotter, n) --Remove unit | |
break --Quit for loop | |
end | |
end | |
end | |
end | |
--Function to determine the value of a target | |
local function getValue(tgt) | |
local v = 0 --Variable to summ value of this target | |
if tgt:hasAttribute("Tanks") then | |
v = v + 10 | |
end | |
if tgt:hasAttribute("IFV") then | |
v = v + 5 | |
end | |
if tgt:hasAttribute("APC") then | |
v = v + 3 | |
end | |
if tgt:hasAttribute("Artillery") then | |
v = v + 10 | |
end | |
if tgt:hasAttribute("Trucks") then | |
v = v + 1 | |
end | |
if tgt:hasAttribute("Cars") then | |
v = v + 1 | |
end | |
if tgt:hasAttribute("Infantry") then | |
v = v + 1 | |
end | |
if tgt:hasAttribute("ATGM") then | |
v = v + 10 | |
end | |
if tgt:hasAttribute("Buildings") then | |
v = v + 3 | |
end | |
if tgt:hasAttribute("Static AAA") then | |
v = v + 3 | |
end | |
if tgt:hasAttribute("Mobile AAA") then | |
v = v + 3 | |
end | |
if tgt:hasAttribute("SAM SR") then | |
v = v + 20 | |
end | |
if tgt:hasAttribute("SAM TR") then | |
v = v + 15 | |
end | |
if tgt:hasAttribute("SAM LL") then | |
v = v + 10 | |
end | |
if tgt:hasAttribute("SR SAM") then | |
v = v + 20 | |
end | |
if tgt:hasAttribute("MR SAM") then | |
v = v + 30 | |
end | |
if tgt:hasAttribute("LR SAM") then | |
v = v + 40 | |
end | |
if tgt:hasAttribute("MANPADS") then | |
v = v + 10 | |
end | |
if tgt:hasAttribute("Helicopters") then | |
v = v + 40 | |
end | |
return v --Return total value | |
end | |
--Detecting blue targets and creating tracks | |
local function BlueSpotterDetectionHandler(tgt, n) --Function to be executed for each unit found unit | |
if tgt:getCoalition() == 1 then --Check if found unit is red | |
local desc = tgt:getDesc() --Get descriptor of target | |
if desc.category == 2 or desc.category == 1 then --If target is a ground unit or helicopter | |
local tgtVec3 = tgt:getPoint() --Get Vec3 position of target | |
local sptVec3 = Unit.getByName(BlueSpotter[n].name):getPoint() --Get Vec3 position of spotter | |
if land.isVisible({x = tgtVec3.x, y = tgtVec3.y + 2, z = tgtVec3.z}, {x = sptVec3.x, y = sptVec3.y + 2, z = sptVec3.z}) then --Check if there is a line of sight between spotter and target. Add 2 meters of height to the y axis to avoid LOS problems very close to the ground. | |
local tgtName = tgt:getName() --Get name of target | |
if BlueSpotter[n].target[tgtName] then --Check if target is already in target-table of spotter | |
if (desc.category == 2 and tgtVec3.x <= BlueSpotter[n].target[tgtName].x + 5 and tgtVec3.x >= BlueSpotter[n].target[tgtName].x - 5 and tgtVec3.z <= BlueSpotter[n].target[tgtName].z + 5 and tgtVec3.z >= BlueSpotter[n].target[tgtName].z - 5) or (desc.category == 1 and tgtVec3.x <= BlueSpotter[n].target[tgtName].x + 20 and tgtVec3.x >= BlueSpotter[n].target[tgtName].x - 20 and tgtVec3.z <= BlueSpotter[n].target[tgtName].z + 20 and tgtVec3.z >= BlueSpotter[n].target[tgtName].z - 20 and land.getHeight({x = tgtVec3.x, y = tgtVec3.z}) + 20 > tgtVec3.y) then --Check if target position is within 5m of previously stored target position if the target is a ground unit, or if the target is within 20m of previsouly stored target position and target altitude is lower than 20m AGL if target is a helicopter. If yes, the target is considered stationary and eligible for an artillery strike. | |
BlueSpotter[n].target[tgtName] = tgtVec3 --Store the new target position | |
---------------Put target into track--------------- | |
for i = 0, #BlueSpotter[n].track do --Iterate through all tracks | |
if i > 0 and BlueSpotter[n].track[i].active == true and tgtVec3.x <= BlueSpotter[n].track[i].coord.x + 200 and tgtVec3.x >= BlueSpotter[n].track[i].coord.x - 200 and tgtVec3.z <= BlueSpotter[n].track[i].coord.y + 200 and tgtVec3.z >= BlueSpotter[n].track[i].coord.y - 200 then --Check if target is within 200m of existing track. For simplicity the check is made with x/y axis and not radius. | |
BlueSpotter[n].track[i].coord.x = (BlueSpotter[n].track[i].coord.x + tgtVec3.x) / 2 --Average track x axis with new target position | |
BlueSpotter[n].track[i].coord.y = (BlueSpotter[n].track[i].coord.y + tgtVec3.z) / 2 --Average track y axis with new target position | |
if timer.getTime() - 2 < BlueSpotter[n].track[i].time then --Check if track was update within last 2 seconds | |
BlueSpotter[n].track[i].value = BlueSpotter[n].track[i].value + getValue(tgt) --If yes, add target value to total track value | |
else | |
BlueSpotter[n].track[i].value = getValue(tgt) --If no, reset track value with target value | |
end | |
BlueSpotter[n].track[i].time = timer.getTime() --Reset timer | |
BlueSpotter[n].track[i].active = true --The track is active | |
do break end --Quit track loop | |
elseif i == #BlueSpotter[n].track then --If target is not within 200m of an existing track... | |
BlueSpotter[n].track[i + 1] = { --create a new track | |
coord = {x = tgtVec3.x, y = tgtVec3.z}, --Set track coordinates | |
time = timer.getTime(), --Time the track was created | |
value = getValue(tgt), --Value of the track. Initial value is value of initial target | |
active = true, --The track is active | |
firemission = false --The track currently has not created a firemission | |
} | |
break --Quit track loop | |
end | |
end | |
--------------------------------------------------- | |
else --The target has moved more than 5m since the last time it was detected and is therfore ineligible for an artillery strike. | |
BlueSpotter[n].target[tgtName] = tgtVec3 --Store the new target position | |
end | |
else --If not... | |
table.insert(BlueSpotter[n].target, tgtName) --Add target to target-table of spotter | |
BlueSpotter[n].target[tgtName] = tgtVec3 --Store target Vec3 position | |
end | |
end | |
end | |
end | |
return true | |
end | |
--Detecting red targets and creating tracks | |
local function RedSpotterDetectionHandler(tgt, n) --Function to be executed for each unit found unit | |
if tgt:getCoalition() == 2 then --Check if found unit is blue | |
local desc = tgt:getDesc() --Get descriptor of target | |
if desc.category == 2 or desc.category == 1 then --If target is a ground unit or helicopter | |
local tgtVec3 = tgt:getPoint() --Get Vec3 position of target | |
local sptVec3 = Unit.getByName(RedSpotter[n].name):getPoint() --Get Vec3 position of spotter | |
if land.isVisible({x = tgtVec3.x, y = tgtVec3.y + 2, z = tgtVec3.z}, {x = sptVec3.x, y = sptVec3.y + 2, z = sptVec3.z}) then --Check if there is a line of sight between spotter and target. Add 2 meters of height to the y axis to avoid LOS problems very close to the ground. | |
local tgtName = tgt:getName() --Get name of target | |
if RedSpotter[n].target[tgtName] then --Check if target is already in target-table of spotter | |
if (desc.category == 2 and tgtVec3.x <= RedSpotter[n].target[tgtName].x + 5 and tgtVec3.x >= RedSpotter[n].target[tgtName].x - 5 and tgtVec3.z <= RedSpotter[n].target[tgtName].z + 5 and tgtVec3.z >= RedSpotter[n].target[tgtName].z - 5) or (desc.category == 1 and tgtVec3.x <= RedSpotter[n].target[tgtName].x + 20 and tgtVec3.x >= RedSpotter[n].target[tgtName].x - 20 and tgtVec3.z <= RedSpotter[n].target[tgtName].z + 20 and tgtVec3.z >= RedSpotter[n].target[tgtName].z - 20 and land.getHeight({x = tgtVec3.x, y = tgtVec3.z}) + 20 > tgtVec3.y) then --Check if target position is within 5m of previously stored target position if the target is a ground unit, or if the target is within 20m of previsouly stored target position and target altitude is lower than 20m AGL if target is a helicopter. If yes, the target is considered stationary and eligible for an artillery strike. | |
RedSpotter[n].target[tgtName] = tgtVec3 --Store the new target position | |
---------------Put target into track--------------- | |
for i = 0, #RedSpotter[n].track do --Iterate through all tracks | |
if i > 0 and RedSpotter[n].track[i].active == true and tgtVec3.x <= RedSpotter[n].track[i].coord.x + 200 and tgtVec3.x >= RedSpotter[n].track[i].coord.x - 200 and tgtVec3.z <= RedSpotter[n].track[i].coord.y + 200 and tgtVec3.z >= RedSpotter[n].track[i].coord.y - 200 then --Check if target is within 200m of existing track. For simplicity the check is made with x/y axis and not radius. | |
RedSpotter[n].track[i].coord.x = (RedSpotter[n].track[i].coord.x + tgtVec3.x) / 2 --Average track x axis with new target position | |
RedSpotter[n].track[i].coord.y = (RedSpotter[n].track[i].coord.y + tgtVec3.z) / 2 --Average track y axis with new target position | |
if timer.getTime() - 2 < RedSpotter[n].track[i].time then --Check if track was update within last 2 seconds | |
RedSpotter[n].track[i].value = RedSpotter[n].track[i].value + getValue(tgt) --If yes, add target value to total track value | |
else | |
RedSpotter[n].track[i].value = getValue(tgt) --If no, reset track value with target value | |
end | |
RedSpotter[n].track[i].time = timer.getTime() --Reset timer | |
RedSpotter[n].track[i].active = true --The track is active | |
do break end --Quit track loop | |
elseif i == #RedSpotter[n].track then --If target is not within 200m of an existing track... | |
RedSpotter[n].track[i + 1] = { --create a new track | |
coord = {x = tgtVec3.x, y = tgtVec3.z}, --Set track coordinates | |
time = timer.getTime(), --Time the track was created | |
value = getValue(tgt), --Value of the track. Initial value is value of initial target | |
active = true, --The track is active | |
firemission = false --The track currently has not created a firemission | |
} | |
break --Quit track loop | |
end | |
end | |
--------------------------------------------------- | |
else --The target has moved more than 5m since the last time it was detected and is therfore ineligible for an artillery strike. | |
RedSpotter[n].target[tgtName] = tgtVec3 --Store the new target position | |
end | |
else --If not... | |
table.insert(RedSpotter[n].target, tgtName) --Add target to target-table of spotter | |
RedSpotter[n].target[tgtName] = tgtVec3 --Store target Vec3 position | |
end | |
end | |
end | |
end | |
return true | |
end | |
--Spotter detection cycle | |
local function SpotterDetection() --Function to execute spotter search for targers. To be repeated every 10 seconds | |
for n = 1, #BlueSpotter do --Iterate through all blue spotters | |
local Spotter = Unit.getByName(BlueSpotter[n].name) --Get spotter unit | |
local SpotterP = Spotter:getPoint() --Get Vec3 position of spotter unit | |
local SearchArea = { --Define search area for this spotter unit | |
id = world.VolumeType.SPHERE, --Search area is a sphere | |
params = { | |
point = SpotterP, --Centered on spotter unit Vec3 position | |
radius = BlueSpotter[n].range --With a radius of the search range of the spotter unit | |
} | |
} | |
world.searchObjects(Object.Category.UNIT, SearchArea, BlueSpotterDetectionHandler, n) --Execute a search for units in the definded search area. For each found unit, execute function BlueSpotterDetectionHandler | |
end | |
for n = 1, #RedSpotter do --Iterate through all Red spotters | |
local Spotter = Unit.getByName(RedSpotter[n].name) --Get spotter unit | |
local SpotterP = Spotter:getPoint() --Get Vec3 position of spotter unit | |
local SearchArea = { --Define search area for this spotter unit | |
id = world.VolumeType.SPHERE, --Search area is a sphere | |
params = { | |
point = SpotterP, --Centered on spotter unit Vec3 position | |
radius = RedSpotter[n].range --With a radius of the search range of the spotter unit | |
} | |
} | |
world.searchObjects(Object.Category.UNIT, SearchArea, RedSpotterDetectionHandler, n) --Execute a search for units in the definded search area. For each found unit, execute function RedSpotterDetectionHandler | |
end | |
return timer.getTime() + 10 --Repeat function every 10 seconds | |
end | |
timer.scheduleFunction(SpotterDetection, arg, 5) --Run function for the first time after 5 seconds | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Management of Counterfire Radars | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Table that holds the properties of the available counterfire radar types | |
local CounterfireRadarType = { | |
["Q36"] = { | |
name = "AN/TPQ-36 Firefinder", | |
mortar = { --Detection area of mortar shells | |
minrange = 750, --Minimum detection range in meters | |
maxrange = 18000, --Maxium detection range in meters | |
angle = 45, --In degrees. Angle of the scan area left or right from azimuth of unit | |
detect = 90, --Chance of detection in percent | |
poserror = 1, --In percent. Detected position is somewhere within a circle with the radius x% of range. | |
maxaccuracy = 100 --In meters. Detected position is never more accurate than maxaccuracy. | |
}, | |
artillery = { --Detection area of artillery shells | |
minrange = 3000, --Minimum detection range in meters | |
maxrange = 14500, --Maxium detection range in meters | |
angle = 45, --In degrees. Angle of the scan area left or right from azimuth of unit | |
detect = 70, --Chance of detection in percent | |
poserror = 1.5, --In percent. Detected position is somewhere within a circle with the radius x% of range. | |
maxaccuracy = 150 --In meters. Detected position is never more accurate than maxaccuracy. | |
}, | |
rocket = { --Detection area of rockets | |
minrange = 8000, --Minimum detection range in meters | |
maxrange = 24000, --Maxium detection range in meters | |
angle = 45, --In degrees. Angle of the scan area left or right from azimuth of unit | |
detect = 80, --Chance of detection in percent | |
poserror = 2.5, --In percent. Detected position is somewhere within a circle with the radius x% of range. | |
maxaccuracy = 300 --In meters. Detected position is never more accurate than maxaccuracy. | |
} | |
}, | |
["Q36-360"] = { | |
name = "AN/TPQ-36 Firefinder 360°", --AN/TPQ-36 set to scan around 360° with 4 times lower detection propability. | |
mortar = { --Detection area of mortar shells | |
minrange = 750, --Minimum detection range in meters | |
maxrange = 18000, --Maxium detection range in meters | |
angle = 180, --In degrees. Angle of the scan area left or right from azimuth of unit | |
detect = 22.5, --Chance of detection in percent | |
poserror = 1, --In percent. Detected position is somewhere within a circle with the radius x% of range. | |
maxaccuracy = 100 --In meters. Detected position is never more accurate than maxaccuracy. | |
}, | |
artillery = { --Detection area of artillery shells | |
minrange = 3000, --Minimum detection range in meters | |
maxrange = 14500, --Maxium detection range in meters | |
angle = 180, --In degrees. Angle of the scan area left or right from azimuth of unit | |
detect = 17.5, --Chance of detection in percent | |
poserror = 1.5, --In percent. Detected position is somewhere within a circle with the radius x% of range. | |
maxaccuracy = 150 --In meters. Detected position is never more accurate than maxaccuracy. | |
}, | |
rocket = { --Detection area of rockets | |
minrange = 8000, --Minimum detection range in meters | |
maxrange = 24000, --Maxium detection range in meters | |
angle = 180, --In degrees. Angle of the scan area left or right from azimuth of unit | |
detect = 20, --Chance of detection in percent | |
poserror = 2.5, --In percent. Detected position is somewhere within a circle with the radius x% of range. | |
maxaccuracy = 300 --In meters. Detected position is never more accurate than maxaccuracy. | |
} | |
}, | |
["Q37"] = { | |
name = "AN/TPQ-37 Firefinder", | |
mortar = { --Detection area of mortar shells | |
minrange = 4000, --Minimum detection range in meters | |
maxrange = 30000, --Maxium detection range in meters | |
angle = 45, --In degrees. Angle of the scan area left or right from azimuth of unit | |
detect = 85, --Chance of detection in percent | |
poserror = 0.9, --In percent. Detected position is somewhere within a circle with the radius x% of range. | |
maxaccuracy = 90 --In meters. Detected position is never more accurate than maxaccuracy. | |
}, | |
artillery = { --Detection area of artillery shells | |
minrange = 4000, --Minimum detection range in meters | |
maxrange = 30000, --Maxium detection range in meters | |
angle = 45, --In degrees. Angle of the scan area left or right from azimuth of unit | |
detect = 85, --Chance of detection in percent | |
poserror = 0.9, --In percent. Detected position is somewhere within a circle with the radius x% of range. | |
maxaccuracy = 90 --In meters. Detected position is never more accurate than maxaccuracy. | |
}, | |
rocket = { --Detection area of rockets | |
minrange = 4000, --Minimum detection range in meters | |
maxrange = 37000, --Maxium detection range in meters | |
angle = 45, --In degrees. Angle of the scan area left or right from azimuth of unit | |
detect = 85, --Chance of detection in percent | |
poserror = 1, --In percent. Detected position is somewhere within a circle with the radius x% of range. | |
maxaccuracy = 175 --In meters. Detected position is never more accurate than maxaccuracy. | |
} | |
}, | |
["ARK1"] = { | |
name = "ARK-1M Rys", | |
mortar = { --Detection area of mortar shells | |
minrange = 750, --Value of AN/TPQ-36 | |
maxrange = 12000, --Source: http://www.npostrela.com/en/products/museum/92/237/ | |
angle = 45, --Value of AN/TPQ-36 | |
detect = 90, --Value of AN/TPQ-36 | |
poserror = 1, --Value of AN/TPQ-36 | |
maxaccuracy = 100 --Value of AN/TPQ-36 | |
}, | |
artillery = { --Detection area of artillery shells | |
minrange = 3000, --Value of AN/TPQ-36 | |
maxrange = 9000, --Source: http://www.npostrela.com/en/products/museum/92/237/ | |
angle = 45, --Value of AN/TPQ-36 | |
detect = 70, --Value of AN/TPQ-36 | |
poserror = 1.5, --Value of AN/TPQ-36 | |
maxaccuracy = 150 --Value of AN/TPQ-36 | |
}, | |
rocket = { --Detection area of rockets | |
minrange = 8000, --Value of AN/TPQ-36 | |
maxrange = 16000, --Source: http://www.npostrela.com/en/products/museum/92/237/ | |
angle = 45, --Value of AN/TPQ-36 | |
detect = 80, --Value of AN/TPQ-36 | |
poserror = 2.5, --Value of AN/TPQ-36 | |
maxaccuracy = 300 --Value of AN/TPQ-36 | |
} | |
}, | |
["Omni"] = { --A fictional system that dedects everything within 50 km | |
name = "Omni", | |
mortar = { -- | |
minrange = 0, -- | |
maxrange = 50000, -- | |
angle = 180, -- | |
detect = 100, -- | |
poserror = 0, -- | |
maxaccuracy = 0 -- | |
}, | |
artillery = { -- | |
minrange = 0, -- | |
maxrange = 50000, -- | |
angle = 180, -- | |
detect = 100, -- | |
poserror = 0, -- | |
maxaccuracy = 0 -- | |
}, | |
rocket = { -- | |
minrange = 0, -- | |
maxrange = 50000, -- | |
angle = 180, -- | |
detect = 100, -- | |
poserror = 0, -- | |
maxaccuracy = 0 -- | |
} | |
} | |
} | |
--Tables that store counterfire radars | |
BlueCounterfireRadar = {} --Table that stores all blue counterfire radars | |
RedCounterfireRadar = {} --Table that stores all red counterfire radars | |
--Function that adds a counterfire radar to table | |
function AddCounterfireRadar(UnitName, t, lvl) --Valid entries for argument t: "Q36", "Q36-360", "Q37" , "ARK1" and "Omni" | |
local coal = Unit.getByName(UnitName):getCoalition() --Get coalition of unit | |
local UnitType = t or "Omni" --Default to "Omni" if no argument is provided | |
if coal == 1 then --If unit is red | |
RedCounterfireRadar[#RedCounterfireRadar + 1] = { | |
name = UnitName, | |
type = { --Get the specification for the indicated UnitType from the CounterfireRadarType and store them for each added radar | |
name = CounterfireRadarType[UnitType].name, | |
mortar = { | |
minrange = CounterfireRadarType[UnitType].mortar.minrange, | |
maxrange = CounterfireRadarType[UnitType].mortar.maxrange, | |
angle = CounterfireRadarType[UnitType].mortar.angle, | |
detect = CounterfireRadarType[UnitType].mortar.detect, | |
poserror = CounterfireRadarType[UnitType].mortar.poserror, | |
maxaccuracy = CounterfireRadarType[UnitType].mortar.maxaccuracy | |
}, | |
artillery = { | |
minrange = CounterfireRadarType[UnitType].artillery.minrange, | |
maxrange = CounterfireRadarType[UnitType].artillery.maxrange, | |
angle = CounterfireRadarType[UnitType].artillery.angle, | |
detect = CounterfireRadarType[UnitType].artillery.detect, | |
poserror = CounterfireRadarType[UnitType].artillery.poserror, | |
maxaccuracy = CounterfireRadarType[UnitType].artillery.maxaccuracy | |
}, | |
rocket = { | |
minrange = CounterfireRadarType[UnitType].rocket.minrange, | |
maxrange = CounterfireRadarType[UnitType].rocket.maxrange, | |
angle = CounterfireRadarType[UnitType].rocket.angle, | |
detect = CounterfireRadarType[UnitType].rocket.detect, | |
poserror = CounterfireRadarType[UnitType].rocket.poserror, | |
maxaccuracy = CounterfireRadarType[UnitType].rocket.maxaccuracy | |
} | |
}, | |
level = lvl or 11111, --Hierarchy level of radar. Format digit-digit-digit-digit-digit. Default to 11111 if no argument is provided | |
track = {} --Table to store the tracks of this radar | |
} | |
elseif coal == 2 then --If unit is blue | |
BlueCounterfireRadar[#BlueCounterfireRadar + 1] = { | |
name = UnitName, | |
type = { --Get the specification for the indicated UnitType from the CounterfireRadarType and store them for each added radar | |
name = CounterfireRadarType[UnitType].name, | |
mortar = { | |
minrange = CounterfireRadarType[UnitType].mortar.minrange, | |
maxrange = CounterfireRadarType[UnitType].mortar.maxrange, | |
angle = CounterfireRadarType[UnitType].mortar.angle, | |
detect = CounterfireRadarType[UnitType].mortar.detect, | |
poserror = CounterfireRadarType[UnitType].mortar.poserror, | |
maxaccuracy = CounterfireRadarType[UnitType].mortar.maxaccuracy | |
}, | |
artillery = { | |
minrange = CounterfireRadarType[UnitType].artillery.minrange, | |
maxrange = CounterfireRadarType[UnitType].artillery.maxrange, | |
angle = CounterfireRadarType[UnitType].artillery.angle, | |
detect = CounterfireRadarType[UnitType].artillery.detect, | |
poserror = CounterfireRadarType[UnitType].artillery.poserror, | |
maxaccuracy = CounterfireRadarType[UnitType].artillery.maxaccuracy | |
}, | |
rocket = { | |
minrange = CounterfireRadarType[UnitType].rocket.minrange, | |
maxrange = CounterfireRadarType[UnitType].rocket.maxrange, | |
angle = CounterfireRadarType[UnitType].rocket.angle, | |
detect = CounterfireRadarType[UnitType].rocket.detect, | |
poserror = CounterfireRadarType[UnitType].rocket.poserror, | |
maxaccuracy = CounterfireRadarType[UnitType].rocket.maxaccuracy | |
} | |
}, | |
level = lvl or 11111, --Hierarchy level of radar. Format digit-digit-digit-digit-digit. Default to 11111 if no argument is provided | |
track = {} --Table to store the tracks of this radar | |
} | |
end | |
end | |
--Function to remove a counterfire radar from table | |
function RemoveCounterfireRadar(UnitName) | |
local coal = Unit.getByName(UnitName):getCoalition() --Get coalition of unit | |
if coal == 1 then --If units is red | |
for n = 1, #RedCounterfireRadar do --Iterate through all units | |
if RedCounterfireRadar[n].name == UnitName then --Find n-th entry that contains unit we want to remove | |
table.remove(RedCounterfireRadar, n) --Remove unit | |
break --Quit for loop | |
end | |
end | |
elseif coal == 2 then --If unit is blue | |
for n = 1, #BlueCounterfireRadar do --Iterate through all unit | |
if BlueCounterfireRadar[n].name == UnitName then --Find n-th entry that contains unit we want to remove | |
table.remove(BlueCounterfireRadar, n) --Remove unit | |
break --Quit for loop | |
end | |
end | |
end | |
end | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Management of Counterfire Radar Detection | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Function that checks if a shot was detected by a counterfire radar. Returns true of false | |
local function CounterfireDetection(Vec2, coal, wpn, n) --Arguments: Vec2: position of shot, coal: side that shot, wpn: mortar/artillery/rocket, n: number of the counterfire radar | |
if coal == 1 then --Shooter is red | |
local CFR = Unit.getByName(BlueCounterfireRadar[n].name) --Get counterfire radar | |
local CFRP3 = CFR:getPosition() --Get the P3 position of the counterfire radar | |
local CFRVec2 = {x = CFRP3.p.x, y = CFRP3.p.z} --Get the Vec2 position of the counterfire radar | |
local distance = GetDistance(Vec2, CFRVec2) --Get distance between counterfire radar and shot | |
local maxrange = BlueCounterfireRadar[n].type[wpn].maxrange --Get maxium detection range of counterfire radar for wpn type | |
local minrange = BlueCounterfireRadar[n].type[wpn].minrange --Get minimum detection range of counterfire radar for wpn type | |
if (distance <= maxrange) and (distance >= minrange) then --Check if shot is between min and max range of counterfire radar | |
local heading = GetHeading(CFRP3) --Get heading of counterfire radar | |
local bearing = GetHeadingBetween(CFRVec2, Vec2) --Get bearing of shot from counterfire radar | |
local zonemax = heading + BlueCounterfireRadar[n].type[wpn].angle --Get maximum detection azimuth of counterfire radar | |
local zonemin = heading - BlueCounterfireRadar[n].type[wpn].angle --Get minimum detection azimuth of counterfire radar | |
if zonemin < 1 then --Detection sector overlaps north on left side | |
if (bearing <= zonemax) or (bearing >= (zonemin + 360)) then --Check if shot is in detection sector | |
if math.random(1, 100) <= BlueCounterfireRadar[n].type[wpn].detect then --Apply chance of detection with available radar type | |
return true --If random check is true, detection is true | |
else | |
return false --If random check is false, detection is false | |
end | |
else | |
return false --If shot is outside detection sector, detection is false | |
end | |
elseif zonemax > 360 then --Detection sector overlaps north on right side | |
if (bearing >= zonemin) or (bearing <= (zonemax - 360)) then --Check if shot is in detection sector | |
if math.random(1, 100) <= BlueCounterfireRadar[n].type[wpn].detect then --Apply chance of detection with available radar type | |
return true --If random check is true, detection is true | |
else | |
return false --If random check is false, detection is false | |
end | |
else | |
return false --If shot is outside detection sector, detection is false | |
end | |
else --Detection sector does not overlap north | |
if (bearing >= zonemin) and (bearing <= zonemax) then --Check if shot is in detection sector | |
if math.random(1, 100) <= BlueCounterfireRadar[n].type[wpn].detect then --Apply chance of detection with available radar type | |
return true --If random check is true, detection is true | |
else | |
return false --If random check is false, detection is false | |
end | |
else | |
return false --If shot is outside detection sector, detection is false | |
end | |
end | |
else | |
return false --If shot is oustide detection range, detectin is false | |
end | |
elseif coal == 2 then --Shooter is blue | |
local CFR = Unit.getByName(RedCounterfireRadar[n].name) --Get counterfire radar | |
local CFRP3 = CFR:getPosition() --Get the P3 position of the counterfire radar | |
local CFRVec2 = {x = CFRP3.p.x, y = CFRP3.p.z} --Get the Vec2 position of the counterfire radar | |
local distance = GetDistance(Vec2, CFRVec2) --Get distance between counterfire radar and shot | |
local maxrange = RedCounterfireRadar[n].type[wpn].maxrange --Get maxium detection range of counterfire radar for wpn type | |
local minrange = RedCounterfireRadar[n].type[wpn].minrange --Get minimum detection range of counterfire radar for wpn type | |
if (distance <= maxrange) and (distance >= minrange) then --Check if shot is between min and max range of counterfire radar | |
local heading = GetHeading(CFRP3) --Get heading of counterfire radar | |
local bearing = GetHeadingBetween(CFRVec2, Vec2) --Get bearing of shot from counterfire radar | |
local zonemax = heading + RedCounterfireRadar[n].type[wpn].angle --Get maximum detection azimuth of counterfire radar | |
local zonemin = heading - RedCounterfireRadar[n].type[wpn].angle --Get minimum detection azimuth of counterfire radar | |
if zonemin < 1 then --Detection sector overlaps north on left side | |
if (bearing <= zonemax) or (bearing >= (zonemin + 360)) then --Check if shot is in detection sector | |
if math.random(1, 100) <= RedCounterfireRadar[n].type[wpn].detect then --Apply chance of detection with available radar type | |
return true --If random check is true, detection is true | |
else | |
return false --If random check is false, detection is false | |
end | |
else | |
return false --If shot is outside detection sector, detection is false | |
end | |
elseif zonemax > 360 then --Detection sector overlaps north on right side | |
if (bearing >= zonemin) or (bearing <= (zonemax - 360)) then --Check if shot is in detection sector | |
if math.random(1, 100) <= RedCounterfireRadar[n].type[wpn].detect then --Apply chance of detection with available radar type | |
return true --If random check is true, detection is true | |
else | |
return false --If random check is false, detection is false | |
end | |
else | |
return false --If shot is outside detection sector, detection is false | |
end | |
else --Detection sector does not overlap north | |
if (bearing >= zonemin) and (bearing <= zonemax) then --Check if shot is in detection sector | |
if math.random(1, 100) <= RedCounterfireRadar[n].type[wpn].detect then --Apply chance of detection with available radar type | |
return true --If random check is true, detection is true | |
else | |
return false --If random check is false, detection is false | |
end | |
else | |
return false --If shot is outside detection sector, detection is false | |
end | |
end | |
else | |
return false --If shot is oustide detection range, detectin is false | |
end | |
end | |
end | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Management of Counterfire Tracks | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Building a track. A track is the accumulation of multiple target positions into one average coordinate. | |
local function BuildBlueCFTrack(Vec2, Rn) --Function that takes target points (Vec2) and puts them into existing or new tracks. | |
for n = 0, #BlueCounterfireRadar[Rn].track do | |
if (#BlueCounterfireRadar[Rn].track > 0) and (n > 0) and (BlueCounterfireRadar[Rn].track[n].active == true) and ((Vec2.x < (BlueCounterfireRadar[Rn].track[n].coord.x + 200)) and (Vec2.x > (BlueCounterfireRadar[Rn].track[n].coord.x - 200))) and ((Vec2.y < (BlueCounterfireRadar[Rn].track[n].coord.y + 200)) and (Vec2.y > (BlueCounterfireRadar[Rn].track[n].coord.y - 200))) then --Check if track table is not empty and if the new target point Vec2 is within 200m of an existing track. If yes, average the existing track position with the new target position. If no, create a new track. The check is actually not within a 200m radius circle of the track but a 400x400m square around the track. Implemented like this for simplicity. | |
BlueCounterfireRadar[Rn].track[n].coord.x = (BlueCounterfireRadar[Rn].track[n].coord.x + Vec2.x) / 2 --Average the existing track x coordinate with the suppled coordinate | |
BlueCounterfireRadar[Rn].track[n].coord.y = (BlueCounterfireRadar[Rn].track[n].coord.y + Vec2.y) / 2 --Average the existing track y coordinate with the suppled coordinate | |
BlueCounterfireRadar[Rn].track[n].time[#BlueCounterfireRadar[Rn].track[n].time + 1] = timer.getTime() --Add the time of the update | |
BlueCounterfireRadar[Rn].track[n].active = true --This track is active again | |
do break end --Update of track completed, quit for loop | |
elseif n == #BlueCounterfireRadar[Rn].track then --If the last track n has been reached and wasn't update, create a new track | |
BlueCounterfireRadar[Rn].track[n + 1] = { --Create a new track that ... | |
coord = {x = Vec2.x, y = Vec2.y}, --holds the coordinate of the shot | |
time = {timer.getTime()}, --and the time of shot | |
active = true, --This new track is an active track | |
firemission = false --This new track does currently not have an assigned firemission | |
} | |
do break end --New track added, quit for loop | |
end | |
end | |
end | |
local function BuildRedCFTrack(Vec2, Rn) --Function that takes target points (Vec2) and puts them into existing or new tracks. | |
for n = 0, #RedCounterfireRadar[Rn].track do | |
if (#RedCounterfireRadar[Rn].track > 0) and (n > 0) and (RedCounterfireRadar[Rn].track[n].active == true) and ((Vec2.x < (RedCounterfireRadar[Rn].track[n].coord.x + 200)) and (Vec2.x > (RedCounterfireRadar[Rn].track[n].coord.x - 200))) and ((Vec2.y < (RedCounterfireRadar[Rn].track[n].coord.y + 200)) and (Vec2.y > (RedCounterfireRadar[Rn].track[n].coord.y - 200))) then --Check if track table is not empty and if the new target point Vec2 is within 200m of an existing track. If yes, average the existing track position with the new target position. If no, create a new track. The check is actually not within a 200m radius circle of the track but a 400x400m square around the track. Implemented like this for simplicity. | |
RedCounterfireRadar[Rn].track[n].coord.x = (RedCounterfireRadar[Rn].track[n].coord.x + Vec2.x) / 2 --Average the existing track x coordinate with the suppled coordinate | |
RedCounterfireRadar[Rn].track[n].coord.y = (RedCounterfireRadar[Rn].track[n].coord.y + Vec2.y) / 2 --Average the existing track y coordinate with the suppled coordinate | |
RedCounterfireRadar[Rn].track[n].time[#RedCounterfireRadar[Rn].track[n].time + 1] = timer.getTime() --Add the time of the update | |
RedCounterfireRadar[Rn].track[n].active = true --This track is active again | |
do break end --Update of track completed, quit for loop | |
elseif n == #RedCounterfireRadar[Rn].track then --If the last track n has been reached and wasn't update, create a new track | |
RedCounterfireRadar[Rn].track[n + 1] = { --Create a new track that ... | |
coord = {x = Vec2.x, y = Vec2.y}, --holds the coordinate of the shot | |
time = {timer.getTime()}, --and the time of shot | |
active = true, --This new track is an active track | |
firemission = false --This new track does currently not have an assigned firemission | |
} | |
do break end --New track added, quit for loop | |
end | |
end | |
end | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Detection of shot events, counting shots of batteries, manage counterfire radar detection, add shots to CFtracks | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Eventhandler to get shot events | |
do | |
EventHandler = {} | |
function EventHandler:onEvent(event) | |
if event.id == world.event.S_EVENT_SHOT then --Check if event is a shot event | |
local Init = event.initiator --Get initiator unit of shot event | |
local Wep = event.weapon --Get weapon of the shot event | |
local InitWep = Wep:getTypeName() --Get type of the weapon | |
if (InitWep == "weapons.shells.2A60_120") then --Check if shooter is a mortar unit | |
InitClass = "mortar" --Assign to mortar class | |
elseif (InitWep == "weapons.shells.M185_155") or (InitWep == "weapons.shells.2A18_122") or (InitWep == "weapons.shells.2A33_152" ) or (InitWep == "weapons.shells.2A64_152") then --Check if shooter is an artillery unit | |
InitClass = "artillery" --Assign to artillery class | |
elseif (InitWep == "weapons.nurs.M26") or (InitWep == "weapons.nurs.GRAD_9M22U") or (InitWep == "weapons.nurs.SMERCH_9M55K") then --Check if shooter is a rocket unit | |
InitClass = "rocket" --Assign to rocket class | |
end | |
local InitCoal = Init:getCoalition() --Get coalition of shooter | |
if (InitClass == "mortar") or (InitClass == "artillery") or (InitClass == "rocket") then --Check if shooter is an indirect fire unit | |
-----section for the battery shotcounter----- | |
for n = 1, #BlueFiringBattery do --This for-loop serves to count the shots from blue batteries to end fire missions after a certain number of shots. | |
if Init:getGroup():getName() == BlueFiringBattery[n].name then --If the firing unit is part of a group that is a blue battery... | |
BlueFiringBattery[n].shotcounter = BlueFiringBattery[n].shotcounter + 1 --then increase the shotcounter of this battery by one. | |
end | |
end | |
for n = 1, #RedFiringBattery do --This for-loop serves to count the shots from red batteries to end fire missions after a certain number of shots. | |
if Init:getGroup():getName() == RedFiringBattery[n].name then --If the firing unit is part of a group that is a red battery... | |
RedFiringBattery[n].shotcounter = RedFiringBattery[n].shotcounter + 1 --then increase the shotcounter of this battery by one. | |
end | |
end | |
-----continue with counterfire detection----- | |
local InitPosP3 = Init:getPosition() --Get P3 position of shooter | |
local InitPosV2 = {x = InitPosP3.p.x, y = InitPosP3.p.z} --Get Vec2 position of shooter | |
if InitCoal == 1 then --If shooter is red... | |
for n = 1, #BlueCounterfireRadar do --Iterate through all blue counterfire radars | |
if CounterfireDetection(InitPosV2, InitCoal, InitClass, n) then --CounterfireDetection() checks if the shot was detection by a counterfire radar. Returns true or false | |
--Here follows the section that applies the accuracy error on the shot detection | |
local CFR = Unit.getByName(BlueCounterfireRadar[n].name) --Get counterfire radar | |
local CFRP3 = CFR:getPosition() --Get the P3 position of the counterfire radar | |
local CFRVec2 = {x = CFRP3.p.x, y = CFRP3.p.z} --Get the Vec2 position of the counterfire radar | |
local distance = GetDistance(InitPosV2, CFRVec2) --Get the distance between the radar and the shot | |
local accuracy = distance / 100 * BlueCounterfireRadar[n].type[InitClass].poserror --Determine the accuracy of the detection for application of the position error. | |
if accuracy > BlueCounterfireRadar[n].type[InitClass].maxaccuracy then --If the accuracy is worse than maxaccuracy, use accuracy. Target coordinates should be somehwere within a circle radius 'accuracy' of the true target position. For simplicity we apply a square position error (x and y axis). | |
local heading = math.random(1, 360) --Get offset heading | |
local offset = math.random(0, accuracy) --Get offset distance | |
InitPosV2.x = InitPosV2.x + math.cos(heading) * offset --Apply position error to x axis | |
InitPosV2.y = InitPosV2.y + math.sin(heading) * offset --Apply position error to y axis | |
else --If the accuracy is better than maxaccuracy, use maxaccuracy. Target coordinates should be somehwere within a circle radius maxacuracy of the true target position. For simplicity we apply a square position error (x and y axis). | |
local heading = math.random(1, 360) --Get offset heading | |
local offset = math.random(0, BlueCounterfireRadar[n].type[InitClass].maxaccuracy) --Get offset distance | |
InitPosV2.x = InitPosV2.x + math.cos(heading) * offset --Apply position error to x axis | |
InitPosV2.y = InitPosV2.y + math.sin(heading) * offset --Apply position error to y axis | |
end | |
BuildBlueCFTrack(InitPosV2, n) --Execute function to build or update track | |
end | |
end | |
elseif InitCoal == 2 then --If shooter is blue... | |
for n = 1, #RedCounterfireRadar do --Iterate through all red counterfire radars | |
if CounterfireDetection(InitPosV2, InitCoal, InitClass, n) then --CounterfireDetection() checks if the shot was detection by a counterfire radar. Returns true or false | |
--Here follows the section that applies the accuracy error on the shot detection | |
local CFR = Unit.getByName(RedCounterfireRadar[n].name) --Get counterfire radar | |
local CFRP3 = CFR:getPosition() --Get the P3 position of the counterfire radar | |
local CFRVec2 = {x = CFRP3.p.x, y = CFRP3.p.z} --Get the Vec2 position of the counterfire radar | |
local distance = GetDistance(InitPosV2, CFRVec2) --Get the distance between the radar and the shot | |
local accuracy = distance / 100 * RedCounterfireRadar[n].type[InitClass].poserror --Determine the accuracy of the detection for application of the position error. | |
if accuracy > RedCounterfireRadar[n].type[InitClass].maxaccuracy then --If the accuracy is worse than maxaccuracy, use accuracy. Target coordinates should be somehwere within a circle radius 'accuracy' of the true target position. For simplicity we apply a square position error (x and y axis). | |
local heading = math.random(1, 360) --Get offset heading | |
local offset = math.random(0, accuracy) --Get offset distance | |
InitPosV2.x = InitPosV2.x + math.cos(heading) * offset --Apply position error to x axis | |
InitPosV2.y = InitPosV2.y + math.sin(heading) * offset --Apply position error to y axis | |
else --If the accuracy is better than maxaccuracy, use maxaccuracy. Target coordinates should be somehwere within a circle radius maxacuracy of the true target position. For simplicity we apply a square position error (x and y axis). | |
local heading = math.random(1, 360) --Get offset heading | |
local offset = math.random(0, RedCounterfireRadar[n].type[InitClass].maxaccuracy) --Get offset distance | |
InitPosV2.x = InitPosV2.x + math.cos(heading) * offset --Apply position error to x axis | |
InitPosV2.y = InitPosV2.y + math.sin(heading) * offset --Apply position error to y axis | |
end | |
BuildRedCFTrack(InitPosV2, n) --Execute function to build or update track | |
end | |
end | |
end | |
end | |
-----section to check if a battery is hit by artillery fire----- | |
elseif event.id == world.event.S_EVENT_HIT then | |
local Wep = event.weapon --Get weapon of the hit event | |
local InitWep = Wep:getTypeName() --Get type of the weapon | |
local Target = event.target --Get target of the hit event | |
local TargetCoal = Target:getCoalition() --Get coalition of target | |
local TargetGroupName = Target:getGroup():getName() --Get target group name | |
if (InitWep == "weapons.shells.2A60_120") or (InitWep == "weapons.shells.M185_155") or (InitWep == "weapons.shells.2A18_122") or (InitWep == "weapons.shells.2A33_152" ) or (InitWep == "weapons.shells.2A64_152") or (InitWep == "weapons.nurs.M26") or (InitWep == "weapons.nurs.GRAD_9M22U") or (InitWep == "weapons.nurs.SMERCH_9M55K") then --Check if weapon type is from artillery | |
if TargetCoal == 2 then --If target coalition is blue | |
for n = 1, #BlueFiringBattery do --Iterate through all blue batteries | |
if TargetGroupName == BlueFiringBattery[n].name then --Find battery that is hit | |
if BlueFiringBattery[n].displace == 2 or BlueFiringBattery[n].displace == 3 then --Check if battery should displace under fire | |
if BlueFiringBattery[n].status ~= "displacing" then --Check that the battery is not already displacing | |
BlueDisplacement(n, "under fire") --Perform displacement | |
end | |
end | |
end | |
end | |
elseif TargetCoal == 1 then --If target coalition is red | |
for n = 1, #RedFiringBattery do --Iterate through all red batteries | |
if TargetGroupName == RedFiringBattery[n].name then --Find battery that is hit | |
if RedFiringBattery[n].displace == 2 or RedFiringBattery[n].displace == 3 then --Check if battery should displace under fire | |
if RedFiringBattery[n].status ~= "displacing" then --Check that the battery is not already displacing | |
RedDisplacement(n, "under fire") --Perform displacement | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
world.addEventHandler(EventHandler) | |
end | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Management of Fire Missions | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Definition of the fire mission queues | |
local BlueFireMission = {} --Table that holds blue fire missions | |
local RedFireMission = {} --Table that holds red fire missions | |
--Function to add a red fire mission to the queue | |
local function AddBlueFireMission(Vec2, r, m, p, lvl, n) | |
BlueFireMission[#BlueFireMission + 1] = { | |
coord = Vec2, --Coordinates of the firemission in Vec2 | |
radius = r, --Radius of the fire mission in m | |
mission = m, --Mission type, valid entries: "DS" = Direct Support, "CF" = Counterfire | |
priority = p, --Priority of the fire mission. | |
level = lvl, --Hierarchy level of the fire mission | |
track = n, --Track this firemission was created from, string | |
time = timer.getTime() --Set time of creation the firemission | |
} | |
end | |
--Function to add a red fire mission to the queue | |
local function AddRedFireMission(Vec2, r, m, p, lvl, n) | |
RedFireMission[#RedFireMission + 1] = { | |
coord = Vec2, --Coordinates of the firemission in Vec2 | |
radius = r, --Radius of the fire mission in m | |
mission = m, --Mission type, valid entries: "DS" = Direct Support, "CF" = Counterfire | |
priority = p, --Priority of the fire mission | |
level = lvl, --Hierarchy level of the fire mission | |
track = n, --Track this firemission was created from, string | |
time = timer.getTime() --Set time of creation the firemission | |
} | |
end | |
--Creating fire missions from tracks | |
function CreateFireMission() --Function to create fire missions from tracks. To be repeated every 10 seconds. | |
local currentTime = timer.getTime() | |
-----blue counterfire tracks----- | |
for m = 1, #BlueCounterfireRadar do --Iterate through all blue counterfire radars | |
for n = 1, #BlueCounterfireRadar[m].track do --Iterate through all tracks of these radars | |
if BlueCounterfireRadar[m].track[n].active == true then --Check if the track is still active | |
if currentTime > (BlueCounterfireRadar[m].track[n].time[#BlueCounterfireRadar[m].track[n].time] + 30) then --Check if the track has been updated in the last 30 seconds. | |
BlueCounterfireRadar[m].track[n].active = false --Deactivate the track | |
else | |
if BlueCounterfireRadar[m].track[n].firemission == false then --Check if track has already generated an active firemission | |
local p = 0 --Counter p is the amount of updates of the track (fired enemy shots) in the last 30 seconds. The higher p, the higher the priority the fire mission receives. | |
for i = #BlueCounterfireRadar[m].track[n].time, 1, -1 do --Iterate through all updates of the track, from the lates to the oldest. | |
if BlueCounterfireRadar[m].track[n].time[i] > (currentTime - 10) then --Check if the update of the track happened within the past 10 seconds. | |
p = p + 1 --The update happened in the past 10 second, increase counter p by 1. | |
else | |
break --The update is older than 30 seconds, quit the iteration of the rest of the updates which are even older | |
end | |
end | |
local priority = 0 | |
if p < 3 then --If less than 3 shots in last 10 seconds, assign priority 10 (equals 1 arty piece) | |
priority = 10 | |
elseif p >= 3 and p < 12 then --If between 3 and 12 shots in last 10 seconds, assign priority 30 (equals 3 arty pieces) | |
priority = 30 | |
elseif p >= 12 then | |
priority = 60 --If more than 12 shots in last 10 seconds, assign priority 60 (equals 6 arty pieces) | |
end | |
AddBlueFireMission(BlueCounterfireRadar[m].track[n].coord, 100, "CF", priority, BlueCounterfireRadar[m].level, "BlueCounterfireRadar[" .. m .. "].track[" .. n .. "]") --Add a counterfire firemission to queue. | |
BlueCounterfireRadar[m].track[n].firemission = true --Track has created an active firemission and will therefore not create any more firemissions | |
end | |
end | |
end | |
end | |
end | |
-----red counterfire tracks----- | |
for m = 1, #RedCounterfireRadar do --Iterate through all red counterfire radars | |
for n = 1, #RedCounterfireRadar[m].track do --Iterate through all tracks of these radars | |
if RedCounterfireRadar[m].track[n].active == true then --Check if the track is still active | |
if currentTime > (RedCounterfireRadar[m].track[n].time[#RedCounterfireRadar[m].track[n].time] + 30) then --Check if the track has been updated in the last 30 seconds. | |
RedCounterfireRadar[m].track[n].active = false --Deactivate the track | |
else | |
if RedCounterfireRadar[m].track[n].firemission == false then --Check if track has already generated an active firemission | |
local p = 0 --Counter p is the amount of updates of the track (fired enemy shots) in the last 30 seconds. The higher p, the higher the priority the fire mission receives. | |
for i = #RedCounterfireRadar[m].track[n].time, 1, -1 do --Iterate through all updates of the track, from the lates to the oldest. | |
if RedCounterfireRadar[m].track[n].time[i] > (currentTime - 10) then --Check if the update of the track happened within the past 30 seconds. | |
p = p + 1 --The update happened in the past 10 second, increase counter p by 1. | |
else | |
break --The update is older than 10 seconds, quit the iteration of the rest of the updates which are even older | |
end | |
end | |
local priority = 0 | |
if p < 3 then --If less than 3 shots in last 10 seconds, assign priority 10 (equals 1 arty piece) | |
priority = 10 | |
elseif p >= 3 and p < 12 then --If between 3 and 12 shots in last 10 seconds, assign priority 30 (equals 3 arty pieces) | |
priority = 30 | |
elseif p >= 12 then | |
priority = 60 --If more than 12 shots in last 3 seconds, assign priority 60 (equals 6 arty pieces) | |
end | |
AddRedFireMission(RedCounterfireRadar[m].track[n].coord, 100, "CF", priority, RedCounterfireRadar[m].level, "RedCounterfireRadar[" .. m .. "].track[" .. n .. "]") --Add a counterfire firemission to queue. | |
RedCounterfireRadar[m].track[n].firemission = true --Track has created an active firemission and will therefore not create any more firemissions | |
end | |
end | |
end | |
end | |
end | |
-----blue spotter tracks----- | |
for m = 1, #BlueSpotter do --Iterate through all blue spotters | |
for n = 1, #BlueSpotter[m].track do --Iterate through all blue spotter tracks | |
if BlueSpotter[m].track[n].active == true then --Check if track is active | |
if currentTime > BlueSpotter[m].track[n].time + 30 then --Check if track is more than 30 seconds old | |
BlueSpotter[m].track[n].active = false --Deactivate track | |
else | |
if BlueSpotter[m].track[n].firemission == false then --Check if track has already generated a firemission | |
AddBlueFireMission(BlueSpotter[m].track[n].coord, 100, "DS", BlueSpotter[m].track[n].value, BlueSpotter[m].level, "BlueSpotter[" .. m .. "].track[" .. n .. "]") --Add firemission to queue | |
BlueSpotter[m].track[n].firemission = true --A firemission is now assigned for this track | |
end | |
end | |
end | |
end | |
end | |
-----red spotter tracks----- | |
for m = 1, #RedSpotter do --Iterate through all red spotters | |
for n = 1, #RedSpotter[m].track do --Iterate through all red spotter tracks | |
if RedSpotter[m].track[n].active == true then --Check if track is active | |
if currentTime > RedSpotter[m].track[n].time + 30 then --Check if track is more than 30 seconds old | |
RedSpotter[m].track[n].active = false --Deactivate track | |
else | |
if RedSpotter[m].track[n].firemission == false then --Check if track has already generated a firemission | |
AddRedFireMission(RedSpotter[m].track[n].coord, 100, "DS", RedSpotter[m].track[n].value, RedSpotter[m].level, "RedSpotter[" .. m .. "].track[" .. n .. "]") --Add firemission to queue | |
RedSpotter[m].track[n].firemission = true --A firemission is now assigned for this track | |
end | |
end | |
end | |
end | |
end | |
return timer.getTime() + 10 --Repeat function every 10 senconds | |
end | |
timer.scheduleFunction(CreateFireMission, arg, 10) --Execute the reccuring CreateFireMission function for the first time after 10 seconds | |
--Function to execute a blue fire mission | |
function ShootBlueFireMission(GroupName, Vec2, r, Bn, Tn) --Arguments: GroupName: Name of firing group as string; Vec2 position of target; r: radius of fire mission in meter; Bn: number n of the FiringBattery; Tn: string, name of the track this firemission was build from) | |
FireAtPointControlledTask = { --Defining a controlled task, which includes a stop condition for the task. | |
id = 'ControlledTask', | |
params = { | |
task = { --Defining the task | |
id = 'FireAtPoint', | |
params = { | |
point = Vec2, | |
radius = r | |
} | |
}, | |
stopCondition = { --Primary stop conditio: when the battery has fired the amount of shots as defined by FM_rounds. | |
condition = "if BlueFiringBattery[" .. Bn .. "].shotcounter == BlueFiringBattery[" .. Bn .. "].FM_rounds then" .. [[ | |
]] .. "BlueDisplacement(" .. Bn .. ")" .. [[ | |
]] .. "local DelayTrackFMfalse = function() " .. Tn .. ".firemission = false end" .. [[ | |
]] .. "timer.scheduleFunction(DelayTrackFMfalse, nil, timer.getTime() + 60)" .. [[ | |
]] .. "return true end", --After mission is complete, return status of battery back to ready and remove the firemission flag from the respective track | |
duration = 360 --Alternate stop condition. Stop fire mission after 6 minutes. | |
} | |
} | |
} | |
Group.getByName(GroupName):getController():pushTask(FireAtPointControlledTask) | |
end | |
--Function to execute a red fire mission | |
function ShootRedFireMission(GroupName, Vec2, r, Bn, Tn) --Arguments: GroupName: Name of firing group as string; Vec2 position of target; r: radius of fire mission in meter; Bn: number n of the FiringBattery; Tn: string, name of the track this firemission was build from | |
FireAtPointControlledTask = { --Defining a controlled task, which includes a stop condition for the task. | |
id = 'ControlledTask', | |
params = { | |
task = { --Defining the task | |
id = 'FireAtPoint', | |
params = { | |
point = Vec2, | |
radius = r | |
} | |
}, | |
stopCondition = { --Primary stop conditio: when the battery has fired the amount of shots as defined by FM_rounds. | |
condition = "if RedFiringBattery[" .. Bn .. "].shotcounter == RedFiringBattery[" .. Bn .. "].FM_rounds then" .. [[ | |
]] .. "RedDisplacement(" .. Bn .. ")" .. [[ | |
]] .. "local DelayTrackFMfalse = function() " .. Tn .. ".firemission = false end" .. [[ | |
]] .. "timer.scheduleFunction(DelayTrackFMfalse, nil, timer.getTime() + 60)" .. [[ | |
]] .. "return true end", --After mission is complete, return status of battery back to ready and remove the firemission flag from the respective track | |
duration = 360 --Alternate stop condition. Stop fire mission after 6 minutes. | |
} | |
} | |
} | |
Group.getByName(GroupName):getController():pushTask(FireAtPointControlledTask) | |
end | |
--Function to execute a blue nuclear fire mission | |
function ShootBlueNukeFireMission(GroupName, Vec2, r, Bn, Tn) --Arguments: GroupName: Name of firing group as string; Vec2 position of target; r: radius of fire mission in meter; Bn: number n of the FiringBattery; Tn: string, name of the track this firemission was build from) | |
FireAtPointControlledTask = { --Defining a controlled task, which includes a stop condition for the task. | |
id = 'ControlledTask', | |
params = { | |
task = { --Defining the task | |
id = 'FireAtPoint', | |
params = { | |
point = Vec2, | |
radius = r | |
} | |
}, | |
stopCondition = { --Primary stop conditio: when the battery has fired the amount of shots as defined by FM_rounds. | |
condition = "if BlueFiringBattery[" .. Bn .. "].shotcounter == 1 then" .. [[ | |
]] .. "BlueDisplacement(" .. Bn .. ")" .. [[ | |
]] .. "BlueFiringBattery[" .. Bn .. "].nukes = BlueFiringBattery[" .. Bn .. "].nukes - 1" .. [[ | |
]] .. "local DelayTrackFMfalse = function() " .. Tn .. ".firemission = false end" .. [[ | |
]] .. "timer.scheduleFunction(DelayTrackFMfalse, nil, timer.getTime() + 60)" .. [[ | |
]] .. "return true end", --After mission is complete, return status of battery back to ready and remove the firemission flag from the respective track | |
duration = 360 --Alternate stop condition. Stop fire mission after 6 minutes. | |
} | |
} | |
} | |
Group.getByName(GroupName):getController():pushTask(FireAtPointControlledTask) | |
end | |
--Function to execute a red nuclear fire mission | |
function ShootRedNukeFireMission(GroupName, Vec2, r, Bn, Tn) --Arguments: GroupName: Name of firing group as string; Vec2 position of target; r: radius of fire mission in meter; Bn: number n of the FiringBattery; Tn: string, name of the track this firemission was build from | |
FireAtPointControlledTask = { --Defining a controlled task, which includes a stop condition for the task. | |
id = 'ControlledTask', | |
params = { | |
task = { --Defining the task | |
id = 'FireAtPoint', | |
params = { | |
point = Vec2, | |
radius = r | |
} | |
}, | |
stopCondition = { --Primary stop conditio: when the battery has fired the amount of shots as defined by FM_rounds. | |
condition = "if RedFiringBattery[" .. Bn .. "].shotcounter == 1 then" .. [[ | |
]] .. "RedDisplacement(" .. Bn .. ")" .. [[ | |
]] .. "RedFiringBattery[" .. Bn .. "].nukes = RedFiringBattery[" .. Bn .. "].nukes - 1" .. [[ | |
]] .. "local DelayTrackFMfalse = function() " .. Tn .. ".firemission = false end" .. [[ | |
]] .. "timer.scheduleFunction(DelayTrackFMfalse, nil, timer.getTime() + 60)" .. [[ | |
]] .. "return true end", --After mission is complete, return status of battery back to ready and remove the firemission flag from the respective track | |
duration = 360 --Alternate stop condition. Stop fire mission after 6 minutes. | |
} | |
} | |
} | |
Group.getByName(GroupName):getController():pushTask(FireAtPointControlledTask) | |
end | |
--Function that checks if a battery has enough ammo to execute a fire mission | |
function CheckAmmo(grp) --Input group | |
local groupName = grp:getName() --Get name of group | |
local groupType = getGroupType(groupName) --Get type of group (custom function) | |
local units = grp:getUnits() --Get units of group | |
local minAmmo = ArtilleryProperties[groupType].minAmmo --minAmmo is the number of rounds left after rapid fire | |
local BatteryAmmo = 0 | |
for n = 1, #units do --Iterate through all units of the battery | |
if units[n]:getTypeName() == groupType then --Check if a unit is of the same type as the battery | |
local ammo = units[n]:getAmmo() --Get ammo for this unit | |
if ammo[1] then --Check if ammo[1] exists. If the ammo is used up, it returns nil... | |
local UnitAmmo = ammo[1].count --Get the shell count for this unit | |
if UnitAmmo > minAmmo then --Check if there is ready ammo left | |
local UnitReadyAmmo = UnitAmmo - minAmmo --Calculate amount of ready ammo | |
BatteryAmmo = BatteryAmmo + UnitReadyAmmo --Add unit ready ammo to total of group | |
end | |
end | |
end | |
end | |
if BatteryAmmo >= ArtilleryProperties[groupType].FM_rounds then --Check if the amount of ready ammo of the battery is equal or bigger then the number of rounds for a fire mission | |
return true --Return true if yes | |
else | |
return false | |
end | |
end | |
--Assigning fire missions to batteries and executing the task | |
function AssignFireMission() --Function to assign fire missions to available batteries. To be repeated every 10 seconds. | |
-----------------------------This section sorts the blue firemissions by priority, from highest to lowest | |
if #BlueFireMission >= 2 then --Sorting is only required for 2 or more firemissions in queue | |
local T = {} | |
T[1] = BlueFireMission[1] | |
for n = 2, #BlueFireMission do | |
for i = 1, #T do | |
if BlueFireMission[n].priority >= T[i].priority then | |
table.insert(T, i, BlueFireMission[n]) | |
do break end | |
elseif i == #T then | |
table.insert(T, i + 1, BlueFireMission[n]) | |
break | |
end | |
end | |
end | |
BlueFireMission = T | |
end | |
----------------------------This section tries to assign a blue fire mission to a blue battery | |
if #BlueFireMission > 0 then --If a blue fire mission exists, try to assign it to a battery | |
local n = 1 | |
repeat --Iterate through all blue fire missions. We use a repeat loop instead of for since the content of the loop adjusts both the counter and the end condition. | |
local loop = true --Local variable that controls which Hierarchy levels should be checked. If a FM is assigned, the variable is set false so that higher Hierarchy levels are not checked. | |
--------------------Try to assign FM to a battery of Hierarchy level 1 | |
for i = 1, #BlueFiringBattery do | |
if BlueFireMission[n].level == BlueFiringBattery[i].level then --Check if FM matches Hierarchy level of battery | |
if BlueFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (BlueFiringBattery[i].mission == "All") or (BlueFiringBattery[i].mission == BlueFireMission[n].mission) then --Check if battery can accept the mission type | |
if BlueFireMission[n].priority >= BlueFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(BlueFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(BlueFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= BlueFiringBattery[i].minrange) and (dist <= BlueFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if BlueFiringBattery[i].nukes > 0 and BlueFireMission[n].priority >= BlueFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueNukeFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, 0, i, BlueFireMission[n].track) --Execute a nuclear fire mission | |
NuclearShell(BlueFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end --Quit battery loop for this Hierarchy. | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, BlueFireMission[n].radius, i, BlueFireMission[n].track) --Execute fire mission | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Try to assign FM to a battery of hierarchy level 2 | |
if loop == true then --Check if attempt should be made to assign FM on this level | |
for i = 1, #BlueFiringBattery do --Iterate through all batteries of this hierarchy level | |
if math.floor(BlueFireMission[n].level / 10) * 10 == BlueFiringBattery[i].level then --Check if FM matches the hierarchy level of battery | |
if BlueFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (BlueFiringBattery[i].mission == "All") or (BlueFiringBattery[i].mission == BlueFireMission[n].mission) then --Check if battery can accept the mission type | |
if BlueFireMission[n].priority >= BlueFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(BlueFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(BlueFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= BlueFiringBattery[i].minrange) and (dist <= BlueFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if BlueFiringBattery[i].nukes > 0 and BlueFireMission[n].priority >= BlueFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueNukeFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, 0, i, BlueFireMission[n].track) --Execute a nuclear fire mission | |
NuclearShell(BlueFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, BlueFireMission[n].radius, i, BlueFireMission[n].track) --Execute fire mission | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Try to assign FM to a battery of hierarchy level 3 | |
if loop == true then --Check if attempt should be made to assign FM on this level | |
for i = 1, #BlueFiringBattery do --Iterate through all batteries of this hierarchy level | |
if math.floor(BlueFireMission[n].level / 100) * 100 == BlueFiringBattery[i].level then --Check if FM matches the hierarchy level of battery | |
if BlueFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (BlueFiringBattery[i].mission == "All") or (BlueFiringBattery[i].mission == BlueFireMission[n].mission) then --Check if battery can accept the mission type | |
if BlueFireMission[n].priority >= BlueFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(BlueFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(BlueFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= BlueFiringBattery[i].minrange) and (dist <= BlueFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if BlueFiringBattery[i].nukes > 0 and BlueFireMission[n].priority >= BlueFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueNukeFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, 0, i, BlueFireMission[n].track) --Execute a nuclear fire mission | |
NuclearShell(BlueFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, BlueFireMission[n].radius, i, BlueFireMission[n].track) --Execute fire mission | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Try to assign FM to a battery of hierarchy level 4 | |
if loop == true then --Check if attempt should be made to assign FM on this level | |
for i = 1, #BlueFiringBattery do --Iterate through all batteries of this hierarchy level | |
if math.floor(BlueFireMission[n].level / 1000) * 1000 == BlueFiringBattery[i].level then --Check if FM matches the hierarchy level of battery | |
if BlueFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (BlueFiringBattery[i].mission == "All") or (BlueFiringBattery[i].mission == BlueFireMission[n].mission) then --Check if battery can accept the mission type | |
if BlueFireMission[n].priority >= BlueFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(BlueFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(BlueFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= BlueFiringBattery[i].minrange) and (dist <= BlueFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if BlueFiringBattery[i].nukes > 0 and BlueFireMission[n].priority >= BlueFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueNukeFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, 0, i, BlueFireMission[n].track) --Execute a nuclear fire mission | |
NuclearShell(BlueFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, BlueFireMission[n].radius, i, BlueFireMission[n].track) --Execute fire mission | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Try to assign FM to a battery of hierarchy level 5 | |
if loop == true then --Check if attempt should be made to assign FM on this level | |
for i = 1, #BlueFiringBattery do --Iterate through all batteries of this hierarchy level | |
if math.floor(BlueFireMission[n].level / 10000) * 10000 == BlueFiringBattery[i].level then --Check if FM matches the hierarchy level of battery | |
if BlueFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (BlueFiringBattery[i].mission == "All") or (BlueFiringBattery[i].mission == BlueFireMission[n].mission) then --Check if battery can accept the mission type | |
if BlueFireMission[n].priority >= BlueFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(BlueFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(BlueFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= BlueFiringBattery[i].minrange) and (dist <= BlueFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if BlueFiringBattery[i].nukes > 0 and BlueFireMission[n].priority >= BlueFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueNukeFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, 0, i, BlueFireMission[n].track) --Execute a nuclear fire mission | |
NuclearShell(BlueFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
BlueFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootBlueFireMission(BlueFiringBattery[i].name, BlueFireMission[n].coord, BlueFireMission[n].radius, i, BlueFireMission[n].track) --Execute fire mission | |
BlueFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Remove outdated FM | |
if loop == true then | |
if BlueFireMission[n].time < (timer.getTime() - 600) then --Check if creation time fo firemission is older than 10 minutes | |
local x = BlueFireMission[n].track .. ".firemission = false" --Create a string of the location of the original track as string + ".firemission = false" which holds the booleadn that this track has no offspring firemission | |
loadstring(x)() --Eexecute the avove string as LUA code to let the track generate a firemission again | |
table.remove(BlueFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
end | |
end | |
n = n + 1 | |
until n > #BlueFireMission | |
end | |
----------------------------This section sorts the red firemissions by priority, from highest to lowest | |
if #RedFireMission >= 2 then --Sorting is only required for 2 or more firemissions in queue | |
local T = {} | |
T[1] = RedFireMission[1] | |
for n = 2, #RedFireMission do | |
for i = 1, #T do | |
if RedFireMission[n].priority >= T[i].priority then | |
table.insert(T, i, RedFireMission[n]) | |
do break end | |
elseif i == #T then | |
table.insert(T, i + 1, RedFireMission[n]) | |
break | |
end | |
end | |
end | |
RedFireMission = T | |
end | |
---------------------------This section tries to assign a red fire mission to a red battery | |
if #RedFireMission > 0 then --If a red fire mission exists, try to assign it to a battery | |
local n = 1 | |
repeat --Iterate through all Red fire missions. We use a repeat loop instead of for since the content of the loop adjusts both the counter and the end condition. | |
local loop = true --Local variable that controls which hierarchy levels should be checked. If a FM is assigned, the variable is set false so that higher hierarchy levels are not checked. | |
--------------------Try to assign FM to a battery of hierarchy level 1 | |
for i = 1, #RedFiringBattery do | |
if RedFireMission[n].level == RedFiringBattery[i].level then --Check if FM matches hierarchy level of battery | |
if RedFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (RedFiringBattery[i].mission == "All") or (RedFiringBattery[i].mission == RedFireMission[n].mission) then --Check if battery can accept the mission type | |
if RedFireMission[n].priority >= RedFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(RedFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(RedFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= RedFiringBattery[i].minrange) and (dist <= RedFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if RedFiringBattery[i].nukes > 0 and RedFireMission[n].priority >= RedFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootRedNukeFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, 0, i, RedFireMission[n].track) --Execute a nuclear fire mission | |
NuclearShell(RedFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootRedFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, RedFireMission[n].radius, i, RedFireMission[n].track) --Execute fire mission | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Try to assign FM to a battery of hierarchy level 2 | |
if loop == true then --Check if attempt should be made to assign FM on this level | |
for i = 1, #RedFiringBattery do --Iterate through all batteries of this hierarchy level | |
if math.floor(RedFireMission[n].level / 10) * 10 == RedFiringBattery[i].level then --Check if FM matches the hierarchy level of battery | |
if RedFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (RedFiringBattery[i].mission == "All") or (RedFiringBattery[i].mission == RedFireMission[n].mission) then --Check if battery can accept the mission type | |
if RedFireMission[n].priority >= RedFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(RedFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(RedFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= RedFiringBattery[i].minrange) and (dist <= RedFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if RedFiringBattery[i].nukes > 0 and RedFireMission[n].priority >= RedFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootRedNukeFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, 0, i, RedFireMission[n].track) --Execute a nuclear fire mission | |
NuclearShell(RedFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootRedFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, RedFireMission[n].radius, i, RedFireMission[n].track) --Execute fire mission | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Try to assign FM to a battery of hierarchy level 3 | |
if loop == true then --Check if attempt should be made to assign FM on this level | |
for i = 1, #RedFiringBattery do --Iterate through all batteries of this hierarchy level | |
if math.floor(RedFireMission[n].level / 100) * 100 == RedFiringBattery[i].level then --Check if FM matches the hierarchy level of battery | |
if RedFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (RedFiringBattery[i].mission == "All") or (RedFiringBattery[i].mission == RedFireMission[n].mission) then --Check if battery can accept the mission type | |
if RedFireMission[n].priority >= RedFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(RedFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(RedFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= RedFiringBattery[i].minrange) and (dist <= RedFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if RedFiringBattery[i].nukes > 0 and RedFireMission[n].priority >= RedFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootRedNukeFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, 0, i, RedFireMission[n].track) --Execute a nuclear fire mission | |
NuclearShell(RedFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootRedFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, RedFireMission[n].radius, i, RedFireMission[n].track) --Execute fire mission | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Try to assign FM to a battery of hierarchy level 4 | |
if loop == true then --Check if attempt should be made to assign FM on this level | |
for i = 1, #RedFiringBattery do --Iterate through all batteries of this hierarchy level | |
if math.floor(RedFireMission[n].level / 1000) * 1000 == RedFiringBattery[i].level then --Check if FM matches the hierarchy level of battery | |
if RedFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (RedFiringBattery[i].mission == "All") or (RedFiringBattery[i].mission == RedFireMission[n].mission) then --Check if battery can accept the mission type | |
if RedFireMission[n].priority >= RedFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(RedFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(RedFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= RedFiringBattery[i].minrange) and (dist <= RedFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if RedFiringBattery[i].nukes > 0 and RedFireMission[n].priority >= RedFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootRedNukeFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, 0, i, RedFireMission[n].track) --Execute a nuclear fire mission | |
NuclearShell(RedFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootRedFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, RedFireMission[n].radius, i, RedFireMission[n].track) --Execute fire mission | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Try to assign FM to a battery of hierarchy level 5 | |
if loop == true then --Check if attempt should be made to assign FM on this level | |
for i = 1, #RedFiringBattery do --Iterate through all batteries of this hierarchy level | |
if math.floor(RedFireMission[n].level / 10000) * 10000 == RedFiringBattery[i].level then --Check if FM matches the hierarchy level of battery | |
if RedFiringBattery[i].status == "ready" then --Check if battery is ready to fire | |
if (RedFiringBattery[i].mission == "All") or (RedFiringBattery[i].mission == RedFireMission[n].mission) then --Check if battery can accept the mission type | |
if RedFireMission[n].priority >= RedFiringBattery[i].priority then --Check if priority of fire mission is equal or bigger than the minimum priority of the battery | |
local Battery = Group.getByName(RedFiringBattery[i].name) --Get the battery | |
local BatteryUnit1 = Battery:getUnit(1) --Get the first unit of the battery | |
local BatteryPos = BatteryUnit1:getPosition() --Get Vec3 position of battery | |
local dist = GetDistance(RedFireMission[n].coord, {x = BatteryPos.p.x, y = BatteryPos.p.z}) --Get distance between battery and target | |
if (dist >= RedFiringBattery[i].minrange) and (dist <= RedFiringBattery[i].maxrange) then --Check if battery is within range of target | |
if RedFiringBattery[i].nukes > 0 and RedFireMission[n].priority >= RedFiringBattery[i].nukepriority then --Check if battery is eligible for a nuclear strike. Amount of nuclear shells > 0 and priority of firemission >= minimum priority of battery for nuclear attack | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
NuclearShell(RedFiringBattery[i].name) --Function to track the shot of a nuclear shell | |
ShootRedNukeFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, 0, i, RedFireMission[n].track) --Execute a nuclear fire mission | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
do break end | |
elseif CheckAmmo(Battery) then --Check if the battery has ammo left for the fire mission | |
RedFiringBattery[i].shotcounter = 0 --Reset the battery shotcounter to zero | |
ShootRedFireMission(RedFiringBattery[i].name, RedFireMission[n].coord, RedFireMission[n].radius, i, RedFireMission[n].track) --Execute fire mission | |
RedFiringBattery[i].status = "firing" --Set the status of the battery to "firing" | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
loop = false --FM is assigned, no higher hirarchies have to be checked. | |
break --Quit battery loop for this hierarchy. | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
--------------------Remove outdated FM | |
if loop == true then | |
if RedFireMission[n].time < (timer.getTime() - 600) then --Check if creation time fo firemission is older than 10 minutes | |
local x = RedFireMission[n].track .. ".firemission = false" --Create a string of the location of the original track as string + ".firemission = false" which holds the booleadn that this track has no offspring firemission | |
loadstring(x)() --Eexecute the avove string as LUA code to let the track generate a firemission again | |
table.remove(RedFireMission, n) --Remove the executed fire mission from the queue | |
n = n - 1 --Decrease counter n because next firemission is now already n | |
end | |
end | |
n = n + 1 | |
until n > #RedFireMission | |
end | |
---------------- | |
return timer.getTime() + 10 --Repeat function every 10 senconds | |
end | |
timer.scheduleFunction(AssignFireMission, arg, 11) --Execute the reccuring AssignFireMission function for the first time after 11 seconds | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
--Debug | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
local function RedBatteryStatus() | |
if #RedFiringBattery == 1 then | |
return "Red Batteries:\n" .. RedFiringBattery[1].name .. ": " .. RedFiringBattery[1].status .. "\n" | |
elseif #RedFiringBattery > 1 then | |
local a = "Red Batteries:\n" | |
for n = 1, #RedFiringBattery do | |
a = a .. RedFiringBattery[n].name .. ": " .. RedFiringBattery[n].status .. "\n" | |
end | |
return a | |
else | |
return "\nRed Batteries:\nnone\n" | |
end | |
end | |
local function BlueBatteryStatus() | |
if #BlueFiringBattery == 1 then | |
return "\nBlue Batteries:\n" .. BlueFiringBattery[1].name .. ": " .. BlueFiringBattery[1].status .. "\n" | |
elseif #BlueFiringBattery > 1 then | |
local a = "\nBlue Batteries:\n" | |
for n = 1, #BlueFiringBattery do | |
a = a .. BlueFiringBattery[n].name .. ": " .. BlueFiringBattery[n].status .. "\n" | |
end | |
return a | |
else | |
return "\nBlue Batteries:\nnone\n" | |
end | |
end | |
local function RedSpotterStatus() | |
if #RedSpotter == 1 then | |
return "\nRed Spotters:\n" .. RedSpotter[1].name .. ": Targets: " .. #RedSpotter[1].target .. ", Tracks: " .. #RedSpotter[1].track .. "\n" | |
elseif #RedSpotter > 1 then | |
local a = "\nRed Spotter:\n" | |
for n = 1, #RedSpotter do | |
a = a .. RedSpotter[n].name .. ": Targets: " .. #RedSpotter[n].target .. ", Tracks: " .. #RedSpotter[n].track .. "\n" | |
end | |
return a | |
else | |
return "\nRed Spotters:\nnone\n" | |
end | |
end | |
local function BlueSpotterStatus() | |
if #BlueSpotter == 1 then | |
return "\nBlue Spotters:\n" .. BlueSpotter[1].name .. ": Targets: " .. #BlueSpotter[1].target .. ", Tracks: " .. #BlueSpotter[1].track .. "\n" | |
elseif #BlueSpotter > 1 then | |
local a = "\nBlue Spotter:\n" | |
for n = 1, #BlueSpotter do | |
a = a .. BlueSpotter[n].name .. ": Targets: " .. #BlueSpotter[n].target .. ", Tracks: " .. #BlueSpotter[n].track .. "\n" | |
end | |
return a | |
else | |
return "\nBlue Spotters:\nnone\n" | |
end | |
end | |
local function RedCounterfireRadarStatus() | |
if #RedCounterfireRadar == 1 then | |
return "\nRed CounterfireRadars:\n" .. RedCounterfireRadar[1].name .. ": Tracks: " .. #RedCounterfireRadar[1].track .. "\n" | |
elseif #RedCounterfireRadar > 1 then | |
local a = "\nRed CounterfireRadar:\n" | |
for n = 1, #RedCounterfireRadar do | |
a = a .. RedCounterfireRadar[n].name .. ": Tracks: " .. #RedCounterfireRadar[n].track .. "\n" | |
end | |
return a | |
else | |
return "\nRed CounterfireRadars:\nnone\n" | |
end | |
end | |
local function BlueCounterfireRadarStatus() | |
if #BlueCounterfireRadar == 1 then | |
return "\nBlue CounterfireRadars:\n" .. BlueCounterfireRadar[1].name .. ": Tracks: " .. #BlueCounterfireRadar[1].track .. "\n" | |
elseif #BlueCounterfireRadar > 1 then | |
local a = "\nBlue CounterfireRadar:\n" | |
for n = 1, #BlueCounterfireRadar do | |
a = a .. BlueCounterfireRadar[n].name .. ": Tracks: " .. #BlueCounterfireRadar[n].track .. "\n" | |
end | |
return a | |
else | |
return "\nBlue CounterfireRadars:\nnone\n" | |
end | |
end | |
local function RedFireMissionStatus() | |
return "\nRed FireMissions in Queue: " .. #RedFireMission | |
end | |
local function BlueFireMissionStatus() | |
return "\nBlue FireMissions in Queue: " .. #BlueFireMission | |
end | |
function StatusOverview() | |
trigger.action.outText(RedBatteryStatus() .. BlueBatteryStatus() .. RedSpotterStatus() .. BlueSpotterStatus() .. RedCounterfireRadarStatus() .. BlueCounterfireRadarStatus() .. RedFireMissionStatus() .. BlueFireMissionStatus(), 1) | |
return timer.getTime() + 1 | |
end | |
--timer.scheduleFunction(StatusOverview, nil, 2) --Remove the "--" at the beginning of this line to activate status overview window. | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment