Merge branch 'develop' into FF/Ops

This commit is contained in:
Frank
2022-03-25 11:00:17 +01:00
25 changed files with 1744 additions and 693 deletions
+71 -46
View File
@@ -46,7 +46,7 @@
--
-- # The ARMYGROUP Concept
--
-- This class enhances naval groups.
-- This class enhances ground groups.
--
-- @field #ARMYGROUP
ARMYGROUP = {
@@ -115,6 +115,7 @@ function ARMYGROUP:New(group)
self:AddTransition("*", "Cruise", "Cruising") -- Cruise along the given route of waypoints.
self:AddTransition("*", "RTZ", "Returning") -- Group is returning to (home) zone.
self:AddTransition("Holding", "Returned", "Returned") -- Group is returned to (home) zone, e.g. when unloaded from carrier.
self:AddTransition("Returning", "Returned", "Returned") -- Group is returned to (home) zone.
self:AddTransition("*", "Detour", "OnDetour") -- Make a detour to a coordinate and resume route afterwards.
@@ -652,7 +653,10 @@ function ARMYGROUP:Status()
end
end
end
else
-- Check damage of elements and group.
self:_CheckDamage()
end
-- Check that group EXISTS.
@@ -691,7 +695,7 @@ function ARMYGROUP:Status()
local text=string.format("State %s: Alive=%s", fsmstate, tostring(self:IsAlive()))
self:I(self.lid..text)
end
end
---
@@ -706,7 +710,6 @@ function ARMYGROUP:Status()
local name=element.name
local status=element.status
local unit=element.unit
--local life=unit:GetLifeRelative() or 0
local life,life0=self:GetLifePoints(element)
local life0=element.life0
@@ -926,7 +929,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
-- Next waypoint.
local wp=UTILS.DeepCopy(self.waypoints[i]) --Ops.OpsGroup#OPSGROUP.Waypoint
self:T({wp})
-- Speed.
if Speed then
wp.speed=UTILS.KnotsToMps(tonumber(Speed))
@@ -981,6 +984,13 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
-- Insert a point on road.
if wp.action==ENUMS.Formation.Vehicle.OnRoad and (wp.coordinate or wp.roadcoord) then
current=self:GetClosestRoad():WaypointGround(UTILS.MpsToKmph(self.speedWp), ENUMS.Formation.Vehicle.OnRoad)
table.insert(waypoints, 2, current)
-- Removing this for now as I don't see why it is necessary and it is very CPU intensive.
-- You only need the start and end waypoint on the road. Other waypoints on the road are not necessray.
--[[
-- take direct line if on road is too long
local wptable,length,valid=self:GetCoordinate():GetPathOnRoad(wp.coordinate or wp.roadcoord,true,false,false,false) or {}
@@ -1002,7 +1012,8 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
else
current=self:GetClosestRoad():WaypointGround(UTILS.MpsToKmph(self.speedWp), ENUMS.Formation.Vehicle.OnRoad)
table.insert(waypoints, count, current)
end
end
]]
end
-- Debug output.
@@ -1105,16 +1116,6 @@ end
-- @param #string To To state.
function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
self:T(self.lid..string.format("Group is out of ammo at t=%.3f", timer.getTime()))
-- Get current task.
local task=self:GetTaskCurrent()
if task then
if task.dcstask.id=="FireAtPoint" or task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
self:T(self.lid..string.format("Cancelling current %s task because out of ammo!", task.dcstask.id))
self:TaskCancel(task)
end
end
-- Fist, check if we want to rearm once out-of-ammo.
--TODO: IsMobile() check
@@ -1138,6 +1139,16 @@ function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
if self.rtzOnOutOfAmmo then
self:__RTZ(-1)
end
-- Get current task.
local task=self:GetTaskCurrent()
if task then
if task.dcstask.id=="FireAtPoint" or task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
self:T(self.lid..string.format("Cancelling current %s task because out of ammo!", task.dcstask.id))
self:TaskCancel(task)
end
end
end
@@ -1223,9 +1234,6 @@ end
-- @param Core.Zone#ZONE Zone The zone to return to.
-- @param #number Formation Formation of the group.
function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Zone.
local zone=Zone or self.homezone
@@ -1240,6 +1248,9 @@ function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation)
self:T(self.lid..string.format("RTZ to Zone %s", zone:GetName()))
local Coordinate=zone:GetRandomCoordinate()
-- ID of current waypoint.
local uid=self:GetWaypointCurrentUID()
-- Add waypoint after current.
local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true)
@@ -1351,6 +1362,9 @@ function ARMYGROUP:onafterRetreat(From, Event, To, Zone, Formation)
-- Set if we want to resume route after reaching the detour waypoint.
wp.detour=0
-- Cancel all missions.
self:CancelAllMissions()
end
@@ -1417,6 +1431,7 @@ end
function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
self:T(self.lid.."Engaging Target")
-- Make sure this is a target.
if Target:IsInstanceOf("TARGET") then
self.engage.Target=Target
else
@@ -1426,11 +1441,9 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
-- Target coordinate.
self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate())
-- Get a coordinate close to the target.
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Backup ROE and alarm state.
self.engage.roe=self:GetROE()
self.engage.alarmstate=self:GetAlarmstate()
@@ -1442,6 +1455,10 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Set formation.
--TODO: make this input.
local Formation=ENUMS.Formation.Vehicle.Vee
-- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
@@ -1458,37 +1475,46 @@ function ARMYGROUP:_UpdateEngageTarget()
-- Get current position vector.
local vec3=self.engage.Target:GetVec3()
if vec3 then
-- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
-- Check if target moved more than 100 meters.
if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
-- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Check if target moved more than 100 meters.
if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Set if we want to resume route after reaching the detour waypoint.
self.engage.Waypoint.detour=0
-- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
-- Set if we want to resume route after reaching the detour waypoint.
self.engage.Waypoint.detour=0
end
else
-- Could not get position of target (not alive any more?) ==> Disengage.
self:Disengage()
end
else
-- Target not alive any more == Disengage.
-- Target not alive any more ==> Disengage.
self:Disengage()
end
@@ -1587,8 +1613,7 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation
-- Speed in knots.
Speed=Speed or self:GetSpeedCruise()
-- Formation
-- Formation.
if not Formation then
if self.formationPerma then
Formation = self.formationPerma
+78 -7
View File
@@ -94,6 +94,7 @@
-- @field Core.Set#SET_GROUP transportGroupSet Groups to be transported.
-- @field Core.Point#COORDINATE transportPickup Coordinate where to pickup the cargo.
-- @field Core.Point#COORDINATE transportDropoff Coordinate where to drop off the cargo.
-- @field #number transportPickupRadius Radius in meters for pickup zone. Default 500 m.
--
-- @field Ops.OpsTransport#OPSTRANSPORT opstransport OPS transport assignment.
-- @field #number NcarriersMin Min number of required carrier assets.
@@ -383,6 +384,7 @@ _AUFTRAGSNR=0
-- @field #string ARMOREDGUARD On guard - with armored groups.
-- @field #string BARRAGE Barrage.
-- @field #string ARMORATTACK Armor attack.
-- @field #string CASENHANCED Enhanced CAS.
AUFTRAG.Type={
ANTISHIP="Anti Ship",
AWACS="AWACS",
@@ -415,6 +417,7 @@ AUFTRAG.Type={
ARMOREDGUARD="Armored Guard",
BARRAGE="Barrage",
ARMORATTACK="Armor Attack",
CASENHANCED="CAS Enhanced",
}
--- Mission status of an assigned group.
@@ -557,7 +560,7 @@ AUFTRAG.Category={
--- AUFTRAG class version.
-- @field #string version
AUFTRAG.version="0.8.1"
AUFTRAG.version="0.8.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1186,6 +1189,45 @@ function AUFTRAG:NewCAS(ZoneCAS, Altitude, Speed, Coordinate, Heading, Leg, Targ
return mission
end
--- **[AIR]** Create a CASENHANCED mission. Group(s) will go to the zone and patrol it randomly.
-- @param #AUFTRAG self
-- @param Core.Zone#ZONE CasZone The CAS zone.
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
-- @param #number Speed Speed in knots.
-- @param #number RangeMax Max range in NM. Only detected targets within this radius from the group will be engaged. Default is 25 NM.
-- @param #table TargetTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default `{"Helicopters", "Ground Units", "Light armed ships"}`.
-- @param Core.Set#SET_ZONE NoEngageZoneSet Set of zones in which targets are *not* engaged. Default is nowhere.
-- @return #AUFTRAG self
function AUFTRAG:NewCASENHANCED(CasZone, Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes)
local mission=AUFTRAG:New(AUFTRAG.Type.CASENHANCED)
-- Ensure we got a ZONE and not just the zone name.
if type(CasZone)=="string" then
CasZone=ZONE:New(CasZone)
end
mission:_TargetFromObject(CasZone)
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CASENHANCED)
mission:SetEngageDetected(RangeMax, TargetTypes or {"Helicopters", "Ground Units", "Light armed ships"}, CasZone, NoEngageZoneSet)
mission.optionROE=ENUMS.ROE.OpenFire
mission.optionROT=ENUMS.ROT.EvadeFire
mission.missionFraction=1.0
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or nil
mission.categories={AUFTRAG.Category.AIRCRAFT}
mission.DCStask=mission:GetDCSMissionTask()
return mission
end
--- **[AIR]** Create a FACA mission.
-- @param #AUFTRAG self
-- @param Wrapper.Group#GROUP Target Target group. Must be a GROUP object.
@@ -1492,8 +1534,9 @@ end
-- @param Core.Set#SET_GROUP TransportGroupSet The set group(s) to be transported.
-- @param Core.Point#COORDINATE DropoffCoordinate Coordinate where the helo will land drop off the the troops.
-- @param Core.Point#COORDINATE PickupCoordinate Coordinate where the helo will land to pick up the the cargo. Default is the fist transport group.
-- @param #number PickupRadius Radius around the pickup coordinate in meters. Default 100 m.
-- @return #AUFTRAG self
function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupCoordinate)
function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupCoordinate, PickupRadius)
local mission=AUFTRAG:New(AUFTRAG.Type.TROOPTRANSPORT)
@@ -1509,14 +1552,16 @@ function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupC
mission:_TargetFromObject(mission.transportGroupSet)
mission.transportPickup=PickupCoordinate or mission:GetTargetCoordinate()
mission.transportPickup=PickupCoordinate or mission:GetTargetCoordinate()
mission.transportDropoff=DropoffCoordinate
mission.transportPickupRadius=PickupRadius or 100
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.TROOPTRANSPORT)
-- Debug.
mission.transportPickup:MarkToAll("Pickup")
mission.transportDropoff:MarkToAll("Drop off")
--mission.transportPickup:MarkToAll("Pickup Transport")
--mission.transportDropoff:MarkToAll("Drop off")
-- TODO: what's the best ROE here?
mission.optionROE=ENUMS.ROE.ReturnFire
@@ -1645,8 +1690,9 @@ end
-- @param Core.Zone#ZONE Zone The patrol zone.
-- @param #number Speed Speed in knots.
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
-- @param #string Formation Formation used during patrol.
-- @return #AUFTRAG self
function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude)
function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude, Formation)
local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE)
@@ -1670,10 +1716,13 @@ function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude)
mission.categories={AUFTRAG.Category.ALL}
mission.DCStask=mission:GetDCSMissionTask()
mission.DCStask.params.formation=Formation
return mission
end
--- **[GROUND]** Create a ARMORATTACK mission. Armoured ground group(s) will go to the zone and attack.
-- @param #AUFTRAG self
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object.
@@ -3478,7 +3527,7 @@ function AUFTRAG:SetGroupStatus(opsgroup, status)
local groupsDone=self:CheckGroupsDone()
-- Debug info.
self:T2(self.lid..string.format("Setting OPSGROUP %s status to %s. IsNotOver=%s CheckGroupsDone=%s", opsgroup.groupname, self:GetGroupStatus(opsgroup), tostring(self:IsNotOver()), tostring(self:CheckGroupsDone())))
self:T2(self.lid..string.format("Setting OPSGROUP %s status to %s. IsNotOver=%s CheckGroupsDone=%s", opsgroup.groupname, self:GetGroupStatus(opsgroup), tostring(self:IsNotOver()), tostring(groupsDone)))
-- Check if ALL flights are done with their mission.
if isNotOver and groupsDone then
@@ -4958,6 +5007,26 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable)
DCStask.params=param
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.CASENHANCED then
-------------------------
-- CAS ENHANCED Mission --
-------------------------
local DCStask={}
DCStask.id="PatrolZone"
-- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP.
local param={}
param.zone=self:GetObjective()
param.altitude=self.missionAltitude
param.speed=self.missionSpeed
DCStask.params=param
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.ARMORATTACK then
@@ -5147,6 +5216,8 @@ function AUFTRAG:GetMissionTaskforMissionType(MissionType)
mtask=ENUMS.MissionTask.CAS
elseif MissionType==AUFTRAG.Type.PATROLZONE then
mtask=ENUMS.MissionTask.CAS
elseif MissionType==AUFTRAG.Type.CASENHANCED then
mtask=ENUMS.MissionTask.CAS
elseif MissionType==AUFTRAG.Type.ESCORT then
mtask=ENUMS.MissionTask.ESCORT
elseif MissionType==AUFTRAG.Type.FACA then
+57 -14
View File
@@ -6,6 +6,14 @@
--
-- ===
--
-- ## Missions:--- **Ops** -- Combat Search and Rescue.
--
-- ===
--
-- **CSAR** - MOOSE based Helicopter CSAR Operations.
--
-- ===
--
-- ## Missions:
--
-- ### [CSAR - Combat Search & Rescue](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CSAR)
@@ -118,7 +126,8 @@
-- self.SRSModulation = radio.modulation.AM -- modulation
-- --
-- self.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection --shagrat
--
-- self.wetfeettemplate = "man in floating thingy" -- if you use a mod to have a pilot in a rescue float, put the template name in here for wet feet spawns. Note: in conjunction with csarUsePara this might create dual ejected pilots in edge cases.
--
-- ## 3. Results
--
-- Number of successful landings with save pilots and aggregated number of saved pilots is stored in these variables in the object:
@@ -227,8 +236,9 @@ CSAR = {
-- @field #number frequency Frequency of the NDB.
-- @field #string player Player name if applicable.
-- @field Wrapper.Group#GROUP group Spawned group object.
-- @field #number timestamp Timestamp for approach process
-- @field #boolean alive Group is alive or dead/rescued
-- @field #number timestamp Timestamp for approach process.
-- @field #boolean alive Group is alive or dead/rescued.
-- @field #boolean wetfeet Group is spawned over (deep) water.
--- All slot / Limit settings
-- @type CSAR.AircraftType
@@ -244,11 +254,12 @@ CSAR.AircraftType["Mi-8MT"] = 12
CSAR.AircraftType["Mi-24P"] = 8
CSAR.AircraftType["Mi-24V"] = 8
CSAR.AircraftType["Bell-47"] = 2
CSAR.AircraftType["UH-60L"] = 10
CSAR.AircraftType["UH-60L"] = 10
CSAR.AircraftType["AH-64D_BLK_II"] = 2
--- CSAR class version.
-- @field #string version
CSAR.version="1.0.3"
CSAR.version="1.0.4d"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -390,6 +401,10 @@ function CSAR:New(Coalition, Template, Alias)
-- added 0.1.3
self.csarUsePara = false -- shagrat set to true, will use the LandingAfterEjection Event instead of Ejection
-- added 0.1.4
self.wetfeettemplate = nil
self.usewetfeet = false
-- WARNING - here\'ll be dragons
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
@@ -502,8 +517,9 @@ end
-- @param #string Typename Typename of unit.
-- @param #number Frequency Frequency of the NDB in Hz
-- @param #string Playername Name of Player (if applicable)
-- @param #boolean Wetfeet Ejected over water
-- @return #CSAR self.
function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername)
function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername,Wetfeet)
self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername})
-- create new entry
@@ -519,6 +535,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript
DownedPilot.group = Group
DownedPilot.timestamp = 0
DownedPilot.alive = true
DownedPilot.wetfeet = Wetfeet or false
-- Add Pilot
local PilotTable = self.downedPilots
@@ -568,17 +585,23 @@ end
-- @param #number country Country for template.
-- @param Core.Point#COORDINATE point Coordinate to spawn at.
-- @param #number frequency Frequency of the pilot's beacon
-- @param #boolean wetfeet Spawn is over water
-- @return Wrapper.Group#GROUP group The #GROUP object.
-- @return #string alias The alias name.
function CSAR:_SpawnPilotInField(country,point,frequency)
self:T({country,point,frequency})
function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet)
self:T({country,point,frequency,tostring(wetfeet)})
local freq = frequency or 1000
local freq = freq / 1000 -- kHz
for i=1,10 do
math.random(i,10000)
end
if point:IsSurfaceTypeWater() then point.y = 0 end
if point:IsSurfaceTypeWater() or wetfeet then
point.y = 0
end
local template = self.template
if self.usewetfeet and wetfeet then
template = self.wetfeettemplate
end
local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99))
local coalition = self.coalition
local pilotcacontrol = self.allowDownedPilotCAcontrol -- Switch AI on/oof - is this really correct for CA?
@@ -644,13 +667,19 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description})
local template = self.template
local wetfeet = false
local surface = _point:GetSurfaceType()
if surface == land.SurfaceType.WATER then
wetfeet = true
end
if not _freq then
_freq = self:_GenerateADFFrequency()
if not _freq then _freq = 333000 end --noob catch
end
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq)
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq,wetfeet)
local _typeName = _typeName or "Pilot"
@@ -688,7 +717,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
local _GroupName = _spawnedGroup:GetName() or _alias
self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName)
self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName,wetfeet)
self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc.
@@ -935,9 +964,19 @@ function CSAR:_EventHandler(EventData)
if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then
return
end
-- TODO: Over water check --- EVENTS.LandingAfterEjection NOT triggered by DCS, so handle csarUsePara = true case
-- might create dual pilots in edge cases
local wetfeet = false
local surface = _unit:GetCoordinate():GetSurfaceType()
if surface == land.SurfaceType.WATER then
wetfeet = true
end
-- all checks passed, get going.
if self.csarUsePara == false then --shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land
if self.csarUsePara == false or (self.csarUsePara and wetfeet ) then --shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land
local _freq = self:_GenerateADFFrequency()
self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
return true
@@ -1294,7 +1333,8 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
_time = self.landedStatus[_lookupKeyHeli] - 10
self.landedStatus[_lookupKeyHeli] = _time
end
if _time <= 0 or _distance < self.loadDistance then
--if _time <= 0 or _distance < self.loadDistance then
if _distance < self.loadDistance + 5 or _distance <= 13 then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true)
return true
@@ -1999,6 +2039,9 @@ function CSAR:onafterStart(From, Event, To)
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
end
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
if self.wetfeettemplate then
self.usewetfeet = true
end
self:__Status(-10)
return self
end
+15 -6
View File
@@ -1017,11 +1017,12 @@ CTLD.UnitTypes = {
["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers.
--Actually it's longer, but the center coord is off-center of the model.
["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, -- 2 ppl **outside** the helo
}
--- CTLD class version.
-- @field #string version
CTLD.version="1.0.9"
CTLD.version="1.0.10"
--- Instantiate a new CTLD.
-- @param #CTLD self
@@ -2095,11 +2096,18 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist, _ignoreweight)
-- cycle
local index = 0
local found = {}
local loadedmass = self:_GetUnitCargoMass(_unit)
local unittype = _unit:GetTypeName()
local capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitCapabilities
local maxmass = capabilities.cargoweightlimit
local maxloadable = maxmass - loadedmass
local loadedmass = 0
local unittype = "none"
local capabilities = {}
local maxmass = 2000
local maxloadable = 2000
if not _ignoreweight then
loadedmass = self:_GetUnitCargoMass(_unit)
unittype = _unit:GetTypeName()
capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitCapabilities
maxmass = capabilities.cargoweightlimit or 2000
maxloadable = maxmass - loadedmass
end
self:T(self.lid .. " Max loadable mass: " .. maxloadable)
for _,_cargoobject in pairs (existingcrates) do
local cargo = _cargoobject -- #CTLD_CARGO
@@ -2256,6 +2264,7 @@ end
-- @return #number mass in kgs
function CTLD:_GetUnitCargoMass(Unit)
self:T(self.lid .. " _GetUnitCargoMass")
if not Unit then return 0 end
local unitname = Unit:GetName()
local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo
local loadedmass = 0 -- #number
+43 -6
View File
@@ -103,7 +103,7 @@
--
-- Strategically important zones, which should be captured can be added via the @{#CHIEF.AddStrategicZone}() function.
--
-- If the zone is currently owned by another coalition and enemy ground troops are present in the zone, a CAS mission is lauchned.
-- If the zone is currently owned by another coalition and enemy ground troops are present in the zone, a CAS and an ARTY mission are lauchned, provided assets are available.
--
-- Once the zone is cleaned of enemy forces, ground (infantry) troops are send there. These require a transportation via helicopters.
-- So in order to deploy our own troops, infantry assets with `AUFTRAG.Type.ONGUARD` and helicopters with `AUFTRAG.Type.OPSTRANSPORT` need to be available.
@@ -180,7 +180,7 @@ CHIEF.Strategy = {
--- CHIEF class version.
-- @field #string version
CHIEF.version="0.1.0"
CHIEF.version="0.1.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1731,7 +1731,7 @@ function CHIEF:CheckOpsZoneQueue()
-- Has a patrol mission?
local hasMissionPatrol=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ONGUARD) or stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARMOREDGUARD)
-- Has a CAS mission?
local hasMissionCAS=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CAS)
local hasMissionCAS=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CASENHANCED)
-- Has a ARTY mission?
local hasMissionARTY=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY)
@@ -1775,12 +1775,13 @@ function CHIEF:CheckOpsZoneQueue()
-- Recruite CAS assets.
local recruited=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.CAS, 1, 1)
local recruited=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.CASENHANCED, 1, 1)
-- Debug message.
self:T(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets=%s", tostring(recruited)))
end
if not hasMissionARTY then
-- Debug message.
@@ -1811,7 +1812,7 @@ function CHIEF:CheckOpsZoneQueue()
local hasMissionPATROL=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.PATROLZONE)
-- Has a CAS mission?
local hasMissionCAS, CASMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CAS)
local hasMissionCAS, CASMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CASENHANCED)
local hasMissionARTY, ARTYMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY)
if ownercoalition==self.coalition and stratzone.opszone:IsEmpty() and hasMissionCAS then
@@ -2208,7 +2209,7 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
end
-- Recruite infantry assets.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, RangeMax, nil, nil, Categories, Attributes)
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, RangeMax, nil, nil, nil, Categories, Attributes)
if recruited then
@@ -2260,12 +2261,48 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
-- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission)
-- Set ops zone to transport.
transport.opszone=StratZone.opszone
transport.chief=self
transport.commander=self.commander
return true
else
LEGION.UnRecruitAssets(assets)
return false
end
elseif MissionType==AUFTRAG.Type.CASENHANCED then
-- Create Patrol zone mission.
local caszone = StratZone.opszone.zone
local coord = caszone:GetCoordinate()
local height = UTILS.MetersToFeet(coord:GetLandHeight())+2500
local Speed = 200
if assets[1] then
if assets[1].speedmax then
Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200
end
end
--local mission=AUFTRAG:NewCAS(caszone,height,Speed,coord,math.random(0,359),Leg)
local mission=AUFTRAG:NewCASENHANCED(caszone, height, Speed)
-- Add assets to mission.
for _,asset in pairs(assets) do
mission:AddAsset(asset)
end
-- Assign mission to legions.
self:MissionAssign(mission, legions)
-- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission)
return true
elseif MissionType==AUFTRAG.Type.CAS then
-- Create Patrol zone mission.
+25 -23
View File
@@ -31,7 +31,8 @@
-- @field #string livery Livery of the cohort.
-- @field #number skill Skill of cohort members.
-- @field Ops.Legion#LEGION legion The LEGION object the cohort belongs to.
-- @field #number Ngroups Number of asset OPS groups this cohort has.
-- @field #number Ngroups Number of asset OPS groups this cohort has.
-- @field #number Nkilled Number of destroyed asset groups.
-- @field #number engageRange Mission range in meters.
-- @field #string attribute Generalized attribute of the cohort template group.
-- @field #table tacanChannel List of TACAN channels available to the cohort.
@@ -67,6 +68,7 @@ COHORT = {
skill = nil,
legion = nil,
Ngroups = nil,
Ngroups = 0,
engageRange = nil,
tacanChannel = {},
weightAsset = 99999,
@@ -275,12 +277,10 @@ end
--- Set number of units in groups.
-- @param #COHORT self
-- @param #number nunits Number of units. Must be >=1 and <=4. Default 2.
-- @param #number nunits Number of units. Default 2.
-- @return #COHORT self
function COHORT:SetGrouping(nunits)
self.ngrouping=nunits or 2
if self.ngrouping<1 then self.ngrouping=1 end
if self.ngrouping>4 then self.ngrouping=4 end
return self
end
@@ -919,36 +919,38 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
-- ARMY/NAVYGROUP combat ready?
---
-- Disable this for now as it can cause problems - at least with transport and cargo assets.
--self:I("Attribute is: "..asset.attribute)
if flightgroup:IsArmygroup() then
-- check for fighting assets
if asset.attribute == WAREHOUSE.Attribute.GROUND_ARTILLERY or
asset.attribute == WAREHOUSE.Attribute.GROUND_TANK or
asset.attribute == WAREHOUSE.Attribute.GROUND_INFANTRY or
asset.attribute == WAREHOUSE.Attribute.GROUND_AAA or
asset.attribute == WAREHOUSE.Attribute.GROUND_SAM
then
combatready=true
end
else
combatready=false
end
-- Not ready when rearming, retreating or returning!
if flightgroup:IsRearming() or flightgroup:IsRetreating() or flightgroup:IsReturning() then
combatready=false
end
end
-- Check transport/cargo for combat readyness!
-- Not ready when currently acting as ops transport carrier.
if flightgroup:IsLoading() or flightgroup:IsTransporting() or flightgroup:IsUnloading() or flightgroup:IsPickingup() or flightgroup:IsCarrier() then
combatready=false
end
end
-- Not ready when currently acting as ops transport cargo.
if flightgroup:IsCargo() or flightgroup:IsBoarding() or flightgroup:IsAwaitingLift() then
combatready=false
end
-- Disable this for now as it can cause problems - at least with transport and cargo assets.
--self:I("Attribute is: "..asset.attribute)
if flightgroup:IsArmygroup() then
-- check for fighting assets
if asset.attribute == WAREHOUSE.Attribute.GROUND_ARTILLERY or
asset.attribute == WAREHOUSE.Attribute.GROUND_TANK or
asset.attribute == WAREHOUSE.Attribute.GROUND_INFANTRY or
asset.attribute == WAREHOUSE.Attribute.GROUND_AAA or
asset.attribute == WAREHOUSE.Attribute.GROUND_SAM
then
combatready=true
end
else
combatready=false
end
-- This asset is "combatready".
if combatready then
self:T(self.lid.."Adding SPAWNED asset to ANOTHER mission as it is COMBATREADY")
+9 -5
View File
@@ -132,7 +132,7 @@ COMMANDER = {
--- COMMANDER class version.
-- @field #string version
COMMANDER.version="0.1.0"
COMMANDER.version="0.1.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1190,7 +1190,7 @@ function COMMANDER:RecruitAssetsForMission(Mission)
local Payloads=Mission.payloads
-- Recruite assets.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem, nil)
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem)
return recruited, assets, legions
end
@@ -1292,6 +1292,7 @@ function COMMANDER:CheckTransportQueue()
-- Weight of the heaviest cargo group. Necessary condition that this fits into on carrier unit!
local weightGroup=0
local TotalWeight=0
-- Calculate the max weight so we know which cohorts can provide carriers.
if #cargoOpsGroups>0 then
@@ -1301,13 +1302,14 @@ function COMMANDER:CheckTransportQueue()
if weight>weightGroup then
weightGroup=weight
end
TotalWeight=TotalWeight+weight
end
end
if weightGroup>0 then
-- Recruite assets from legions.
local recruited, assets, legions=self:RecruitAssetsForTransport(transport, weightGroup)
local recruited, assets, legions=self:RecruitAssetsForTransport(transport, weightGroup, TotalWeight)
if recruited then
@@ -1344,10 +1346,12 @@ end
--- Recruit assets for a given OPS transport.
-- @param #COMMANDER self
-- @param Ops.OpsTransport#OPSTRANSPORT Transport The OPS transport.
-- @param #number CargoWeight Weight of the heaviest cargo group.
-- @param #number TotalWeight Total weight of all cargo groups.
-- @return #boolean If `true`, enough assets could be recruited.
-- @return #table Recruited assets.
-- @return #table Legions that have recruited assets.
function COMMANDER:RecruitAssetsForTransport(Transport, CargoWeight)
function COMMANDER:RecruitAssetsForTransport(Transport, CargoWeight, TotalWeight)
if CargoWeight==0 then
-- No cargo groups!
@@ -1381,7 +1385,7 @@ function COMMANDER:RecruitAssetsForTransport(Transport, CargoWeight)
local NreqMin,NreqMax=Transport:GetRequiredCarriers()
-- Recruit assets and legions.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NreqMin, NreqMax, TargetVec2, nil, nil, nil, CargoWeight)
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NreqMin, NreqMax, TargetVec2, nil, nil, nil, CargoWeight, TotalWeight)
return recruited, assets, legions
end
+5 -56
View File
@@ -168,7 +168,7 @@ FLIGHTGROUP.Attribute = {
--- FLIGHTGROUP class version.
-- @field #string version
FLIGHTGROUP.version="0.7.0"
FLIGHTGROUP.version="0.7.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -730,60 +730,6 @@ end
-- Status
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---- Update status.
-- @param #FLIGHTGROUP self
function FLIGHTGROUP:onbeforeStatus(From, Event, To)
-- First we check if elements are still alive. Could be that they were despawned without notice, e.g. when landing on a too small airbase.
for i,_element in pairs(self.elements) do
local element=_element --Ops.OpsGroup#OPSGROUP.Element
-- Check that element is not already dead or not yet alive.
if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
-- Unit shortcut.
local unit=element.unit
local isdead=false
if unit and unit:IsAlive() then
-- Get life points.
local life=unit:GetLife() or 0
-- Units with life <=1 are dead.
if life<=1 then
--env.info(string.format("FF unit %s: live<=1 in status at T=%.3f", unit:GetName(), timer.getTime()))
isdead=true
end
else
-- Not alive any more.
--env.info(string.format("FF unit %s: NOT alive in status at T=%.3f", unit:GetName(), timer.getTime()))
isdead=true
end
-- This one is dead.
if isdead then
local text=string.format("Element %s is dead at t=%.3f but has status %s! Maybe despawned without notice or landed at a too small airbase. Calling ElementDead in 60 sec to give other events a chance",
tostring(element.name), timer.getTime(), tostring(element.status))
self:T(self.lid..text)
self:__ElementDead(60, element)
end
end
end
if self:IsDead() then
self:T(self.lid..string.format("Onbefore Status DEAD ==> false"))
return false
elseif self:IsStopped() then
self:T(self.lid..string.format("Onbefore Status STOPPED ==> false"))
return false
end
return true
end
--- Status update.
-- @param #FLIGHTGROUP self
function FLIGHTGROUP:Status()
@@ -845,7 +791,10 @@ function FLIGHTGROUP:Status()
end
end
end
else
-- Check damage.
self:_CheckDamage()
end
---
File diff suppressed because it is too large Load Diff
+108 -18
View File
@@ -45,13 +45,14 @@ LEGION = {
--- LEGION class version.
-- @field #string version
LEGION.version="0.2.0"
LEGION.version="0.2.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Create FLEED class.
-- DONE: Aircraft will not start hot on Alert5.
-- DONE: OPS transport.
-- DONE: Make general so it can be inherited by AIRWING and BRIGADE classes.
@@ -81,6 +82,10 @@ function LEGION:New(WarehouseName, LegionName)
-- Defaults:
-- TODO: What?
self:SetMarker(false)
-- Dead and crash events are handled via opsgroups.
self:UnHandleEvent(EVENTS.Crash)
self:UnHandleEvent(EVENTS.Dead)
-- Add FSM transitions.
-- From State --> Event --> To State
@@ -530,6 +535,7 @@ function LEGION:CheckMissionQueue()
local Transport=nil
if mission.NcarriersMin then
local Legions=mission.transportLegions or {self}
TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone)
end
@@ -743,6 +749,10 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
if Mission.missionTask then
asset.missionTask=Mission.missionTask
end
if Mission.type==AUFTRAG.Type.ALERT5 then
asset.takeoffType=COORDINATE.WaypointType.TakeOffParking
end
end
@@ -882,7 +892,18 @@ function LEGION:onafterTransportCancel(From, Event, To, Transport)
local cargos=Transport:GetCargoOpsGroups(false)
for _,_cargo in pairs(cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP
-- Remover my lift.
cargo:_DelMyLift(Transport)
-- Legion of cargo group
local legion=cargo.legion
-- Add asset back to legion.
if legion then
legion:T(self.lid..string.format("Adding cargo group %s back to legion", cargo:GetName()))
legion:__AddAsset(0.1, cargo.group, 1)
end
end
-- Remove asset from mission.
@@ -993,7 +1014,7 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
-- Debug text.
local text=string.format("New asset %s with assignment %s and request assignment %s", asset.spawngroupname, tostring(asset.assignment), tostring(assignment))
self:T3(self.lid..text)
self:T(self.lid..text)
-- Get cohort.
local cohort=self:_GetCohort(asset.assignment)
@@ -1010,7 +1031,7 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
local nunits=#asset.template.units
-- Debug text.
local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s, nunits=%d %s", cohort.name, assignment, asset.unittype, asset.attribute, nunits, tostring(cohort.ngrouping))
local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s, nunits=%d ngroup=%s", cohort.name, assignment, asset.unittype, asset.attribute, nunits, tostring(cohort.ngrouping))
self:T(self.lid..text)
-- Adjust number of elements in the group.
@@ -1018,6 +1039,10 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
local template=asset.template
local N=math.max(#template.units, cohort.ngrouping)
-- We need to recalc the total weight and cargo bay.
asset.weight=0
asset.cargobaytot=0
-- Handle units.
for i=1,N do
@@ -1028,15 +1053,28 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
-- If grouping is larger than units present, copy first unit.
if i>nunits then
table.insert(template.units, UTILS.DeepCopy(template.units[1]))
asset.cargobaytot=asset.cargobaytot+asset.cargobay[1]
asset.weight=asset.weight+asset.weights[1]
template.units[i].x=template.units[1].x+5*(i-nunits)
template.units[i].y=template.units[1].y+5*(i-nunits)
else
if i<=cohort.ngrouping then
asset.weight=asset.weight+asset.weights[i]
asset.cargobaytot=asset.cargobaytot+asset.cargobay[i]
end
end
-- Remove units if original template contains more than in grouping.
if cohort.ngrouping<nunits and i>nunits then
unit=nil
if i>cohort.ngrouping then
template.units[i]=nil
end
end
-- Set number of units.
asset.nunits=cohort.ngrouping
-- Debug info.
self:T(self.lid..string.format("After regrouping: Nunits=%d, weight=%.1f cargobaytot=%.1f kg", #asset.template.units, asset.weight, asset.cargobaytot))
end
-- Set takeoff type.
@@ -1061,6 +1099,11 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
-- Asset is returned to the COHORT
---
self:T(self.lid..string.format("Asset returned to legion ==> calling LegionAssetReturned event"))
-- Set takeoff type in case it was overwritten for an ALERT5 mission.
asset.takeoffType=cohort.takeoffType
-- Trigger event.
self:LegionAssetReturned(cohort, asset)
@@ -1078,7 +1121,7 @@ end
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned.
function LEGION:onafterLegionAssetReturned(From, Event, To, Cohort, Asset)
-- Debug message.
self:T(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment)))
self:I(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment)))
-- Stop flightgroup.
if Asset.flightgroup and not Asset.flightgroup:IsStopped() then
@@ -1246,7 +1289,7 @@ function LEGION:onafterAssetDead(From, Event, To, asset, request)
-- Remove group from the detection set of the CHIEF (INTEL).
if self.commander and self.commander.chief then
self.commander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname})
end
end
-- Remove asset from mission is done via Mission:AssetDead() call from flightgroup onafterFlightDead function
-- Remove asset from squadron same
@@ -1809,7 +1852,7 @@ function LEGION:RecruitAssetsForMission(Mission)
end
-- Recuit assets.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem, nil)
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem)
return recruited, assets, legions
end
@@ -1826,17 +1869,20 @@ function LEGION:RecruitAssetsForTransport(Transport)
local cargoOpsGroups=Transport:GetCargoOpsGroups(false)
local weightGroup=0
local TotalWeight=nil
-- At least one group should be spawned.
if #cargoOpsGroups>0 then
-- Calculate the max weight so we know which cohorts can provide carriers.
TotalWeight=0
for _,_opsgroup in pairs(cargoOpsGroups) do
local opsgroup=_opsgroup --Ops.OpsGroup#OPSGROUP
local weight=opsgroup:GetWeightTotal()
if weight>weightGroup then
weightGroup=weight
end
TotalWeight=TotalWeight+weight
end
else
-- No cargo groups!
@@ -1854,7 +1900,7 @@ function LEGION:RecruitAssetsForTransport(Transport)
-- Recruit assets and legions.
local recruited, assets, legions=LEGION.RecruitCohortAssets(self.cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NreqMin, NreqMax, TargetVec2, nil, nil, nil, weightGroup)
local recruited, assets, legions=LEGION.RecruitCohortAssets(self.cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NreqMin, NreqMax, TargetVec2, nil, nil, nil, weightGroup, TotalWeight)
return recruited, assets, legions
end
@@ -1915,12 +1961,13 @@ end
-- @param #number RangeMax Max range in meters.
-- @param #number RefuelSystem Refuelsystem.
-- @param #number CargoWeight Cargo weight for recruiting transport carriers.
-- @param #number TotalWeight Total cargo weight in kg.
-- @param #table Categories Group categories.
-- @param #table Attributes Group attributes. See `GROUP.Attribute.`
-- @return #boolean If `true` enough assets could be recruited.
-- @return #table Recruited assets. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
-- @return #table Legions of recruited assets.
function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, Categories, Attributes)
function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, Categories, Attributes)
-- The recruited assets.
local Assets={}
@@ -2055,10 +2102,30 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
---
-- Add assets to mission.
local cargobay=0
for i=1,Nassets do
local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
asset.isReserved=true
Legions[asset.legion.alias]=asset.legion
if TotalWeight then
-- Number of
local N=math.floor(asset.cargobaytot/asset.nunits / CargoWeight)*asset.nunits
--env.info(string.format("cargobaytot=%d, cargoweight=%d ==> N=%d", asset.cargobaytot, CargoWeight, N))
cargobay=cargobay + N*CargoWeight
if cargobay>=TotalWeight then
--env.info(string.format("FF found enough assets to transport all cargo! N=%d [%d], cargobay=%.1f >= %.1f kg total weight", i, Nassets, cargobay, TotalWeight))
Nassets=i
break
end
end
end
-- Return payloads of not needed assets.
@@ -2153,7 +2220,7 @@ function LEGION:AssignAssetsForEscort(Cohorts, Assets, NescortMin, NescortMax)
end
-- Recruit escort asset for the mission asset.
local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, nil, NescortMin, NescortMax, TargetVec2, nil, nil, nil, nil, Categories)
local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, nil, NescortMin, NescortMax, TargetVec2, nil, nil, nil, nil, nil, Categories)
if Erecruited then
Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets, ecategory=asset.category, TargetTypes=TargetTypes}
@@ -2264,13 +2331,14 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
end
-- Get all legions and heaviest cargo group weight
local CargoLegions={} ; local CargoWeight=nil
local CargoLegions={} ; local CargoWeight=nil ; local TotalWeight=0
for _,_asset in pairs(CargoAssets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
CargoLegions[asset.legion.alias]=asset.legion
if CargoWeight==nil or asset.weight>CargoWeight then
CargoWeight=asset.weight
end
TotalWeight=TotalWeight+asset.weight
end
-- Target is the deploy zone.
@@ -2278,7 +2346,7 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
-- Recruit assets and legions.
local TransportAvail, CarrierAssets, CarrierLegions=
LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NcarriersMin, NcarriersMax, TargetVec2, nil, nil, nil, CargoWeight, Categories, Attributes)
LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NcarriersMin, NcarriersMax, TargetVec2, nil, nil, nil, CargoWeight, TotalWeight, Categories, Attributes)
if TransportAvail then
@@ -2396,12 +2464,34 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
-- Reduce score for legions that are futher away.
score=score-distance
-- Intercepts need to be carried out quickly. We prefer spawned assets.
--if MissionType==AUFTRAG.Type.INTERCEPT then
if asset.spawned then
score=score+25
-- Intercepts need to be carried out quickly. We prefer spawned assets.
if asset.spawned and asset.flightgroup and asset.flightgroup:IsAlive() then
local currmission=asset.flightgroup:GetMissionCurrent()
if currmission then
if currmission.type==AUFTRAG.Type.ALERT5 and currmission.alert5MissionType==MissionType then
-- Prefer assets that are on ALERT5 for this mission type.
score=score+25
elseif currmission==AUFTRAG.Type.GCICAP and MissionType==AUFTRAG.Type.INTERCEPT then
-- Prefer assets that are on GCICAP to perform INTERCEPTS
score=score+25
end
end
--end
if MissionType==AUFTRAG.Type.OPSTRANSPORT or MissionType==AUFTRAG.Type.AMMOSUPPLY or MissionType==AUFTRAG.Type.AWACS or MissionType==AUFTRAG.Type.FUELSUPPLY or MissionType==AUFTRAG.Type.TANKER then
-- TODO: need to check for missions that do not require ammo like transport, recon, awacs, tanker etc.
-- We better take a fresh asset. Sometimes spawned assets to something else, which is difficult to check.
score=score-10
else
-- Combat mission.
if asset.flightgroup:IsOutOfAmmo() then
-- Assets that are out of ammo are not considered.
score=score-1000
end
end
end
-- TRANSPORT specific.
if MissionType==AUFTRAG.Type.OPSTRANSPORT then
+41 -23
View File
@@ -90,7 +90,7 @@ NAVYGROUP = {
--- NavyGroup version.
-- @field #string version
NAVYGROUP.version="0.7.0"
NAVYGROUP.version="0.7.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -760,6 +760,12 @@ function NAVYGROUP:Status(From, Event, To)
-- Check into wind queue.
self:_CheckTurnsIntoWind()
-- Check ammo status.
self:_CheckAmmoStatus()
-- Check damage of elements and group.
self:_CheckDamage()
-- Check if group got stuck.
self:_CheckStuck()
@@ -775,6 +781,9 @@ function NAVYGROUP:Status(From, Event, To)
end
end
else
-- Check damage of elements and group.
self:_CheckDamage()
end
-- Group exists but can also be inactive.
@@ -1430,37 +1439,46 @@ function NAVYGROUP:_UpdateEngageTarget()
-- Get current position vector.
local vec3=self.engage.Target:GetVec3()
if vec3 then
-- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
-- Check if target moved more than 100 meters.
if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
-- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Check if target moved more than 100 meters.
if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Set if we want to resume route after reaching the detour waypoint.
self.engage.Waypoint.detour=0
-- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
-- Set if we want to resume route after reaching the detour waypoint.
self.engage.Waypoint.detour=0
end
else
-- Could not get position of target (not alive any more?) ==> Disengage.
self:Disengage()
end
else
-- Target not alive any more == Disengage.
-- Target not alive any more ==> Disengage.
self:Disengage()
end
+267 -65
View File
@@ -466,7 +466,7 @@ OPSGROUP.CargoStatus={
--- OpsGroup version.
-- @field #string version
OPSGROUP.version="0.7.5"
OPSGROUP.version="0.7.6"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -541,6 +541,9 @@ function OPSGROUP:New(group)
else
end
-- Set gen attribute.
self.attribute=self.group:GetAttribute()
local units=self.group:GetUnits()
@@ -864,6 +867,12 @@ function OPSGROUP:GetLifePoints(Element)
return life, life0
end
--- Get generalized attribute.
-- @param #OPSGROUP self
-- @return #string Generalized attribute.
function OPSGROUP:GetAttribute()
return self.attribute
end
--- Set verbosity level.
-- @param #OPSGROUP self
@@ -879,6 +888,7 @@ end
-- @param Ops.Legion#LEGION Legion The Legion.
-- @return #OPSGROUP self
function OPSGROUP:_SetLegion(Legion)
self:T2(self.lid..string.format("Adding opsgroup to legion %s", Legion.alias))
self.legion=Legion
return self
end
@@ -1609,13 +1619,6 @@ function OPSGROUP:Despawn(Delay, NoEventRemoveUnit)
self.scheduleIDDespawn=self:ScheduleOnce(Delay, OPSGROUP.Despawn, self, 0, NoEventRemoveUnit)
else
if self.legion and not NoEventRemoveUnit then
-- Add asset back in 10 seconds.
self:T(self.lid..string.format("Despawning Group by adding asset to LEGION!"))
self.legion:AddAsset(self.group, 1)
return
end
-- Debug info.
self:T(self.lid..string.format("Despawning Group!"))
@@ -1647,6 +1650,30 @@ function OPSGROUP:Despawn(Delay, NoEventRemoveUnit)
return self
end
--- Return group back to the legion it belongs to.
-- Group is despawned and added back to the stock.
-- @param #OPSGROUP self
-- @param #number Delay Delay in seconds before the group will be despawned. Default immediately
-- @return #OPSGROUP self
function OPSGROUP:ReturnToLegion(Delay)
if Delay and Delay>0 then
self.scheduleIDDespawn=self:ScheduleOnce(Delay, OPSGROUP.ReturnToLegion, self)
else
if self.legion then
-- Add asset back.
self:T(self.lid..string.format("Adding asset back to LEGION"))
self.legion:AddAsset(self.group, 1)
else
self:E(self.lid..string.format("ERROR: Group does not belong to a LEGION!"))
end
end
return self
end
--- Destroy a unit of the group. A *Unit Lost* for aircraft or *Dead* event for ground/naval units is generated.
-- @param #OPSGROUP self
-- @param #string UnitName Name of the unit which should be destroyed.
@@ -1670,6 +1697,9 @@ function OPSGROUP:DestroyUnit(UnitName, Delay)
else
self:CreateEventDead(EventTime, unit)
end
-- Despawn unit.
unit:destroy()
end
@@ -2618,6 +2648,17 @@ function OPSGROUP:GetWaypointCurrent()
return self.waypoints[self.currentwp]
end
--- Get current waypoint UID.
-- @param #OPSGROUP self
-- @return #number Current waypoint UID.
function OPSGROUP:GetWaypointCurrentUID()
local wp=self:GetWaypointCurrent()
if wp then
return wp.uid
end
return nil
end
--- Get coordinate of next waypoint of the group.
-- @param #OPSGROUP self
-- @param #boolean cyclic If true, return first waypoint if last waypoint was reached.
@@ -3574,8 +3615,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
--Coordinate:MarkToAll("Random Patrol Zone Coordinate")
-- Speed and altitude.
local Speed=UTILS.MpsToKnots(Task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
--local Speed=UTILS.KmphToKnots(Task.dcstask.params.speed or self.speedCruise)
local Speed=Task.dcstask.params.speed and UTILS.MpsToKnots(Task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude) or nil
local currUID=self:GetWaypointCurrent().uid
@@ -3585,7 +3625,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
if self.isFlightgroup then
wp=FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
elseif self.isArmygroup then
wp=ARMYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Formation)
wp=ARMYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Task.dcstask.params.formation)
elseif self.isNavygroup then
wp=NAVYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
end
@@ -3612,7 +3652,6 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- Speed and altitude.
local Speed=UTILS.MpsToKnots(Task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
--local Speed=UTILS.KmphToKnots(Task.dcstask.params.speed or self.speedCruise)
local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude) or nil
--Coordinate:MarkToAll("Recon Waypoint Execute")
@@ -3673,7 +3712,6 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- BARRAGE is special!
if Task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
env.info("FF Barrage")
local vec2=self:GetVec2()
local param=Task.dcstask.params
local heading=param.heading or math.random(1, 360)
@@ -4230,7 +4268,7 @@ function OPSGROUP:onafterMissionStart(From, Event, To, Mission)
-- IMMOBILE Group
---
env.info("FF Immobile GROUP")
--env.info("FF Immobile GROUP")
-- Add waypoint task. UpdateRoute is called inside.
local Clock=Mission.Tpush and UTILS.SecondsToClock(Mission.Tpush) or 5
@@ -4580,13 +4618,22 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Refresh DCS task with the known controllable.
mission.DCStask=mission:GetDCSMissionTask(self.group)
-- Create a pickup zone around the pickup coordinate. The troops will go to a random point inside the zone.
-- This is necessary so the helos do not try to land at the exact same location where the troops wait.
local pradius=mission.transportPickupRadius
local pickupZone=ZONE_RADIUS:New("Pickup Zone", mission.transportPickup:GetVec2(), pradius)
-- Add task to embark for the troops.
for _,_group in pairs(mission.transportGroupSet.Set) do
local group=_group --Wrapper.Group#GROUP
if group and group:IsAlive() then
local DCSTask=group:TaskEmbarkToTransport(mission.transportPickup, 500)
-- Get random coordinate inside the zone.
local pcoord=pickupZone:GetRandomCoordinate(20, pradius, {land.SurfaceType.LAND, land.SurfaceType.ROAD})
-- Let the troops embark the transport.
local DCSTask=group:TaskEmbarkToTransport(pcoord, pradius)
group:SetTask(DCSTask, 5)
end
@@ -4901,7 +4948,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
local Coordinate=zone:GetRandomCoordinate(nil, nil, surfacetypes)
-- Speed and altitude.
local Speed=UTILS.MpsToKnots(task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
local Speed=task.dcstask.params.speed and UTILS.MpsToKnots(task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
-- local Speed=UTILS.KmphToKnots(speed or self.speedCruise)
local Altitude=UTILS.MetersToFeet(task.dcstask.params.altitude or self.altitudeCruise)
@@ -4911,7 +4958,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
if self.isFlightgroup then
wp=FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
elseif self.isArmygroup then
wp=ARMYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Formation)
wp=ARMYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, task.dcstask.params.formation)
elseif self.isNavygroup then
wp=NAVYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
end
@@ -4938,8 +4985,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
local Coordinate=zone:GetRandomCoordinate()
-- Speed and altitude.
local Speed=UTILS.MpsToKnots(task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
--local Speed=UTILS.KmphToKnots(task.dcstask.params.speed or self.speedCruise)
local Speed=task.dcstask.params.speed and UTILS.MpsToKnots(task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
local Altitude=task.dcstask.params.altitude and UTILS.MetersToFeet(task.dcstask.params.altitude) or nil
-- Debug.
@@ -5759,6 +5805,38 @@ function OPSGROUP:onafterElementInUtero(From, Event, To, Element)
end
--- On after "ElementDamaged" event.
-- @param #OPSGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #OPSGROUP.Element Element The flight group element.
function OPSGROUP:onafterElementDamaged(From, Event, To, Element)
self:T(self.lid..string.format("Element damaged %s", Element.name))
if Element and (Element.status~=OPSGROUP.ElementStatus.DEAD and Element.status~=OPSGROUP.ElementStatus.INUTERO) then
local lifepoints=0
if Element.DCSunit and Element.DCSunit:isExist() then
-- Get life of unit
lifepoints=Element.DCSunit:getLife()
-- Debug output.
self:T(self.lid..string.format("Element life %s: %.2f/%.2f", Element.name, lifepoints, Element.life0))
end
if lifepoints<=1.0 then
self:T(self.lid..string.format("Element %s life %.2f <= 1.0 ==> Destroyed!", Element.name, lifepoints))
self:ElementDestroyed(Element)
end
end
end
--- On after "ElementDestroyed" event.
-- @param #OPSGROUP self
-- @param #string From From state.
@@ -5793,7 +5871,7 @@ end
function OPSGROUP:onafterElementDead(From, Event, To, Element)
-- Debug info.
self:T(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime()))
self:I(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime()))
-- Set element status.
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
@@ -5890,6 +5968,80 @@ function OPSGROUP:onafterRespawn(From, Event, To, Template)
end
--- Teleport the group to a different location.
-- @param #OPSGROUP self
-- @param Core.Point#COORDINATE Coordinate Coordinate where the group is teleported to.
-- @param #number Delay Delay in seconds before respawn happens. Default 0.
-- @return #OPSGROUP self
function OPSGROUP:Teleport(Coordinate, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, OPSGROUP.Teleport, self, Coordinate)
else
-- Debug message.
self:T(self.lid.."FF Teleporting...")
--Coordinate:MarkToAll("Teleport "..self.groupname)
-- Check if we have a mission running.
if self.currentmission>0 then
self:T(self.lid.."Pausing current mission")
self:PauseMission()
end
-- Get copy of template.
local Template=UTILS.DeepCopy(self.template) --DCS#Template
-- Template units.
local units=Template.units
-- Table with teleported vectors.
local d={}
for i=1,#units do
local unit=units[i]
d[i]={x=Coordinate.x+(units[i].x-units[1].x), y=Coordinate.z+units[i].y-units[1].y}
--COORDINATE:NewFromVec2(d[i]):MarkToAll(unit.name.." teleported")
end
for i=#units,1,-1 do
local unit=units[i]
-- Get element.
local element=self:GetElementByName(unit.name)
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-- No parking.
unit.parking=nil
unit.parking_id=nil
-- Current position.
local vec3=element.unit:GetVec3()
-- Current heading.
local heading=element.unit:GetHeading()
-- Set new x,y.
unit.x=d[i].x
unit.y=d[i].y
-- Set altitude.
unit.alt=Coordinate.y
-- Set heading.
unit.heading=math.rad(heading)
unit.psi=-unit.heading
else
table.remove(units, i)
end
end
-- Respawn from new template.
self:_Respawn(0, Template, true)
end
end
--- Respawn the group.
-- @param #OPSGROUP self
-- @param #number Delay Delay in seconds before respawn happens. Default 0.
@@ -5905,58 +6057,60 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Debug message.
self:T2(self.lid.."FF _Respawn")
-- Given template or get old.
-- Given template or get copy of old.
Template=Template or self:_GetTemplate(true)
-- Number of destroyed units.
self.Ndestroyed=0
-- Check if group is currently alive.
if self:IsAlive() then
---
-- Group is ALIVE
---
--[[
-- Get units.
local units=self.group:GetUnits()
-- Loop over template units.
for UnitID, Unit in pairs(Template.units) do
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
if unit:GetName()==Unit.name then
local vec3=unit:GetVec3()
local heading=unit:GetHeading()
Unit.x=vec3.x
Unit.y=vec3.z
Unit.alt=vec3.y
Unit.heading=math.rad(heading)
Unit.psi=-Unit.heading
end
end
end
]]
-- Template units.
local units=Template.units
for i=#units,1,-1 do
local unit=units[i]
-- Get the element.
local element=self:GetElementByName(unit.name)
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
unit.parking=element.parking and element.parking.TerminalID or unit.parking
unit.parking_id=nil
local vec3=element.unit:GetVec3()
local heading=element.unit:GetHeading()
unit.x=vec3.x
unit.y=vec3.z
unit.alt=vec3.y
unit.heading=math.rad(heading)
unit.psi=-unit.heading
if not Reset then
-- Parking ID.
unit.parking=element.parking and element.parking.TerminalID or unit.parking
unit.parking_id=nil
-- Get current position vector.
local vec3=element.unit:GetVec3()
-- Get heading.
local heading=element.unit:GetHeading()
-- Set unit position.
unit.x=vec3.x
unit.y=vec3.z
unit.alt=vec3.y
-- Set heading in rad.
unit.heading=math.rad(heading)
unit.psi=-unit.heading
end
else
-- Element is dead. Remove from template.
table.remove(units, i)
self.Ndestroyed=self.Ndestroyed+1
end
end
@@ -5967,7 +6121,7 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
else
---
-- Group is DESPAWNED
-- Group is NOT ALIVE
---
-- Ensure elements in utero.
@@ -5997,8 +6151,7 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
self.isDestroyed=false
self.groupinitialized=false
self.Ndestroyed=0
self.groupinitialized=false
self.wpcounter=1
self.currentwp=1
@@ -6007,7 +6160,7 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Init Group.
self:_InitGroup(Template)
-- Reset events.
--self:ResetEvents()
@@ -6074,6 +6227,19 @@ function OPSGROUP:onbeforeDead(From, Event, To)
end
end
--- Cancel all missions in mission queue.
-- @param #OPSGROUP self
function OPSGROUP:CancelAllMissions()
-- Cancel all missions.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
self:T(self.lid.."Cancelling mission "..tostring(mission:GetName()))
self:MissionCancel(mission)
end
end
--- On after "Dead" event.
-- @param #OPSGROUP self
-- @param #string From From state.
@@ -6131,12 +6297,31 @@ function OPSGROUP:onafterDead(From, Event, To)
-- All elements were destroyed ==> Asset group is gone.
self.cohort:DelGroup(self.groupname)
end
if self.legion then
--self.legion:Get
--self.legion:AssetDead()
end
else
-- Not all assets were destroyed (despawn) ==> Add asset back to legion?
end
-- Stop in a sec.
--self:__Stop(-5)
if self.legion then
if not self:IsInUtero() then
-- Get asset.
local asset=self.legion:GetAssetByName(self.groupname)
-- Get request.
local request=self.legion:GetRequestByID(asset.rid)
-- Trigger asset dead event.
self.legion:AssetDead(asset, request)
end
-- Stop in 5 sec to give possible respawn attempts a chance.
self:__Stop(-5)
end
end
--- On before "Stop" event.
@@ -6178,6 +6363,11 @@ function OPSGROUP:onafterStop(From, Event, To)
self:UnHandleEvent(EVENTS.Crash)
self.currbase=nil
end
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
self:MissionCancel(mission)
end
-- Stop check timers.
self.timerCheckZone:Stop()
@@ -7993,6 +8183,12 @@ end
-- @param #OPSGROUP OpsGroupCargo Cargo OPSGROUP that was unloaded from a carrier.
function OPSGROUP:onafterUnloaded(From, Event, To, OpsGroupCargo)
self:T(self.lid..string.format("Unloaded OPSGROUP %s", OpsGroupCargo:GetName()))
if OpsGroupCargo.legion and OpsGroupCargo:IsInZone(OpsGroupCargo.legion.spawnzone) then
self:T(self.lid..string.format("Unloaded group %s returned to legion", OpsGroupCargo:GetName()))
OpsGroupCargo:Returned()
end
end
@@ -8530,18 +8726,24 @@ function OPSGROUP:_CheckGroupDone(delay)
return
end
-- Group is returning
-- Group is returning.
if self:IsReturning() then
self:T(self.lid.."Returning! Group NOT done...")
return
end
-- Group is returning
-- Group is rearming.
if self:IsRearming() then
self:T(self.lid.."Rearming! Group NOT done...")
return
end
-- Group is retreating.
if self:IsRetreating() then
self:T(self.lid.."Retreating! Group NOT done...")
return
end
-- Group is waiting. We deny all updates.
if self:IsWaiting() then
-- If group is waiting, we assume that is the way it is meant to be.
+29 -12
View File
@@ -57,6 +57,8 @@
-- @field #table statusLegion Transport status of all assigned LEGIONs.
-- @field #string statusCommander Staus of the COMMANDER.
-- @field Ops.Commander#COMMANDER commander Commander of the transport.
-- @field Ops.Chief#CHIEF chief Chief of the transport.
-- @field Ops.OpsZone#OPSZONE opszone OPS zone.
-- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the transport was cancelled before the request is processed.
--
-- @extends Core.Fsm#FSM
@@ -1305,7 +1307,7 @@ function OPSTRANSPORT:GetNcarrier()
return self.Ncarrier
end
--- Add asset to transport.
--- Add carrier asset to transport.
-- @param #OPSTRANSPORT self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be added.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
@@ -1323,7 +1325,7 @@ function OPSTRANSPORT:AddAsset(Asset, TransportZoneCombo)
return self
end
--- Delete asset from mission.
--- Delete carrier asset from transport.
-- @param #OPSTRANSPORT self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be removed.
-- @return #OPSTRANSPORT self
@@ -1736,12 +1738,12 @@ function OPSTRANSPORT:onafterDeadCarrierGroup(From, Event, To, OpsGroup)
-- Increase dead counter.
self.NcarrierDead=self.NcarrierDead+1
if #self.carriers==0 then
self:DeadCarrierAll()
end
-- Remove group from carrier list/table.
self:_DelCarrier(OpsGroup)
if #self.carriers==0 then
self:DeadCarrierAll()
end
end
--- On after "DeadCarrierAll" event.
@@ -1750,15 +1752,30 @@ end
-- @param #string Event Event.
-- @param #string To To state.
function OPSTRANSPORT:onafterDeadCarrierAll(From, Event, To)
self:I(self.lid..string.format("ALL Carrier OPSGROUPs are dead! Setting stage to PLANNED if not all cargo was delivered."))
self:I(self.lid..string.format("ALL Carrier OPSGROUPs are dead!"))
if self.opszone then
self:I(self.lid..string.format("Cancelling transport on CHIEF level"))
self.chief:TransportCancel(self)
--for _,_legion in pairs(self.legions) do
-- local legion=_legion --Ops.Legion#LEGION
-- legion:TransportCancel(self)
--end
else
-- Check if cargo was delivered.
self:_CheckDelivered()
-- Check if cargo was delivered.
self:_CheckDelivered()
-- Set state back to PLANNED if not delivered.
if not self:IsDelivered() then
self:Planned()
end
-- Set state back to PLANNED if not delivered.
if not self:IsDelivered() then
self:Planned()
end
end
--- On after "Cancel" event.