From eed41a121c305fb4d7b3e35027746af367b71ecc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 8 Sep 2022 11:57:02 +0200 Subject: [PATCH 001/603] #AUTOLASE * Added option for blacklist of attributes * Added radar unit higher priority over launchers * Removed the "#001-01" appendix on message output for spawned Recce --- .../Moose/Functional/Autolase.lua | 76 +++++++++++++++++-- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index ce2155f44..994a760ba 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -111,7 +111,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.1.15" +AUTOLASE.version = "0.1.20" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -195,6 +195,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self.SRSMod = radio.modulation.AM self.NoMenus = false self.minthreatlevel = 0 + self.blacklistattributes = {} -- Set some string id for output to DCS.log file. self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -307,7 +308,7 @@ function AUTOLASE:SetPilotMenu() local Unit = _unit -- Wrapper.Unit#UNIT if Unit and Unit:IsAlive() then local Group = Unit:GetGroup() - local lasemenu = MENU_GROUP_COMMAND:New(Group,"Autolase Status",nil,self.ShowStatus,self,Group) + local lasemenu = MENU_GROUP_COMMAND:New(Group,"Autolase Status",nil,self.ShowStatus,self,Group,Unit) lasemenu:Refresh() end end @@ -341,6 +342,29 @@ function AUTOLASE:SetMinThreatLevel(Level) return self end +--- (User) Set list of #UNIT level attributes that won't be lased. For list of attributes see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_enum_attributes) and [GitHub](https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB) +-- @param #AUTOLASE self +-- @param #table Attributes Table of #string attributes to blacklist. Can be handed over as a single #string. +-- @return #AUTOLASE self +-- @usage To exclude e.g. manpads from being lased: +-- +-- `myautolase:AddBlackListAttributes("MANPADS")` +-- +-- To exclude trucks and artillery: +-- +-- `myautolase:AddBlackListAttributes({"Trucks","Artillery"})` +-- +function AUTOLASE:AddBlackListAttributes(Attributes) + local attributes = Attributes + if type(attributes) ~= "table" then + attributes = {attributes} + end + for _,_attr in pairs(attributes) do + table.insert(self.blacklistattributes,_attr) + end + return self +end + --- (Internal) Function to get a laser code by recce name -- @param #AUTOLASE self -- @param #string RecceName Unit(!) name of the Recce @@ -613,15 +637,19 @@ end --- (Internal) Function to show status. -- @param #AUTOLASE self -- @param Wrapper.Group#GROUP Group (Optional) show to a certain group +-- @param Wrapper.Unit#UNIT Unit (Optional) show to a certain unit -- @return #AUTOLASE self -function AUTOLASE:ShowStatus(Group) +function AUTOLASE:ShowStatus(Group,Unit) local report = REPORT:New("Autolase") local reccetable = self.RecceSet:GetSetObjects() for _,_recce in pairs(reccetable) do if _recce and _recce:IsAlive() then local unit = _recce:GetUnit(1) local name = unit:GetName() - local code = self:GetLaserCode(name) + if string.find(name,"#") then + name = string.match(name,"^(.*)#") + end + local code = self:GetLaserCode(unit:GetName()) report:Add(string.format("Recce %s has code %d",name,code)) end end @@ -629,10 +657,18 @@ function AUTOLASE:ShowStatus(Group) for _ind,_entry in pairs(self.CurrentLasing) do local entry = _entry -- #AUTOLASE.LaserSpot local reccename = entry.reccename + if string.find(reccename,"#") then + reccename = string.match(reccename,"^(.*)#") + end local typename = entry.unittype local code = entry.lasercode local locationstring = entry.location - local playername = Group:GetPlayerName() + local playername = nil + if Unit and Unit:IsAlive() then + playername = Unit:GetPlayerName() + elseif Group and Group:IsAlive() then + playername = Group:GetPlayerName() + end if playername then local settings = _DATABASE:GetPlayerSettings(playername) if settings then @@ -652,7 +688,9 @@ function AUTOLASE:ShowStatus(Group) end local reporttime = self.reporttimelong if lines == 0 then reporttime = self.reporttimeshort end - if Group and Group:IsAlive() then + if Unit and Unit:IsAlive() then + local m = MESSAGE:New(report:Text(),reporttime,"Info"):ToUnit(Unit) + elseif Group and Group:IsAlive() then local m = MESSAGE:New(report:Text(),reporttime,"Info"):ToGroup(Group) else local m = MESSAGE:New(report:Text(),reporttime,"Info"):ToCoalition(self.coalition) @@ -724,6 +762,20 @@ end -- @param Wrapper.Unit#UNIT Unit The lased #UNIT -- @return #boolean outcome True or false function AUTOLASE:CanLase(Recce,Unit) + + local function HasNoBlackListAttribute(Unit) + local nogos = self.blacklistattributes or {} + local having = true + local unit = Unit -- Wrapper.Unit#UNIT + for _,_attribute in pairs (nogos) do + if unit:HasAttribute(_attribute) then + having = false + break + end + end + return having + end + local canlase = false -- cooldown? if Recce and Recce:IsAlive() == true then @@ -744,7 +796,7 @@ function AUTOLASE:CanLase(Recce,Unit) -- calculate distance local distance = math.floor(reccecoord:Get3DDistance(unitcoord)) local lasedistance = self:GetLosFromUnit(Recce) - if distance <= lasedistance and islos then + if distance <= lasedistance and islos and HasNoBlackListAttribute(Unit) then canlase = true end end @@ -836,6 +888,10 @@ function AUTOLASE:onafterMonitor(From, Event, To) local coord = unit:GetCoordinate() if threat > 0 then local unitname = unit:GetName() + -- prefer radar units + if unit:HasAttribute("RADAR_BAND1_FOR_ARM") or unit:HasAttribute("RADAR_BAND2_FOR_ARM") or unit:HasAttribute("Optical Tracker") then + threat = 11 + end table.insert(unitsbythreat,{unit,threat}) self.RecceUnitNames[unitname] = reccename end @@ -1001,7 +1057,11 @@ function AUTOLASE:onbeforeLasing(From,Event,To,LaserSpot) self:T({From, Event, To, LaserSpot.unittype}) if self.notifypilots or self.debug then local laserspot = LaserSpot -- #AUTOLASE.LaserSpot - local text = string.format("%s is lasing %s code %d\nat %s",laserspot.reccename,laserspot.unittype,laserspot.lasercode,laserspot.location) + local name = laserspot.reccename + if string.find(name,"#") then + name = string.match(name,"^(.*)#") + end + local text = string.format("%s is lasing %s code %d\nat %s",name,laserspot.unittype,laserspot.lasercode,laserspot.location) self:NotifyPilots(text,self.reporttimeshort+5) end return self From 31cd125954e0072ca44ac0fb4ef47cf2afacb5a9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 8 Sep 2022 15:51:07 +0200 Subject: [PATCH 002/603] #GROUP, #UNIT * Improve GetAmmunition() to include artillery shells --- Moose Development/Moose/Wrapper/Group.lua | 10 ++++++---- Moose Development/Moose/Wrapper/Unit.lua | 10 ++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 7194afd61..d3495fac7 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1249,7 +1249,8 @@ end -- @return #number Number of shells left. -- @return #number Number of rockets left. -- @return #number Number of bombs left. --- @return #number Number of missiles left. +-- @return #number Number of missiles left. +-- @return #number Number of artillery shells left (with explosive mass, included in shells; shells can also be machine gun ammo) function GROUP:GetAmmunition() self:F( self.ControllableName ) @@ -1260,6 +1261,7 @@ function GROUP:GetAmmunition() local Nrockets=0 local Nmissiles=0 local Nbombs=0 + local Narti=0 if DCSControllable then @@ -1268,19 +1270,19 @@ function GROUP:GetAmmunition() local Unit = UnitData -- Wrapper.Unit#UNIT -- Get ammo of the unit - local ntot, nshells, nrockets, nbombs, nmissiles = Unit:GetAmmunition() + local ntot, nshells, nrockets, nbombs, nmissiles, narti = Unit:GetAmmunition() Ntot=Ntot+ntot Nshells=Nshells+nshells Nrockets=Nrockets+nrockets Nmissiles=Nmissiles+nmissiles Nbombs=Nbombs+nbombs - + Narti=Narti+narti end end - return Ntot, Nshells, Nrockets, Nbombs, Nmissiles + return Ntot, Nshells, Nrockets, Nbombs, Nmissiles, Narti end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index d72c0f96a..6195f03e5 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -698,6 +698,7 @@ end -- @return #number Number of rockets left. -- @return #number Number of bombs left. -- @return #number Number of missiles left. +-- @return #number Number of artillery shells left (with explosive mass, included in shells; shells can also be machine gun ammo) function UNIT:GetAmmunition() -- Init counter. @@ -706,6 +707,7 @@ function UNIT:GetAmmunition() local nrockets=0 local nmissiles=0 local nbombs=0 + local narti=0 local unit=self @@ -742,7 +744,11 @@ function UNIT:GetAmmunition() -- Add up all shells. nshells=nshells+Nammo - + + if ammotable[w].desc.warhead and ammotable[w].desc.warhead.explosiveMass and ammotable[w].desc.warhead.explosiveMass > 0 then + narti=narti+Nammo + end + elseif Category==Weapon.Category.ROCKET then -- Add up all rockets. @@ -779,7 +785,7 @@ function UNIT:GetAmmunition() -- Total amount of ammunition. nammo=nshells+nrockets+nmissiles+nbombs - return nammo, nshells, nrockets, nbombs, nmissiles + return nammo, nshells, nrockets, nbombs, nmissiles, narti end --- Returns the unit sensors. From 34b80727a2333f777f3c5fa41d27ee4a3e09ef69 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 8 Sep 2022 15:54:22 +0200 Subject: [PATCH 003/603] #AMMOTRUCK * Initial Release --- .../Moose/Functional/AmmoTruck.lua | 681 ++++++++++++++++++ Moose Development/Moose/Modules.lua | 1 + 2 files changed, 682 insertions(+) create mode 100644 Moose Development/Moose/Functional/AmmoTruck.lua diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua new file mode 100644 index 000000000..0d697e927 --- /dev/null +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -0,0 +1,681 @@ +--- **Functional** -- Send a truck to supply artillery groups. +-- +-- === +-- +-- **AMMOTRUCK** - Send a truck to supply artillery groups. +-- +-- === +-- +-- ## Missions: +-- +-- ### [tbd](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/tbd) +-- +-- === +-- +-- ### Author : **applevangelist ** +-- +-- @module Functional.AmmoTruck +-- @image Functional.AmmoTruck.jpg +-- +-- Date: Sep 2022 + +------------------------------------------------------------------------- +--- **AMMOTRUCK** class, extends Core.FSM#FSM +-- @type AMMOTRUCK +-- @field #string ClassName Class Name +-- @field #string lid Lid for log entries +-- @field #string version Version string +-- @field #string alias Alias name +-- @field #boolean debug Debug flag +-- @field #table trucklist List of (alive) #AMMOTRUCK.data trucks +-- @field #table targetlist List of (alive) #AMMOTRUCK.data artillery +-- @field #number coalition Coalition this is for +-- @field Core.Set#SET_GROUP truckset SET of trucks +-- @field Core.Set#SET_GROUP targetset SET of artillery +-- @field #table remunitionqueue List of (alive) #AMMOTRUCK.data artillery to be reloaded +-- @field #table waitingtargets List of (alive) #AMMOTRUCK.data artillery waiting +-- @field #number ammothreshold Threshold (min) ammo before sending a truck +-- @field #number remunidist Max distance trucks will go +-- @field #number monitor Monitor interval in seconds +-- @field #number unloadtime Unload time in seconds +-- @field #number waitingtime Max waiting time in seconds +-- @field #boolean routeonroad Route truck on road if true (default) +-- @extends Core.FSM#FSM + +--- *Amateurs talk about tactics, but professionals study logistics.* - Gen. Robert H. Barrow, USMC +-- +-- Simple Class to re-arm your artillery with trucks. +-- +-- #AMMOTRUCK +-- +-- * Controls a SET\_GROUP of trucks which will re-arm a SET\_GROUP of artillery groups when they run out of ammunition. +-- +-- ## 1 The AMMOTRUCK concept +-- +-- A SET\_GROUP of trucks which will re-arm a SET\_GROUP of artillery groups when they run out of ammunition. They will be based on a +-- homebase and drive from there to the artillery groups and then back home. +-- Trucks are the **only known in-game mechanic** to re-arm artillery and other units in DCS. Working units are e.g.: M-939 (blue), Ural-375 and ZIL-135 (both red). +-- +-- ## 2 Set-up +-- +-- Define a set of trucks and a set of artillery: +-- +-- local truckset = SET_GROUP:New():FilterCoalitions("blue"):FilterActive(true):FilterCategoryGround():FilterPrefixes("Ammo Truck"):FilterStart() +-- local ariset = SET_GROUP:New():FilterCoalitions("blue"):FilterActive(true):FilterCategoryGround():FilterPrefixes("Artillery"):FilterStart() +-- +-- Create an AMMOTRUCK object to take care of the artillery using the trucks, with a homezone: +-- +-- local ammotruck = AMMOTRUCK:New(truckset,ariset,coalition.side.BLUE,"Logistics",ZONE:FindByName("HomeZone") +-- +-- ## 2 Options +-- +-- ammotruck.ammothreshold = 5 -- send a truck when down to this many rounds +-- ammotruck.remunidist = 20000 -- 20km - send trucks max this far from home +-- ammotruck.unloadtime = 600 -- 10 minutes - min time to unload ammunition +-- ammotruck.waitingtime = 1800 -- 30 mintes - wait max this long until remunition is done +-- ammotruck.monitor = -60 - 1 minute - AMMOTRUCK checks on things every 1 minute +-- ammotruck.routeonroad = true - Trucks will **try** to drive on roads +-- +-- ## 3 FSM Events to shape mission +-- +-- Truck has been sent off: +-- +-- function ammotruck:OnAfterRouteTruck(From, Event, To, Truckdata, Aridata) +-- ... +-- end +-- +-- Truck has arrived: +-- +-- function ammotruck:OnAfterTruckArrived(From, Event, To, Truckdata) +-- ... +-- end +-- +-- Truck is unloading: +-- +-- function ammotruck:OnAfterTruckUnloading(From, Event, To, Truckdata) +-- ... +-- end +-- +-- Truck is returning home: +-- +-- function ammotruck:OnAfterTruckReturning(From, Event, To, Truckdata) +-- ... +-- end +-- +-- Truck is arrived at home: +-- +-- function ammotruck:OnAfterTruckHome(From, Event, To, Truckdata) +-- ... +-- end +-- +-- @field #AMMOTRUCK +AMMOTRUCK = { + ClassName = "AMMOTRUCK", + lid = "", + version = "0.0.1", + alias = "", + debug = false, + trucklist = {}, + targetlist = {}, + coalition = nil, + truckset = nil, + targetset = nil, + remunitionqueue = {}, + waitingtargets = {}, + ammothreshold = 5, + remunidist = 20000, + monitor = -60, + unloadtime = 600, + waitingtime = 1800, + routeonroad = true +} + +--- +-- @type AMMOTRUCK.State +AMMOTRUCK.State = { + IDLE = "idle", + DRIVING = "driving", + ARRIVED = "arrived", + UNLOADING = "unloading", + RETURNING = "returning", + WAITING = "waiting", + RELOADING = "reloading", + OUTOFAMMO = "outofammo", + REQUESTED = "requested", +} + +--- +--@type AMMOTRUCK.data +--@field Wrapper.Group#GROUP group +--@field #string name +--@field #AMMOTRUCK.State state +--@field #number timestamp +--@field #number ammo +--@field Core.Point#COORDINATE coordinate +--@field #string targetname +--@field Wrapper.Group#GROUP targetgroup +--@field Core.Point#COORDINATE targetcoordinate + +--- +-- @param #AMMOTRUCK self +-- @param Core.Set#SET_GROUP Truckset Set of truck groups +-- @param Core.Set#SET_GROUP Targetset Set of artillery groups +-- @param #number Coalition Coalition +-- @param #string Alias Alias Name +-- @param Core.Zone#ZONE Homezone Home, return zone for trucks +-- @return #AMMOTRUCK self +-- @usage +-- Define a set of trucks and a set of artillery: +-- local truckset = SET_GROUP:New():FilterCoalitions("blue"):FilterActive(true):FilterCategoryGround():FilterPrefixes("Ammo Truck"):FilterStart() +-- local ariset = SET_GROUP:New():FilterCoalitions("blue"):FilterActive(true):FilterCategoryGround():FilterPrefixes("Artillery"):FilterStart() +-- +-- Create an AMMOTRUCK object to take care of the artillery using the trucks, with a homezone: +-- local ammotruck = AMMOTRUCK:New(truckset,ariset,coalition.side.BLUE,"Logistics",ZONE:FindByName("HomeZone") +function AMMOTRUCK:New(Truckset,Targetset,Coalition,Alias,Homezone) + + -- Inherit everything from BASE class. + local self=BASE:Inherit(self, FSM:New()) -- #AMMOTRUCK + + self.truckset = Truckset -- Core.Set#SET_GROUP + self.targetset = Targetset -- Core.Set#SET_GROUP + self.coalition = Coalition -- #number + self.alias = Alias -- #string + self.debug = false + self.remunitionqueue = {} + self.trucklist = {} + self.targetlist = {} + self.ammothreshold = 5 + self.remunidist = 20000 + self.homezone = Homezone -- Core.Zone#ZONE + self.waitingtime = 1800 + + -- Log id. + self.lid=string.format("AMMOTRUCK %s | %s | ", self.version, self.alias) + + self:SetStartState("Stopped") + self:AddTransition("Stopped", "Start", "Running") + self:AddTransition("*", "Monitor", "*") + self:AddTransition("*", "RouteTruck", "*") + self:AddTransition("*", "TruckArrived", "*") + self:AddTransition("*", "TruckUnloading", "*") + self:AddTransition("*", "TruckReturning", "*") + self:AddTransition("*", "TruckHome", "*") + self:AddTransition("*", "Stop", "Stopped") + + self:__Start(math.random(5,10)) + + self:I(self.lid .. "Started") + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param #table dataset table of #AMMOTRUCK.data entries +-- @return #AMMOTRUCK self +function AMMOTRUCK:CheckDrivingTrucks(dataset) + self:T(self.lid .. " CheckDrivingTrucks") + local data = dataset + for _,_data in pairs (data) do + local truck = _data -- #AMMOTRUCK.data + -- see if we arrived at destination + local coord = truck.group:GetCoordinate() + local tgtcoord = truck.targetcoordinate + local dist = coord:Get2DDistance(tgtcoord) + if dist <= 150 then + -- arrived + truck.state = AMMOTRUCK.State.ARRIVED + truck.timestamp = timer.getAbsTime() + truck.coordinate = coord + self:__TruckArrived(1,truck) + end + end + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param Wrapper.Group#GROUP Group +-- @return #AMMOTRUCK self +function AMMOTRUCK:GetAmmoStatus(Group) + local ammotot, shells, rockets, bombs, missiles, narti = Group:GetAmmunition() + --self:I({ammotot, shells, rockets, bombs, missiles, narti}) + return rockets+missiles+narti +end + +--- +-- @param #AMMOTRUCK self +-- @param #table dataset table of #AMMOTRUCK.data entries +-- @return #AMMOTRUCK self +function AMMOTRUCK:CheckWaitingTargets(dataset) + self:T(self.lid .. " CheckWaitingTargets") + local data = dataset + for _,_data in pairs (data) do + local truck = _data -- #AMMOTRUCK.data + -- see how long we're waiting - maybe ammo truck is dead? + local Tnow = timer.getAbsTime() + local Tdiff = Tnow - truck.timestamp + if Tdiff > self.waitingtime then + local hasammo = self:GetAmmoStatus(truck.group) + --if truck.group:GetAmmunition() <= self.ammothreshold then + if hasammo <= self.ammothreshold then + truck.state = AMMOTRUCK.State.OUTOFAMMO + else + truck.state = AMMOTRUCK.State.IDLE + end + end + end + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param #table dataset table of #AMMOTRUCK.data entries +-- @return #AMMOTRUCK self +function AMMOTRUCK:CheckReturningTrucks(dataset) + self:T(self.lid .. " CheckReturningTrucks") + local data = dataset + local tgtcoord = self.homezone:GetCoordinate() + local radius = self.homezone:GetRadius() + for _,_data in pairs (data) do + local truck = _data -- #AMMOTRUCK.data + -- see if we arrived at destination + local coord = truck.group:GetCoordinate() + local dist = coord:Get2DDistance(tgtcoord) + self:T({name=truck.name,radius=radius,distance=dist}) + if dist <= radius then + -- arrived + truck.state = AMMOTRUCK.State.IDLE + truck.timestamp = timer.getAbsTime() + truck.coordinate = coord + self:__TruckHome(1,truck) + end + end + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param #string name Artillery group name to find +-- @return #AMMOTRUCK.data Data +function AMMOTRUCK:FindTarget(name) + self:T(self.lid .. " FindTarget") + local data = nil + local dataset = self.targetlist + for _,_entry in pairs(dataset) do + local entry = _entry -- #AMMOTRUCK.data + if entry.name == name then + data = entry + break + end + end + return data +end + +--- +-- @param #AMMOTRUCK self +-- @param #string name Truck group name to find +-- @return #AMMOTRUCK.data Data +function AMMOTRUCK:FindTruck(name) + self:T(self.lid .. " FindTruck") + local data = nil + local dataset = self.trucklist + for _,_entry in pairs(dataset) do + local entry = _entry -- #AMMOTRUCK.data + if entry.name == name then + data = entry + break + end + end + return data +end + +--- +-- @param #AMMOTRUCK self +-- @param #table dataset table of #AMMOTRUCK.data entries +-- @return #AMMOTRUCK self +function AMMOTRUCK:CheckArrivedTrucks(dataset) + self:T(self.lid .. " CheckArrivedTrucks") + local data = dataset + for _,_data in pairs (data) do + -- set to unloading + local truck = _data -- #AMMOTRUCK.data + truck.state = AMMOTRUCK.State.UNLOADING + truck.timestamp = timer.getAbsTime() + self:__TruckUnloading(2,truck) + -- set target to reloading + local aridata = self:FindTarget(truck.targetname) -- #AMMOTRUCK.data + if aridata then + aridata.state = AMMOTRUCK.State.RELOADING + aridata.timestamp = timer.getAbsTime() + end + end + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param #table dataset table of #AMMOTRUCK.data entries +-- @return #AMMOTRUCK self +function AMMOTRUCK:CheckUnloadingTrucks(dataset) + self:T(self.lid .. " CheckUnloadingTrucks") + local data = dataset + for _,_data in pairs (data) do + -- check timestamp + local truck = _data -- #AMMOTRUCK.data + local Tnow = timer.getAbsTime() + local Tpassed = Tnow - truck.timestamp + local hasammo = self:GetAmmoStatus(truck.targetgroup) + --local ammostate = truck.targetgroup:GetAmmunition() + if Tpassed > self.unloadtime and hasammo > self.ammothreshold then + truck.state = AMMOTRUCK.State.RETURNING + truck.timestamp = timer.getAbsTime() + self:__TruckReturning(2,truck) + -- set target to reloaded + local aridata = self:FindTarget(truck.targetname) -- #AMMOTRUCK.data + if aridata then + aridata.state = AMMOTRUCK.State.IDLE + aridata.timestamp = timer.getAbsTime() + end + end + end + return self +end + +--- +-- @param #AMMOTRUCK self +-- @return #AMMOTRUCK self +function AMMOTRUCK:CheckTargetsAlive() + self:T(self.lid .. " CheckTargetsAlive") + local arilist = self.targetlist + for _,_ari in pairs(arilist) do + local ari = _ari -- #AMMOTRUCK.data + if ari.group and ari.group:IsAlive() then + -- everything fine + else + -- ari dead + self.targetlist[ari.name] = nil + end + end + -- new arrivals? + local aritable = self.targetset:GetSetObjects() --#table + for _,_ari in pairs(aritable) do + local ari = _ari -- Wrapper.Group#GROUP + if ari and ari:IsAlive() and not self.targetlist[ari:GetName()] then + local name = ari:GetName() + local newari = {} -- #AMMOTRUCK.data + newari.name = name + newari.group = ari + newari.state = AMMOTRUCK.State.IDLE + newari.timestamp = timer.getAbsTime() + newari.coordinate = ari:GetCoordinate() + local hasammo = self:GetAmmoStatus(ari) + --newari.ammo = ari:GetAmmunition() + newari.ammo = hasammo + self.targetlist[name] = newari + end + end + return self +end + +--- +-- @param #AMMOTRUCK self +-- @return #AMMOTRUCK self +function AMMOTRUCK:CheckTrucksAlive() + self:T(self.lid .. " CheckTrucksAlive") + local trucklist = self.trucklist + for _,_truck in pairs(trucklist) do + local truck = _truck -- #AMMOTRUCK.data + if truck.group and truck.group:IsAlive() then + -- everything fine + else + -- truck dead + local tgtname = truck.targetname + local targetdata = self:FindTarget(tgtname) -- #AMMOTRUCK.data + if targetdata then + if targetdata.state ~= AMMOTRUCK.State.IDLE then + targetdata.state = AMMOTRUCK.State.IDLE + end + end + self.trucklist[truck.name] = nil + end + end + -- new arrivals? + local trucktable = self.truckset:GetSetObjects() --#table + for _,_truck in pairs(trucktable) do + local truck = _truck -- Wrapper.Group#GROUP + if truck and truck:IsAlive() and not self.trucklist[truck:GetName()] then + local name = truck:GetName() + local newtruck = {} -- #AMMOTRUCK.data + newtruck.name = name + newtruck.group = truck + newtruck.state = AMMOTRUCK.State.IDLE + newtruck.timestamp = timer.getAbsTime() + newtruck.coordinate = truck:GetCoordinate() + self.trucklist[name] = newtruck + end + end + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #AMMOTRUCK self +function AMMOTRUCK:onafterStart(From, Event, To) + self:T({From, Event, To}) + self:CheckTargetsAlive() + self:CheckTrucksAlive() + self:__Monitor(-30) + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #AMMOTRUCK self +function AMMOTRUCK:onafterMonitor(From, Event, To) + self:T({From, Event, To}) + self:CheckTargetsAlive() + self:CheckTrucksAlive() + -- update ammo state + local remunition = false + local remunitionqueue = {} + local waitingtargets = {} + for _,_ari in pairs(self.targetlist) do + local data = _ari -- #AMMOTRUCK.data + if data.group and data.group:IsAlive() then + --data.ammo = data.group:GetAmmunition() + data.ammo = self:GetAmmoStatus(data.group) + data.timestamp = timer.getAbsTime() + local text = string.format("Ari %s | Ammo %d | State %s",data.name,data.ammo,data.state) + self:T(text) + if data.ammo <= self.ammothreshold and (data.state == AMMOTRUCK.State.IDLE or data.state == AMMOTRUCK.State.OUTOFAMMO) then + -- add to remu queue + data.state = AMMOTRUCK.State.OUTOFAMMO + remunitionqueue[#remunitionqueue+1] = data + remunition = true + elseif data.state == AMMOTRUCK.State.WAITING then + waitingtargets[#waitingtargets+1] = data + end + else + self.targetlist[data.name] = nil + end + end + -- sort trucks in buckets + local idletrucks = {} + local drivingtrucks = {} + local unloadingtrucks = {} + local arrivedtrucks = {} + local returningtrucks = {} + local found = false + for _,_truckdata in pairs(self.trucklist) do + local data = _truckdata -- #AMMOTRUCK.data + if data.group and data.group:IsAlive() then + -- check state + local text = string.format("Truck %s | State %s",data.name,data.state) + self:T(text) + if data.state == AMMOTRUCK.State.IDLE then + idletrucks[#idletrucks+1] = data + found = true + elseif data.state == AMMOTRUCK.State.DRIVING then + drivingtrucks[#drivingtrucks+1] = data + elseif data.state == AMMOTRUCK.State.ARRIVED then + arrivedtrucks[#arrivedtrucks+1] = data + elseif data.state == AMMOTRUCK.State.UNLOADING then + unloadingtrucks[#unloadingtrucks+1] = data + elseif data.state == AMMOTRUCK.State.RETURNING then + returningtrucks[#returningtrucks+1] = data + idletrucks[#idletrucks+1] = data + found = true + end + else + self.truckset[data.name] = nil + end + end + -- see if we can/need route one + local n=0 + if found and remunition then + -- match + local match = false + for _,_truckdata in pairs(idletrucks) do + local truckdata = _truckdata -- #AMMOTRUCK.data + local truckcoord = truckdata.group:GetCoordinate() -- Core.Point#COORDINATE + for _,_aridata in pairs(remunitionqueue) do + local aridata = _aridata -- #AMMOTRUCK.data + local aricoord = aridata.coordinate + local distance = truckcoord:Get2DDistance(aricoord) + if distance <= self.remunidist and aridata.state == AMMOTRUCK.State.OUTOFAMMO and n <= #idletrucks then + n = n + 1 + aridata.state = AMMOTRUCK.State.REQUESTED + self:__RouteTruck(n*5,truckdata,aridata) + break + end + end + end + end + + -- check driving trucks + if #drivingtrucks > 0 then + self:CheckDrivingTrucks(drivingtrucks) + end + + -- check arrived trucks + if #arrivedtrucks > 0 then + self:CheckArrivedTrucks(arrivedtrucks) + end + + -- check unloading trucks + if #unloadingtrucks > 0 then + self:CheckUnloadingTrucks(unloadingtrucks) + end + + -- check returningtrucks trucks + if #returningtrucks > 0 then + self:CheckReturningTrucks(returningtrucks) + end + + -- check waiting targets + if #waitingtargets > 0 then + self:CheckWaitingTargets(waitingtargets) + end + + self:__Monitor(self.monitor) + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #AMMOTRUCK.data Truckdata +-- @param #AMMOTRUCK.data Aridata +-- @return #AMMOTRUCK self +function AMMOTRUCK:onafterRouteTruck(From, Event, To, Truckdata, Aridata) + self:T({From, Event, To, Truckdata.name, Aridata.name}) + local truckdata = Truckdata -- #AMMOTRUCK.data + local aridata = Aridata -- #AMMOTRUCK.data + local tgtgrp = aridata.group + local tgtzone = ZONE_GROUP:New(aridata.name,tgtgrp,30) + local tgtcoord = tgtzone:GetRandomCoordinate(15) + if self.routeonroad then + truckdata.group:RouteGroundOnRoad(tgtcoord,30,1,"Cone") + else + truckdata.group:RouteGroundTo(tgtcoord,30,"Cone",1) + end + truckdata.state = AMMOTRUCK.State.DRIVING + truckdata.targetgroup = tgtgrp + truckdata.targetname = aridata.name + truckdata.targetcoordinate = tgtcoord + aridata.state = AMMOTRUCK.State.WAITING + aridata.timestamp = timer.getAbsTime() + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #AMMOTRUCK.data Truckdata +-- @return #AMMOTRUCK self + function AMMOTRUCK:onafterTruckUnloading(From, Event, To, Truckdata) + local m = MESSAGE:New("Truck "..Truckdata.name.." unloading!",15,"AmmoTruck"):ToCoalitionIf(self.coalition,self.debug) + local truck = Truckdata -- Functional.AmmoTruck#AMMOTRUCK.data + local coord = truck.group:GetCoordinate() + local heading = truck.group:GetHeading() + heading = heading < 180 and (360-heading) or (heading - 180) + local cid = self.coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA + cid = self.coalition == coalition.side.NEUTRAL and country.id.UN_PEACEKEEPERS or cid + local ammo = {} + for i=1,5 do + ammo[i] = SPAWNSTATIC:NewFromType("ammo_cargo","Cargos",cid) + :InitCoordinate(coord:Translate((15+((i-1)*4)),heading)) + :Spawn(0,"AmmoCrate-"..math.random(1,10000)) + end + + local function destroyammo(ammo) + for _,_crate in pairs(ammo) do + _crate:Destroy(false) + end + end + + local scheduler = SCHEDULER:New(nil,destroyammo,{ammo},self.waitingtime) + end + +--- +-- @param #AMMOTRUCK self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #AMMOTRUCK.data Truck +-- @return #AMMOTRUCK self +function AMMOTRUCK:onafterTruckReturning(From, Event, To, Truck) + self:T({From, Event, To, Truck.name}) + -- route home + local truckdata = Truck -- #AMMOTRUCK.data + local tgtzone = self.homezone + local tgtcoord = tgtzone:GetRandomCoordinate() + if self.routeonroad then + truckdata.group:RouteGroundOnRoad(tgtcoord,30,1,"Cone") + else + truckdata.group:RouteGroundTo(tgtcoord,30,"Cone",1) + end + return self +end + +--- +-- @param #AMMOTRUCK self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #AMMOTRUCK self +function AMMOTRUCK:onafterStop(From, Event, To) + self:T({From, Event, To}) + return self +end + diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 6b9307f67..37bc6f187 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -76,6 +76,7 @@ __Moose.Include( 'Scripts/Moose/Functional/Mantis.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Shorad.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Autolase.lua' ) __Moose.Include( 'Scripts/Moose/Functional/AICSAR.lua' ) +__Moose.Include( 'Scripts/Moose/Functional/AmmoTruck.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' ) __Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' ) From 949e1fc013f36b70cd69424035a47403948e644a Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Thu, 8 Sep 2022 15:56:45 +0200 Subject: [PATCH 004/603] Update Moose.files --- Moose Setup/Moose.files | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 4eea18a66..d73ef4c1e 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -74,6 +74,7 @@ Functional/Mantis.lua Functional/Shorad.lua Functional/Autolase.lua Functional/AICSAR.lua +Functional/AmmoTruck.lua Ops/Airboss.lua Ops/RecoveryTanker.lua From 612419e61509a78c1e6c66941fa5578288a54bbe Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 9 Sep 2022 15:00:42 +0200 Subject: [PATCH 005/603] ZONE * change to distance calc for finding spots w/o buildings --- Moose Development/Moose/Core/Zone.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 1ec83be66..081fad214 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1458,7 +1458,7 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma for _,_coord in pairs (buildings) do local coord = _coord -- Core.Point#COORDINATE -- keep >50m dist from buildings - if coord:Get2DDistance(rcoord) > dist then + if coord:Get3DDistance(rcoord) > dist then found = true else found = false From 247377e98c91da39f73bc60574ed5c192ff9219d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 9 Sep 2022 15:01:00 +0200 Subject: [PATCH 006/603] #OPSGROUP * Fix for always teleporting --- Moose Development/Moose/Ops/OpsGroup.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index a8c556fd1..255c41e18 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -5701,7 +5701,7 @@ function OPSGROUP:RouteToMission(mission, delay) end -- Check if group is mobile. Note that some immobile units report a speed of 1 m/s = 3.6 km/h. - if self.speedMax<=3.6 or mission.teleport then + if (self.speedMax<=3.6 and mission.teleport) or mission.teleport then -- Teleport to waypoint coordinate. Mission will not be paused. self:Teleport(waypointcoord, nil, true) @@ -11778,7 +11778,7 @@ function OPSGROUP:_UpdatePosition() self.positionLast=self.position or self:GetVec3() self.headingLast=self.heading or self:GetHeading() self.orientXLast=self.orientX or self:GetOrientationX() - self.velocityLast=self.velocity or self.group:GetVelocityMPS() + self.velocityLast=self.velocity or self.group:GetVelocityMPS() -- Current state. self.position=self:GetVec3() From 9ad4ce7a497942c9b568653aa241938d70622c6a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 9 Sep 2022 15:01:34 +0200 Subject: [PATCH 007/603] #AMMOTRUCK * optionally use ARMYGROUP (but routing there sucks) --- .../Moose/Functional/AmmoTruck.lua | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index 0d697e927..e1efc9ecd 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -112,7 +112,7 @@ AMMOTRUCK = { ClassName = "AMMOTRUCK", lid = "", - version = "0.0.1", + version = "0.0.10", alias = "", debug = false, trucklist = {}, @@ -187,7 +187,9 @@ function AMMOTRUCK:New(Truckset,Targetset,Coalition,Alias,Homezone) self.ammothreshold = 5 self.remunidist = 20000 self.homezone = Homezone -- Core.Zone#ZONE - self.waitingtime = 1800 + self.waitingtime = 1800 + self.usearmygroup = false + self.hasarmygroup = false -- Log id. self.lid=string.format("AMMOTRUCK %s | %s | ", self.version, self.alias) @@ -237,7 +239,7 @@ end -- @param Wrapper.Group#GROUP Group -- @return #AMMOTRUCK self function AMMOTRUCK:GetAmmoStatus(Group) - local ammotot, shells, rockets, bombs, missiles, narti = Group:GetAmmunition() + local ammotot, shells, rockets, bombs, missiles, narti = Group:GetAmmunition() --self:I({ammotot, shells, rockets, bombs, missiles, narti}) return rockets+missiles+narti end @@ -448,6 +450,15 @@ function AMMOTRUCK:CheckTrucksAlive() local newtruck = {} -- #AMMOTRUCK.data newtruck.name = name newtruck.group = truck + if self.hasarmygroup then + -- is (not) already ARMYGROUP? + if truck.ClassName and truck.ClassName == "GROUP" then + local trucker = ARMYGROUP:New(truck) + trucker:Activate() + newtruck.group = trucker + --self.opstrucks:AddObject(newtruck) + end + end newtruck.state = AMMOTRUCK.State.IDLE newtruck.timestamp = timer.getAbsTime() newtruck.coordinate = truck:GetCoordinate() @@ -465,8 +476,13 @@ end -- @return #AMMOTRUCK self function AMMOTRUCK:onafterStart(From, Event, To) self:T({From, Event, To}) + if ARMYGROUP and self.usearmygroup then + self.hasarmygroup = true + else + self.hasarmygroup = false + end self:CheckTargetsAlive() - self:CheckTrucksAlive() + self:CheckTrucksAlive() self:__Monitor(-30) return self end @@ -602,7 +618,17 @@ function AMMOTRUCK:onafterRouteTruck(From, Event, To, Truckdata, Aridata) local tgtgrp = aridata.group local tgtzone = ZONE_GROUP:New(aridata.name,tgtgrp,30) local tgtcoord = tgtzone:GetRandomCoordinate(15) - if self.routeonroad then + if self.hasarmygroup then + --local targetzone = ZONE_RADIUS:New(aridata.group:GetName(),tgtcoord:GetVec2(),10) + local mission = AUFTRAG:NewONGUARD(tgtcoord) + local oldmission = truckdata.group:GetMissionCurrent() + if oldmission then oldmission:Cancel() end + mission:SetVerbosity(3) + mission:SetMissionSpeed(UTILS.KmphToKnots(20)) + mission:SetTeleport(false) + --mission:SetTime(nil,math.random(self.unloadtime,self.waitingtime)) + truckdata.group:AddMission(mission) + elseif self.routeonroad then truckdata.group:RouteGroundOnRoad(tgtcoord,30,1,"Cone") else truckdata.group:RouteGroundTo(tgtcoord,30,"Cone",1) @@ -660,7 +686,15 @@ function AMMOTRUCK:onafterTruckReturning(From, Event, To, Truck) local truckdata = Truck -- #AMMOTRUCK.data local tgtzone = self.homezone local tgtcoord = tgtzone:GetRandomCoordinate() - if self.routeonroad then + if self.hasarmygroup then + local mission = AUFTRAG:NewONGUARD(tgtcoord) + local oldmission = truckdata.group:GetMissionCurrent() + if oldmission then oldmission:Cancel() end + mission:SetMissionSpeed(UTILS.KmphToKnots(20)) + --mission:SetEnableMarkers() + mission:SetTeleport(false) + truckdata.group:AddMission(mission) + elseif self.routeonroad then truckdata.group:RouteGroundOnRoad(tgtcoord,30,1,"Cone") else truckdata.group:RouteGroundTo(tgtcoord,30,"Cone",1) From f8fa6acc60cbd0a6773582f0cdc8b27312d3901b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 9 Sep 2022 15:30:39 +0200 Subject: [PATCH 008/603] #AMMOTRUCK --- Moose Development/Moose/Functional/AmmoTruck.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index e1efc9ecd..2c741d38d 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -481,6 +481,10 @@ function AMMOTRUCK:onafterStart(From, Event, To) else self.hasarmygroup = false end + if self.debug then + BASE:TraceOn() + BASE:TraceClass("AMMOTRUCK") + end self:CheckTargetsAlive() self:CheckTrucksAlive() self:__Monitor(-30) @@ -629,9 +633,9 @@ function AMMOTRUCK:onafterRouteTruck(From, Event, To, Truckdata, Aridata) --mission:SetTime(nil,math.random(self.unloadtime,self.waitingtime)) truckdata.group:AddMission(mission) elseif self.routeonroad then - truckdata.group:RouteGroundOnRoad(tgtcoord,30,1,"Cone") + truckdata.group:RouteGroundOnRoad(tgtcoord,30) else - truckdata.group:RouteGroundTo(tgtcoord,30,"Cone",1) + truckdata.group:RouteGroundTo(tgtcoord,30) end truckdata.state = AMMOTRUCK.State.DRIVING truckdata.targetgroup = tgtgrp From 5d6488bfa4bc548a7b8fd8d12b6260a8eb8d6ca1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 9 Sep 2022 17:32:00 +0200 Subject: [PATCH 009/603] #OPSGROUP --- Moose Development/Moose/Ops/OpsGroup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 255c41e18..538ab4d42 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -5701,7 +5701,7 @@ function OPSGROUP:RouteToMission(mission, delay) end -- Check if group is mobile. Note that some immobile units report a speed of 1 m/s = 3.6 km/h. - if (self.speedMax<=3.6 and mission.teleport) or mission.teleport then + if self.speedMax<=3.6 or mission.teleport then -- Teleport to waypoint coordinate. Mission will not be paused. self:Teleport(waypointcoord, nil, true) From 8d8bd162b3f1e56a3fedf4bef654e54822cedb30 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 10 Sep 2022 11:34:24 +0200 Subject: [PATCH 010/603] #AmmoTruck --- .../Moose/Functional/AmmoTruck.lua | 88 ++++++++++++------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index 2c741d38d..b1de76c85 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -148,7 +148,7 @@ AMMOTRUCK.State = { --@type AMMOTRUCK.data --@field Wrapper.Group#GROUP group --@field #string name ---@field #AMMOTRUCK.State state +--@field #AMMOTRUCK.State statusquo --@field #number timestamp --@field #number ammo --@field Core.Point#COORDINATE coordinate @@ -225,11 +225,37 @@ function AMMOTRUCK:CheckDrivingTrucks(dataset) local dist = coord:Get2DDistance(tgtcoord) if dist <= 150 then -- arrived - truck.state = AMMOTRUCK.State.ARRIVED + truck.statusquo = AMMOTRUCK.State.ARRIVED truck.timestamp = timer.getAbsTime() truck.coordinate = coord self:__TruckArrived(1,truck) end + -- still driving? + local Tnow = timer.getAbsTime() + if Tnow - truck.timestamp > 30 then + local group = truck.group + if self.usearmygroup then + group = truck.group:GetGroup() + end + local currspeed = group:GetVelocityKMH() + if truck.lastspeed then + if truck.lastspeed == 0 and currspeed == 0 then + self:T(truck.group:GetName().." Is not moving!") + -- try and move it + truck.timestamp = timer.getAbsTime() + if self.routeonroad then + group:RouteGroundOnRoad(truck.targetcoordinate,30,2,"Vee") + else + group:RouteGroundTo(truck.targetcoordinate,30,"Vee",2) + end + end + truck.lastspeed = currspeed + else + truck.lastspeed = currspeed + truck.timestamp = timer.getAbsTime() + end + self:I({truck=truck.group:GetName(),currspeed=currspeed,lastspeed=truck.lastspeed}) + end end return self end @@ -260,9 +286,9 @@ function AMMOTRUCK:CheckWaitingTargets(dataset) local hasammo = self:GetAmmoStatus(truck.group) --if truck.group:GetAmmunition() <= self.ammothreshold then if hasammo <= self.ammothreshold then - truck.state = AMMOTRUCK.State.OUTOFAMMO + truck.statusquo = AMMOTRUCK.State.OUTOFAMMO else - truck.state = AMMOTRUCK.State.IDLE + truck.statusquo = AMMOTRUCK.State.IDLE end end end @@ -286,7 +312,7 @@ function AMMOTRUCK:CheckReturningTrucks(dataset) self:T({name=truck.name,radius=radius,distance=dist}) if dist <= radius then -- arrived - truck.state = AMMOTRUCK.State.IDLE + truck.statusquo = AMMOTRUCK.State.IDLE truck.timestamp = timer.getAbsTime() truck.coordinate = coord self:__TruckHome(1,truck) @@ -341,13 +367,13 @@ function AMMOTRUCK:CheckArrivedTrucks(dataset) for _,_data in pairs (data) do -- set to unloading local truck = _data -- #AMMOTRUCK.data - truck.state = AMMOTRUCK.State.UNLOADING + truck.statusquo = AMMOTRUCK.State.UNLOADING truck.timestamp = timer.getAbsTime() self:__TruckUnloading(2,truck) -- set target to reloading local aridata = self:FindTarget(truck.targetname) -- #AMMOTRUCK.data if aridata then - aridata.state = AMMOTRUCK.State.RELOADING + aridata.statusquo = AMMOTRUCK.State.RELOADING aridata.timestamp = timer.getAbsTime() end end @@ -369,13 +395,13 @@ function AMMOTRUCK:CheckUnloadingTrucks(dataset) local hasammo = self:GetAmmoStatus(truck.targetgroup) --local ammostate = truck.targetgroup:GetAmmunition() if Tpassed > self.unloadtime and hasammo > self.ammothreshold then - truck.state = AMMOTRUCK.State.RETURNING + truck.statusquo = AMMOTRUCK.State.RETURNING truck.timestamp = timer.getAbsTime() self:__TruckReturning(2,truck) -- set target to reloaded local aridata = self:FindTarget(truck.targetname) -- #AMMOTRUCK.data if aridata then - aridata.state = AMMOTRUCK.State.IDLE + aridata.statusquo = AMMOTRUCK.State.IDLE aridata.timestamp = timer.getAbsTime() end end @@ -407,7 +433,7 @@ function AMMOTRUCK:CheckTargetsAlive() local newari = {} -- #AMMOTRUCK.data newari.name = name newari.group = ari - newari.state = AMMOTRUCK.State.IDLE + newari.statusquo = AMMOTRUCK.State.IDLE newari.timestamp = timer.getAbsTime() newari.coordinate = ari:GetCoordinate() local hasammo = self:GetAmmoStatus(ari) @@ -434,8 +460,8 @@ function AMMOTRUCK:CheckTrucksAlive() local tgtname = truck.targetname local targetdata = self:FindTarget(tgtname) -- #AMMOTRUCK.data if targetdata then - if targetdata.state ~= AMMOTRUCK.State.IDLE then - targetdata.state = AMMOTRUCK.State.IDLE + if targetdata.statusquo ~= AMMOTRUCK.State.IDLE then + targetdata.statusquo = AMMOTRUCK.State.IDLE end end self.trucklist[truck.name] = nil @@ -459,7 +485,7 @@ function AMMOTRUCK:CheckTrucksAlive() --self.opstrucks:AddObject(newtruck) end end - newtruck.state = AMMOTRUCK.State.IDLE + newtruck.statusquo = AMMOTRUCK.State.IDLE newtruck.timestamp = timer.getAbsTime() newtruck.coordinate = truck:GetCoordinate() self.trucklist[name] = newtruck @@ -511,14 +537,14 @@ function AMMOTRUCK:onafterMonitor(From, Event, To) --data.ammo = data.group:GetAmmunition() data.ammo = self:GetAmmoStatus(data.group) data.timestamp = timer.getAbsTime() - local text = string.format("Ari %s | Ammo %d | State %s",data.name,data.ammo,data.state) + local text = string.format("Ari %s | Ammo %d | State %s",data.name,data.ammo,data.statusquo) self:T(text) - if data.ammo <= self.ammothreshold and (data.state == AMMOTRUCK.State.IDLE or data.state == AMMOTRUCK.State.OUTOFAMMO) then + if data.ammo <= self.ammothreshold and (data.statusquo == AMMOTRUCK.State.IDLE or data.statusquo == AMMOTRUCK.State.OUTOFAMMO) then -- add to remu queue - data.state = AMMOTRUCK.State.OUTOFAMMO + data.statusquo = AMMOTRUCK.State.OUTOFAMMO remunitionqueue[#remunitionqueue+1] = data remunition = true - elseif data.state == AMMOTRUCK.State.WAITING then + elseif data.statusquo == AMMOTRUCK.State.WAITING then waitingtargets[#waitingtargets+1] = data end else @@ -536,18 +562,18 @@ function AMMOTRUCK:onafterMonitor(From, Event, To) local data = _truckdata -- #AMMOTRUCK.data if data.group and data.group:IsAlive() then -- check state - local text = string.format("Truck %s | State %s",data.name,data.state) + local text = string.format("Truck %s | State %s",data.name,data.statusquo) self:T(text) - if data.state == AMMOTRUCK.State.IDLE then + if data.statusquo == AMMOTRUCK.State.IDLE then idletrucks[#idletrucks+1] = data found = true - elseif data.state == AMMOTRUCK.State.DRIVING then + elseif data.statusquo == AMMOTRUCK.State.DRIVING then drivingtrucks[#drivingtrucks+1] = data - elseif data.state == AMMOTRUCK.State.ARRIVED then + elseif data.statusquo == AMMOTRUCK.State.ARRIVED then arrivedtrucks[#arrivedtrucks+1] = data - elseif data.state == AMMOTRUCK.State.UNLOADING then + elseif data.statusquo == AMMOTRUCK.State.UNLOADING then unloadingtrucks[#unloadingtrucks+1] = data - elseif data.state == AMMOTRUCK.State.RETURNING then + elseif data.statusquo == AMMOTRUCK.State.RETURNING then returningtrucks[#returningtrucks+1] = data idletrucks[#idletrucks+1] = data found = true @@ -568,9 +594,9 @@ function AMMOTRUCK:onafterMonitor(From, Event, To) local aridata = _aridata -- #AMMOTRUCK.data local aricoord = aridata.coordinate local distance = truckcoord:Get2DDistance(aricoord) - if distance <= self.remunidist and aridata.state == AMMOTRUCK.State.OUTOFAMMO and n <= #idletrucks then + if distance <= self.remunidist and aridata.statusquo == AMMOTRUCK.State.OUTOFAMMO and n <= #idletrucks then n = n + 1 - aridata.state = AMMOTRUCK.State.REQUESTED + aridata.statusquo = AMMOTRUCK.State.REQUESTED self:__RouteTruck(n*5,truckdata,aridata) break end @@ -627,8 +653,9 @@ function AMMOTRUCK:onafterRouteTruck(From, Event, To, Truckdata, Aridata) local mission = AUFTRAG:NewONGUARD(tgtcoord) local oldmission = truckdata.group:GetMissionCurrent() if oldmission then oldmission:Cancel() end - mission:SetVerbosity(3) - mission:SetMissionSpeed(UTILS.KmphToKnots(20)) + --mission:SetVerbosity(3) + --mission:SetMissionSpeed(UTILS.KmphToKnots(30)) + mission:SetTime(5) mission:SetTeleport(false) --mission:SetTime(nil,math.random(self.unloadtime,self.waitingtime)) truckdata.group:AddMission(mission) @@ -637,11 +664,11 @@ function AMMOTRUCK:onafterRouteTruck(From, Event, To, Truckdata, Aridata) else truckdata.group:RouteGroundTo(tgtcoord,30) end - truckdata.state = AMMOTRUCK.State.DRIVING + truckdata.statusquo = AMMOTRUCK.State.DRIVING truckdata.targetgroup = tgtgrp truckdata.targetname = aridata.name truckdata.targetcoordinate = tgtcoord - aridata.state = AMMOTRUCK.State.WAITING + aridata.statusquo = AMMOTRUCK.State.WAITING aridata.timestamp = timer.getAbsTime() return self end @@ -694,8 +721,9 @@ function AMMOTRUCK:onafterTruckReturning(From, Event, To, Truck) local mission = AUFTRAG:NewONGUARD(tgtcoord) local oldmission = truckdata.group:GetMissionCurrent() if oldmission then oldmission:Cancel() end - mission:SetMissionSpeed(UTILS.KmphToKnots(20)) + --mission:SetMissionSpeed(UTILS.KmphToKnots(30)) --mission:SetEnableMarkers() + mission:SetTime(5) mission:SetTeleport(false) truckdata.group:AddMission(mission) elseif self.routeonroad then From 2d2f9015459b70d0fbc040d4ef557f9e87e0b23a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 10 Sep 2022 15:13:55 +0200 Subject: [PATCH 011/603] Foreign class definition --- Moose Development/Moose/Wrapper/Controllable.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 156fb8be2..e5a165ea1 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -3871,7 +3871,7 @@ end --- Returns if the unit is a submarine. -- @param #POSITIONABLE self -- @return #boolean Submarines attributes result. -function POSITIONABLE:IsSubmarine() +function CONTROLLABLE:IsSubmarine() self:F2() local DCSUnit = self:GetDCSObject() From 93abef86dccaa24fa136a0d842613e5220560e50 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 11 Sep 2022 14:23:48 +0200 Subject: [PATCH 012/603] VArious fixes --- Moose Development/Moose/Ops/PlayerTask.lua | 334 +++++++++++++++++---- 1 file changed, 269 insertions(+), 65 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index fc22c647f..712be4161 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -684,6 +684,11 @@ do ------------------------------------------------------------------------------------------------------------------- -- PLAYERTASKCONTROLLER -- TODO: PLAYERTASKCONTROLLER +-- DONE Playername from after # +-- DONE Coalition-level screen info to SET based +-- DONE Flash directions +-- TODO less rebuilds menu, Task info menu available after join +-- DONE Limit menu entries ------------------------------------------------------------------------------------------------------------------- --- PLAYERTASKCONTROLLER class. @@ -715,8 +720,13 @@ do -- @field #boolean precisionbombing -- @field Ops.FlightGroup#FLIGHTGROUP LasingDrone -- @field Core.MarkerOps_BASE#MARKEROPS_BASE MarkerOps --- @field #boolean askinfomenu +-- @field #boolean taskinfomenu -- @field #boolean MarkerReadOnly +-- @field #table FlashPlayer List of player who switched Flashing Direction Info on +-- @field #boolean AllowFlash Flashing directions for players allowed +-- @field #number menuitemlimit +-- @field #boolean activehasinfomenu +-- @field #number holdmenutime -- @extends Core.Fsm#FSM --- @@ -893,6 +903,10 @@ do -- POINTEROVERTARGET = "%s, %s, pointer in reach for task %03d, lasing!", -- POINTERTARGETREPORT = "\nPointer in reach: %s\nLasing: %s", -- POINTERTARGETLASINGTTS = ". Pointer in reach and lasing.", +-- TARGET = "Target", +-- FLASHON = "%s - Flashing directions is now ON!", +-- FLASHOFF = "%s - Flashing directions is now OFF!", +-- FLASHMENU = "Flash Directions Switch", -- }, -- -- e.g. @@ -1004,7 +1018,8 @@ PLAYERTASKCONTROLLER = { gettext = nil, locale = "en", precisionbombing = false, - taskinfomenu = true, + taskinfomenu = false, + activehasinfomenu = false, MarkerReadOnly = false, } @@ -1094,6 +1109,10 @@ PLAYERTASKCONTROLLER.Messages = { POINTEROVERTARGET = "%s, %s, pointer in reach for task %03d, lasing!", POINTERTARGETREPORT = "\nPointer in reach: %s\nLasing: %s", POINTERTARGETLASINGTTS = ". Pointer in reach and lasing.", + TARGET = "Target", + FLASHON = "%s - Flashing directions is now ON!", + FLASHOFF = "%s - Flashing directions is now OFF!", + FLASHMENU = "Flash Directions Switch", }, DE = { TASKABORT = "Auftrag abgebrochen!", @@ -1151,12 +1170,16 @@ PLAYERTASKCONTROLLER.Messages = { POINTEROVERTARGET = "%s, %s, Marker im Zielbereich für %03d, Laser an!", POINTERTARGETREPORT = "\nMarker im Zielbereich: %s\nLaser an: %s", POINTERTARGETLASINGTTS = ". Marker im Zielbereich, Laser is an.", + TARGET = "Ziel", + FLASHON = "%s - Richtungsangaben einblenden ist EIN!", + FLASHOFF = "%s - Richtungsangaben einblenden ist AUS!", + FLASHMENU = "Richtungsangaben Schalter", }, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.30" +PLAYERTASKCONTROLLER.version="0.1.32" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1190,10 +1213,15 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.TasksPerPlayer = FIFO:New() -- Utilities.FiFo#FIFO self.PrecisionTasks = FIFO:New() -- Utilities.FiFo#FIFO self.PlayerMenu = {} -- #table + self.FlashPlayer = {} -- #table + self.AllowFlash = false self.lasttaskcount = 0 self.taskinfomenu = false + self.activehasinfomenu = false self.MenuName = nil + self.menuitemlimit = 5 + self.holdmenutime = 30 self.MarkerReadOnly = false @@ -1311,6 +1339,16 @@ function PLAYERTASKCONTROLLER:_InitLocalization() return self end +--- [User] Set flash directions option for player (player based info) +-- @param #PLAYERTASKCONTROLLER self +-- @param #boolean OnOff Set to `true` to switch on and `false` to switch off. Default is OFF. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetAllowFlashDirection(OnOff) + self:T(self.lid.."SetAllowFlashDirection") + self.AllowFlash = OnOff + return self +end + --- [User] Set repetition options for tasks -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff Set to `true` to switch on and `false` to switch off (defaults to true) @@ -1329,6 +1367,22 @@ function PLAYERTASKCONTROLLER:SetTaskRepetition(OnOff, Repeats) return self end +--- [Internal] Send message to SET_CLIENT of players +-- @param #PLAYERTASKCONTROLLER self +-- @param #string Text the text to be send +-- @param #number Seconds (optional) Seconds to show, default 10 +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:_SendMessageToClients(Text,Seconds) + self:T(self.lid.."_SendMessageToClients") + local seconds = Seconds or 10 + self.ClientSet:ForEachClient( + function (Client) + local m = MESSAGE:New(Text,seconds,"Tasking"):ToClient(Client) + end + ) + return self +end + --- [User] Allow precision laser-guided bombing on statics and "high-value" ground units (MBT etc) -- @param #PLAYERTASKCONTROLLER self -- @param Ops.FlightGroup#FLIGHTGROUP FlightGroup The FlightGroup (e.g. drone) to be used for lasing (one unit in one group only). @@ -1421,9 +1475,11 @@ function PLAYERTASKCONTROLLER:_GetPlayerName(Client) -- personalized flight name in player naming ttsplayername = string.match(playername,"| ([%a]+)") end - if string.find(playername,"#") then + local group = Client:GetGroup() + local groupname = group:GetName() + if string.find(groupname,"#") then -- personalized flight name in player naming - ttsplayername = string.match(playername,"# ([%a]+)") + ttsplayername = string.match(groupname,"#([%a]+)") end return playername, ttsplayername end @@ -1456,6 +1512,24 @@ function PLAYERTASKCONTROLLER:DisableTaskInfoMenu() return self end +--- [User] Set menu build fine-tuning options +-- @param #PLAYERTASKCONTROLLER self +-- @param #boolean InfoMenu If `true` this option will allow to show the Task Info-Menu also when a player has an active task. +-- Since the menu isn't refreshed if a player holds an active task, the info in there might be stale. +-- @param #number ItemLimit Number of items per task type to show, default 5. +-- @param #number HoldTime Minimum number of seconds between menu refreshes (called every 30 secs) if a player has **no active task**. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetMenuOptions(InfoMenu,ItemLimit,HoldTime) + self:T(self.lid.."SetMenuOptions") + self.activehasinfomenu = InfoMenu or false + if self.activehasinfomenu then + self:EnableTaskInfoMenu() + end + self.menuitemlimit = ItemLimit or 5 + self.holdmenutime = HoldTime or 30 + return self +end + --- [User] Forbid F10 markers to be deleted by pilots. Note: Marker will auto-delete when the undelying task is done. -- @param #PLAYERTASKCONTROLLER self -- @return #PLAYERTASKCONTROLLER self @@ -1639,7 +1713,6 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType() table.sort(threattable, function (k1, k2) return k1.threat > k2.threat end ) - for _id,_data in pairs(threattable) do local threat=_data.threat local task = _data.task -- Ops.PlayerTask#PLAYERTASK @@ -2135,7 +2208,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task) -- Player already has a task if not self.NoScreenOutput then local text = self.gettext:GetEntry("HAVEACTIVETASK",self.locale) - local m=MESSAGE:New(text,"10","Tasking"):ToGroup(Group) + local m=MESSAGE:New(text,"10","Tasking"):ToClient(Client) end return self end @@ -2151,7 +2224,8 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task) local text = string.format(joined,ttsplayername, self.MenuName or self.Name, Task.TTSType, Task.PlayerTaskNr) self:T(self.lid..text) if not self.NoScreenOutput then - local m=MESSAGE:New(text,"10","Tasking"):ToAll() + self:_SendMessageToClients(text) + --local m=MESSAGE:New(text,"10","Tasking"):ToAll() end if self.UseSRS then self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) @@ -2168,6 +2242,55 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task) return self end +--- [Internal] Switch flashing info for a client +-- @param #PLAYERTASKCONTROLLER self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Client#CLIENT Client +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:_SwitchFlashing(Group, Client) + self:T(self.lid.."_SwitchFlashing") + local playername, ttsplayername = self:_GetPlayerName(Client) + if (not self.FlashPlayer[playername]) or (self.FlashPlayer[playername] == false) then + -- Switch on + self.FlashPlayer[playername] = Client + local flashtext = self.gettext:GetEntry("FLASHON",self.locale) + local text = string.format(flashtext,ttsplayername) + local m = MESSAGE:New(text,10,"Tasking"):ToClient(Client) + else + -- Switch off + self.FlashPlayer[playername] = false + local flashtext = self.gettext:GetEntry("FLASHOFF",self.locale) + local text = string.format(flashtext,ttsplayername) + local m = MESSAGE:New(text,10,"Tasking"):ToClient(Client) + end + return self +end + +--- [Internal] Flashing directional info for a client +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:_FlashInfo() + self:T(self.lid.."_FlashInfo") + for _playername,_client in pairs(self.FlashPlayer) do + if _client and _client:IsAlive() then + if self.TasksPerPlayer:HasUniqueID(_playername) then + local task = self.TasksPerPlayer:ReadByID(_playername) -- Ops.PlayerTask#PLAYERTASK + local Coordinate = task.Target:GetCoordinate() + local CoordText = "" + if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then + CoordText = Coordinate:ToStringA2G(_client) + else + c = Coordinate:ToStringA2A(_client) + end + local targettxt = self.gettext:GetEntry("TARGET",self.locale) + local text = "Target: "..CoordText + local m = MESSAGE:New(text,10,"Tasking"):ToClient(_client) + end + end + end + return self +end + --- [Internal] Show active task info -- @param #PLAYERTASKCONTROLLER self -- @param Wrapper.Group#GROUP Group @@ -2252,7 +2375,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end if not self.NoScreenOutput then - local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) + local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client) end return self end @@ -2281,7 +2404,7 @@ function PLAYERTASKCONTROLLER:_MarkTask(Group, Client) text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end if not self.NoScreenOutput then - local m=MESSAGE:New(text,"10","Tasking"):ToGroup(Group) + local m=MESSAGE:New(text,"10","Tasking"):ToClient(Client) end return self end @@ -2309,7 +2432,7 @@ function PLAYERTASKCONTROLLER:_SmokeTask(Group, Client) text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end if not self.NoScreenOutput then - local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) + local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client) end return self end @@ -2337,7 +2460,7 @@ function PLAYERTASKCONTROLLER:_FlareTask(Group, Client) text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end if not self.NoScreenOutput then - local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) + local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client) end return self end @@ -2366,12 +2489,62 @@ function PLAYERTASKCONTROLLER:_AbortTask(Group, Client) text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end if not self.NoScreenOutput then - local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) + local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client) end self:_BuildMenus(Client,true) return self end +--- [Internal] Build Task Info Menu +-- @param #PLAYERTASKCONTROLLER self +-- @param Wrapper.Group#GROUP group +-- @param Wrapper.Client#CLIENT client +-- @param #string playername +-- @param Core.Menu#MENU_BASE topmenu +-- @param #table tasktypes +-- @param #table taskpertype +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) + if self.taskinfomenu then + local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) + local ittypes = {} + local itaskmenu = {} + local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu) + for _tasktype,_data in pairs(tasktypes) do + ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu) + local tasks = taskpertype[_tasktype] or {} + local n = 0 + for _,_task in pairs(tasks) do + _task = _task -- Ops.PlayerTask#PLAYERTASK + local pilotcount = _task:CountClients() + local newtext = "]" + local tnow = timer.getTime() + -- marker for new tasks + if tnow - _task.timestamp < 60 then + newtext = "*]" + end + local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale) + local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext) + if self.UseGroupNames then + local name = _task.Target:GetName() + if name ~= "Unknown" then + text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) + end + end + local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task) + --taskentry:SetTag(playername) + itaskmenu[#itaskmenu+1] = taskentry + -- keep max items limit + n = n + 1 + if n >= self.menuitemlimit then + break + end + end + end + end + return self +end + --- [Internal] Build client menus -- @param #PLAYERTASKCONTROLLER self -- @param Wrapper.Client#CLIENT Client (optional) build for this client name only @@ -2381,10 +2554,13 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) self:T(self.lid.."_BuildMenus") local clients = self.ClientSet:GetAliveSet() - + local joinorabort = false + if Client then + -- client + enforced -- join task or abort clients = {Client} enforced = true + joinorabort = true end for _,_client in pairs(clients) do @@ -2394,11 +2570,6 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) local unknown = self.gettext:GetEntry("UNKNOWN",self.locale) local playername = client:GetPlayerName() or unknown if group and client then - --- - -- Conditions for menu rebuild - -- 1) Player has no menu - -- 2) Player has no running task - -- 3) enforced --- -- TOPMENU --- @@ -2410,17 +2581,62 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) if self:_CheckPlayerHasTask(playername) then playerhastask = true end local topmenu = nil - self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced)) + self:I("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort)) + -- Cases to rebuild menu + -- 1) new player + -- 2) player joined a task, joinorabort = true + -- 3) player left a task, joinorabort = true + -- 4) player has no task, but number of tasks changed, and last build > 30 secs ago if self.PlayerMenu[playername] then - if enforced or not playerhastask then + -- NOT a new player + -- 2)+3) Join or abort? + if joinorabort then self.PlayerMenu[playername]:RemoveSubMenus() + self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) + topmenu = self.PlayerMenu[playername] + elseif (not playerhastask) and enforced then + -- 4) last build > 30 secs? + local T0 = timer.getAbsTime() + local TDiff = T0-self.PlayerMenu[playername].MenuTag + self:I("TDiff = "..TDiff) + if TDiff >= self.holdmenutime then + self.PlayerMenu[playername]:RemoveSubMenus() + self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) + end + topmenu = self.PlayerMenu[playername] + end + else + -- 1) new player# + topmenu = MENU_GROUP_DELAYED:New(group,menuname,nil) + self.PlayerMenu[playername] = topmenu + self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) + end + + --[[ + if self.PlayerMenu[playername] then + if enforced and not playerhastask then + if self.PlayerMenu[playername].MenuTag then + -- don't build if < 30 secs ago + local T0 = timer.getAbsTime() + local TDiff = T0-self.PlayerMenu[playername].MenuTag + if TDiff > 30 then + -- allow rebuild + self.PlayerMenu[playername]:RemoveSubMenus() + self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) + end + else + self.PlayerMenu[playername]:RemoveSubMenus() + self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) + end end topmenu = self.PlayerMenu[playername] else topmenu = MENU_GROUP_DELAYED:New(group,menuname,nil) self.PlayerMenu[playername] = topmenu + self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) end + --]] --- -- ACTIVE TASK MENU @@ -2443,7 +2659,13 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client) end local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client) - + + if self.activehasinfomenu and self.taskinfomenu then + local tasktypes = self:_GetAvailableTaskTypes() + local taskpertype = self:_GetTasksPerType() + self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) + end + elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask) then --- -- JOIN TASK MENU @@ -2451,16 +2673,14 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) local tasktypes = self:_GetAvailableTaskTypes() local taskpertype = self:_GetTasksPerType() local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale) - local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) local joinmenu = MENU_GROUP_DELAYED:New(group,menujoin,topmenu) local ttypes = {} local taskmenu = {} - local ittypes = {} - local itaskmenu = {} for _tasktype,_data in pairs(tasktypes) do ttypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,joinmenu) local tasks = taskpertype[_tasktype] or {} + local n = 0 for _,_task in pairs(tasks) do _task = _task -- Ops.PlayerTask#PLAYERTASK local pilotcount = _task:CountClients() @@ -2478,45 +2698,17 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) end end - --if _task:GetState() == "Planned" or (not _task:HasPlayerName(playername)) then local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task) - taskentry:SetTag(playername) + --taskentry:SetTag(playername) taskmenu[#taskmenu+1] = taskentry - --end + n = n + 1 + if n >= self.menuitemlimit then + break + end end end - --joinmenu:Set() - if self.taskinfomenu then - local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu) - for _tasktype,_data in pairs(tasktypes) do - ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu) - local tasks = taskpertype[_tasktype] or {} - for _,_task in pairs(tasks) do - _task = _task -- Ops.PlayerTask#PLAYERTASK - local pilotcount = _task:CountClients() - local newtext = "]" - local tnow = timer.getTime() - -- marker for new tasks - if tnow - _task.timestamp < 60 then - newtext = "*]" - end - local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale) - local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext) - if self.UseGroupNames then - local name = _task.Target:GetName() - if name ~= "Unknown" then - text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) - end - end - --if _task:GetState() == "Planned" or (not _task:HasPlayerName(playername)) then - local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task) - taskentry:SetTag(playername) - itaskmenu[#itaskmenu+1] = taskentry - --end - end - end - --taskinfomenu:Set() + self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) end elseif self.TaskQueue:Count() == 0 then -- no tasks (yet) @@ -2525,7 +2717,11 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) end --- -- REFRESH MENU - --- + --- + if self.AllowFlash then + local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) + local flashmenu = MENU_GROUP_COMMAND_DELAYED:New(group,flashtext,self.PlayerMenu[playername],self._SwitchFlashing,self,group,client) + end self.PlayerMenu[playername]:Set() end end @@ -2774,6 +2970,9 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To) self:_CheckTargetQueue() self:_CheckTaskQueue() self:_CheckPrecisionTasks() + if self.AllowFlash then + self:_FlashInfo() + end local targetcount = self.TargetQueue:Count() local taskcount = self.TaskQueue:Count() @@ -2826,7 +3025,8 @@ function PLAYERTASKCONTROLLER:onafterTaskCancelled(From, Event, To, Task) local canceltxttts = self.gettext:GetEntry("TASKCANCELLEDTTS",self.locale) local taskname = string.format(canceltxt, Task.PlayerTaskNr, tostring(Task.Type)) if not self.NoScreenOutput then - local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + self:_SendMessageToClients(taskname,15) + --local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) end if self.UseSRS then taskname = string.format(canceltxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) @@ -2849,7 +3049,8 @@ function PLAYERTASKCONTROLLER:onafterTaskSuccess(From, Event, To, Task) local succtxttts = self.gettext:GetEntry("TASKSUCCESSTTS",self.locale) local taskname = string.format(succtxt, Task.PlayerTaskNr, tostring(Task.Type)) if not self.NoScreenOutput then - local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + self:_SendMessageToClients(taskname,15) + --local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) end if self.UseSRS then taskname = string.format(succtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) @@ -2872,7 +3073,8 @@ function PLAYERTASKCONTROLLER:onafterTaskFailed(From, Event, To, Task) local failtxttts = self.gettext:GetEntry("TASKFAILEDTTS",self.locale) local taskname = string.format(failtxt, Task.PlayerTaskNr, tostring(Task.Type)) if not self.NoScreenOutput then - local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + self:_SendMessageToClients(taskname,15) + --local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) end if self.UseSRS then taskname = string.format(failtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) @@ -2895,7 +3097,8 @@ function PLAYERTASKCONTROLLER:onafterTaskRepeatOnFailed(From, Event, To, Task) local repfailtxttts = self.gettext:GetEntry("TASKFAILEDREPLANTTS",self.locale) local taskname = string.format(repfailtxt, Task.PlayerTaskNr, tostring(Task.Type)) if not self.NoScreenOutput then - local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + self:_SendMessageToClients(taskname,15) + --local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) end if self.UseSRS then taskname = string.format(repfailtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) @@ -2917,7 +3120,8 @@ function PLAYERTASKCONTROLLER:onafterTaskAdded(From, Event, To, Task) local addtxt = self.gettext:GetEntry("TASKADDED",self.locale) local taskname = string.format(addtxt, self.MenuName or self.Name, tostring(Task.Type)) if not self.NoScreenOutput then - local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + self:_SendMessageToClients(taskname,15) + --local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) end if self.UseSRS then taskname = string.format(addtxt, self.MenuName or self.Name, tostring(Task.TTSType)) From e4f1e1a57ac6b50908d0eed0d3dafa35845ee9d5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 11 Sep 2022 18:22:35 +0200 Subject: [PATCH 013/603] #AWACS * Fix custom AWACS callsign not observed --- Moose Development/Moose/Ops/Awacs.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index f4957f689..617cff753 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -495,7 +495,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.41", -- #string + version = "0.2.42", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -2068,7 +2068,7 @@ function AWACS:_StartSettings(FlightGroup,Mission) --self.AwacsFG:SetSRS(self.PathToSRS,self.Gender,self.Culture,self.Voice,self.Port,self.PathToGoogleKey,"AWACS",self.Volume) - self.callsigntxt = string.format("%s",AWACS.CallSignClear[self.CallSign]) + self.callsigntxt = string.format("%s",self.CallSignClear[self.CallSign]) self:__CheckRadioQueue(10) @@ -2110,7 +2110,7 @@ function AWACS:_StartSettings(FlightGroup,Mission) --AwacsFG:SetSRS(self.PathToSRS,self.Gender,self.Culture,self.Voice,self.Port,nil,"AWACS") - self.callsigntxt = string.format("%s",AWACS.CallSignClear[self.CallSign]) + self.callsigntxt = string.format("%s",self.CallSignClear[self.CallSign]) local shifting = self.gettext:GetEntry("SHIFTCHANGE",self.locale) @@ -5711,7 +5711,7 @@ function AWACS:onafterStart(From, Event, To) end --self.AwacsFG:SetSRS(self.PathToSRS,self.Gender,self.Culture,self.Voice,self.Port,self.PathToGoogleKey,"AWACS",self.Volume) - self.callsigntxt = string.format("%s",AWACS.CallSignClear[self.CallSign]) + self.callsigntxt = string.format("%s",self.CallSignClear[self.CallSign]) self:__CheckRadioQueue(-10) local sunrise = self.gettext:GetEntry("SUNRISE",self.locale) From 53380409de4ed8330a340070869f73c7d3a04c5d Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 12 Sep 2022 14:38:44 +0200 Subject: [PATCH 014/603] GROUP / customizes, TTS Callsigns Added function to get customized TTS friendly callsigns from the GROUP --- Moose Development/Moose/Wrapper/Group.lua | 164 ++++++++++------------ 1 file changed, 76 insertions(+), 88 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 11cdab4af..2d518fcf1 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2767,94 +2767,82 @@ function GROUP:GetHighestThreat() return nil, nil end ---do -- Smoke +--- Get TTS friendly, optionally customized callsign mainly for **player groups**. A customized callsign is taken from the #GROUP name, after an optional '#' sign, e.g. "Aerial 1-1#Ghostrider" resulting in "Ghostrider 9", or, +-- if that isn't available, from the playername, as set in the mission editor main screen, after an optional '|' sign (actually, more of a personal call sign), e.g. "Apple|Moose" results in "Moose 9 1". Options see below. +-- @param #GROUP self +-- @param #boolean ShortCallsign Return a shortened customized callsign, i.e. "Ghostrider 9" and not "Ghostrider 9 1" +-- @param #boolean (Player only) Keepnumber Return customized callsign, incl optional numbers at the end, e.g. "Aerial 1-1#Ghostrider 109" results in "Ghostrider 109", if you want to e.g. use historical US Navy Callsigns +-- @param #table CallsignTranslations with DCS callsigns as keys and replacements as values +-- @return #string Callsign +-- @usage +-- -- Set Custom CAP Flight Callsigns for use with TTS +-- mygroup:GetCustomCallSign(true,false,{ +-- Devil = 'Bengal', +-- Snake = 'Winder', +-- Colt = 'Camelot', +-- Enfield = 'Victory', +-- Uzi = 'Evil Eye' +-- }) -- ------ Signal a flare at the position of the GROUP. ----- @param #GROUP self ----- @param Utilities.Utils#FLARECOLOR FlareColor ---function GROUP:Flare( FlareColor ) --- self:F2() --- trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 ) ---end +-- results in this outcome if the group has Callsign "Enfield 9 1" on the 1st #UNIT of the group: -- ------ Signal a white flare at the position of the GROUP. ----- @param #GROUP self ---function GROUP:FlareWhite() --- self:F2() --- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 ) ---end +-- 'Victory 9' -- ------ Signal a yellow flare at the position of the GROUP. ----- @param #GROUP self ---function GROUP:FlareYellow() --- self:F2() --- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 ) ---end --- ------ Signal a green flare at the position of the GROUP. ----- @param #GROUP self ---function GROUP:FlareGreen() --- self:F2() --- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 ) ---end --- ------ Signal a red flare at the position of the GROUP. ----- @param #GROUP self ---function GROUP:FlareRed() --- self:F2() --- local Vec3 = self:GetVec3() --- if Vec3 then --- trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 ) --- end ---end --- ------ Smoke the GROUP. ----- @param #GROUP self ---function GROUP:Smoke( SmokeColor, Range ) --- self:F2() --- if Range then --- trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor ) --- else --- trigger.action.smoke( self:GetVec3(), SmokeColor ) --- end --- ---end --- ------ Smoke the GROUP Green. ----- @param #GROUP self ---function GROUP:SmokeGreen() --- self:F2() --- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green ) ---end --- ------ Smoke the GROUP Red. ----- @param #GROUP self ---function GROUP:SmokeRed() --- self:F2() --- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red ) ---end --- ------ Smoke the GROUP White. ----- @param #GROUP self ---function GROUP:SmokeWhite() --- self:F2() --- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White ) ---end --- ------ Smoke the GROUP Orange. ----- @param #GROUP self ---function GROUP:SmokeOrange() --- self:F2() --- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange ) ---end --- ------ Smoke the GROUP Blue. ----- @param #GROUP self ---function GROUP:SmokeBlue() --- self:F2() --- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue ) ---end --- --- --- ---end +-- +function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) + self:T("GetCustomCallSign") + + local callsign = "Ghost 1" + if self:IsAlive() then + local IsPlayer = self:IsPlayer() + local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1 + local callsignroot = string.match(shortcallsign, '(%a+)') -- Uzi + self:T("CallSign = " .. callsignroot) + local groupname = self:GetName() + local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91 + local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9 + local callnumberminor = string.char(string.byte(callnumber,2)) -- 1 + local personalized = false + if IsPlayer and string.find(groupname,"#") then + -- personalized flight name in group naming + if Keepnumber then + shortcallsign = string.match(groupname,"#(.+)") -- Ghostrider 219 + else + shortcallsign = string.match(groupname,"#([%a]+)") -- Ghostrider + end + personalized = true + elseif IsPlayer and string.find(self:GetPlayerName(),"|") then + -- personalized flight name in group naming + shortcallsign = string.match(Group:GetPlayerName(),"|([%a]+)") -- Ghostrider + personalized = true + end + + if (not personalized) and CallsignTranslations and CallsignTranslations[callsignroot] then + callsignroot = CallsignTranslations[callsignroot] + end + + if personalized then + -- player personalized callsign + if Keepnumber then + return shortcallsign -- Ghostrider 219 + elseif ShortCallsign then + callsign = shortcallsign.." "..callnumbermajor -- Ghostrider 9 + else + callsign = shortcallsign.." "..callnumbermajor.." "..callnumberminor -- Ghostrider 9 1 + end + return callsign + end + + -- AI or not personalized + if ShortCallsign then + callsign = callsignroot.." "..callnumbermajor -- Uzi/Victory 9 + else + callsign = callsignroot.." "..callnumbermajor.." "..callnumberminor -- Uzi/Victory 9 1 + end + + self:T("Generated Callsign = " .. callsign) + end + + return callsign +end + From 4dbfef028e8ebc3aba8a39ffea72fb5f4e18a96e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 13 Sep 2022 11:02:03 +0200 Subject: [PATCH 015/603] *options --- Moose Development/Moose/Ops/PlayerTask.lua | 80 ++++++++++++++-------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 82aed5737..a7b875957 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -684,10 +684,10 @@ do ------------------------------------------------------------------------------------------------------------------- -- PLAYERTASKCONTROLLER -- TODO: PLAYERTASKCONTROLLER --- DONE Playername from after # +-- DONE Playername customized -- DONE Coalition-level screen info to SET based -- DONE Flash directions --- TODO less rebuilds menu, Task info menu available after join +-- DONE less rebuilds menu, Task info menu available after join -- DONE Limit menu entries ------------------------------------------------------------------------------------------------------------------- @@ -727,6 +727,9 @@ do -- @field #number menuitemlimit -- @field #boolean activehasinfomenu -- @field #number holdmenutime +-- @field #table customcallsigns +-- @field #boolean ShortCallsign +-- @field #boolean Keepnumber -- @extends Core.Fsm#FSM --- @@ -1021,6 +1024,9 @@ PLAYERTASKCONTROLLER = { taskinfomenu = false, activehasinfomenu = false, MarkerReadOnly = false, + customcallsigns = {}, + ShortCallsign = true, + Keepnumber = false, } --- @@ -1179,7 +1185,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.32" +PLAYERTASKCONTROLLER.version="0.1.33" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1228,6 +1234,10 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.repeatonfailed = true self.repeattimes = 5 self.UseGroupNames = true + + self.customcallsigns = {} + self.ShortCallsign = true + self.Keepnumber = false if ClientFilter then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart() @@ -1349,6 +1359,22 @@ function PLAYERTASKCONTROLLER:SetAllowFlashDirection(OnOff) return self end +--- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns. +-- @param #PLAYERTASKCONTROLLER self +-- @param #boolean ShortCallsign If true, only call out the major flight number +-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name as-is, no amendments or numbers. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetCallSignOptions(ShortCallsign,Keepnumber) + if not ShortCallsign or ShortCallsign == false then + self.ShortCallsign = false + else + self.ShortCallsign = true + end + self.Keepnumber = Keepnumber or false + return self +end + + --- [User] Set repetition options for tasks -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff Set to `true` to switch on and `false` to switch off (defaults to true) @@ -1470,16 +1496,13 @@ end function PLAYERTASKCONTROLLER:_GetPlayerName(Client) self:T(self.lid.."DisablePrecisionBombing") local playername = Client:GetPlayerName() - local ttsplayername = playername - if string.find(playername,"|") then - -- personalized flight name in player naming - ttsplayername = string.match(playername,"| ([%a]+)") - end - local group = Client:GetGroup() - local groupname = group:GetName() - if string.find(groupname,"#") then - -- personalized flight name in player naming - ttsplayername = string.match(groupname,"#([%a]+)") + local ttsplayername = nil + if not self.customcallsigns[playername] then + local playergroup = Client:GetGroup() + ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber) + self.customcallsigns[playername] = ttsplayername + else + ttsplayername = self.customcallsigns[playername] end return playername, ttsplayername end @@ -1593,10 +1616,13 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) modulation = UTILS.GetModulationName(modulation) local switchtext = self.gettext:GetEntry("BROADCAST",self.locale) - local playername = EventData.IniPlayerName - if string.find(playername,"|") then + local playername = EventData.IniPlayerName + if EventData.IniGroup then -- personalized flight name in player naming - playername = string.match(playername,"| ([%a]+)") + if self.customcallsigns[playername] then + self.customcallsigns[playername] = nil + end + playername = EventData.IniGroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber) end --local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext) local text = string.format(switchtext,self.MenuName or self.Name,playername,freqtext) @@ -1606,11 +1632,6 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) return self end -function PLAYERTASKCONTROLLER:_DummyMenu(group) - self:T(self.lid.."_DummyMenu") - return self -end - --- [User] Set locale for localization. Defaults to "en" -- @param #PLAYERTASKCONTROLLER self -- @param #string Locale The locale to use @@ -1862,8 +1883,12 @@ function PLAYERTASKCONTROLLER:_CheckPrecisionTasks() local text = "" for _,playername in pairs(clients) do local pointertext = self.gettext:GetEntry("POINTEROVERTARGET",self.locale) + local ttsplayername = playername + if self.customcallsigns[playername] then + ttsplayername = self.customcallsigns[playername] + end --text = string.format("%s, %s, pointer over target for task %03d, lasing!", playername, self.MenuName or self.Name, task.PlayerTaskNr) - text = string.format(pointertext, playername, self.MenuName or self.Name, task.PlayerTaskNr) + text = string.format(pointertext, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr) if not self.NoScreenOutput then local client = nil self.ClientSet:ForEachClient( @@ -2344,9 +2369,10 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) local clienttxt = self.gettext:GetEntry("PILOTS",self.locale) if clientcount > 0 then for _,_name in pairs(clientlist) do - if string.find(_name,"|") then + if self.customcallsigns[_name] then -- personalized flight name in player naming - _name = string.match(_name,"| ([%a]+)") + --_name = string.match(_name,"| ([%a]+)") + _name = self.customcallsigns[_name] end clienttxt = clienttxt .. _name .. ", " end @@ -2581,7 +2607,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) if self:_CheckPlayerHasTask(playername) then playerhastask = true end local topmenu = nil - self:I("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort)) + self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort)) -- Cases to rebuild menu -- 1) new player @@ -2599,7 +2625,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) -- 4) last build > 30 secs? local T0 = timer.getAbsTime() local TDiff = T0-self.PlayerMenu[playername].MenuTag - self:I("TDiff = "..TDiff) + self:T("TDiff = "..TDiff) if TDiff >= self.holdmenutime then self.PlayerMenu[playername]:RemoveSubMenus() self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) @@ -2965,7 +2991,7 @@ end -- @param #string To -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) self:_CheckTargetQueue() self:_CheckTaskQueue() From b6fb7d94c8631dc42a0c96304616a474c20ca536 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 13 Sep 2022 13:13:06 +0200 Subject: [PATCH 016/603] 'Menu changes --- Moose Development/Moose/Ops/PlayerTask.lua | 34 +++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index a7b875957..2aa7b14ca 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -730,6 +730,10 @@ do -- @field #table customcallsigns -- @field #boolean ShortCallsign -- @field #boolean Keepnumber +-- @field #table CallsignTranslations +-- @field #table PlayerFlashMenu +-- @field #table PlayerJoinMenu +-- @field #table PlayerInfoMenu -- @extends Core.Fsm#FSM --- @@ -1026,7 +1030,11 @@ PLAYERTASKCONTROLLER = { MarkerReadOnly = false, customcallsigns = {}, ShortCallsign = true, - Keepnumber = false, + Keepnumber = false, + CallsignTranslations = nil, + PlayerFlashMenu = {}, + PlayerJoinMenu = {}, + PlayerInfoMenu = {}, } --- @@ -1185,7 +1193,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.33" +PLAYERTASKCONTROLLER.version="0.1.34" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1238,6 +1246,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.customcallsigns = {} self.ShortCallsign = true self.Keepnumber = false + self.CallsignTranslations = nil if ClientFilter then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart() @@ -1363,18 +1372,20 @@ end -- @param #PLAYERTASKCONTROLLER self -- @param #boolean ShortCallsign If true, only call out the major flight number -- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name as-is, no amendments or numbers. +-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized +-- callsigns from playername or group name. -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:SetCallSignOptions(ShortCallsign,Keepnumber) +function PLAYERTASKCONTROLLER:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations) if not ShortCallsign or ShortCallsign == false then self.ShortCallsign = false else self.ShortCallsign = true end self.Keepnumber = Keepnumber or false + self.CallsignTranslations = CallsignTranslations return self end - --- [User] Set repetition options for tasks -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff Set to `true` to switch on and `false` to switch off (defaults to true) @@ -1499,7 +1510,7 @@ function PLAYERTASKCONTROLLER:_GetPlayerName(Client) local ttsplayername = nil if not self.customcallsigns[playername] then local playergroup = Client:GetGroup() - ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber) + ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) self.customcallsigns[playername] = ttsplayername else ttsplayername = self.customcallsigns[playername] @@ -2532,10 +2543,19 @@ end -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) if self.taskinfomenu then - local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) + local taskinfomenu = nil + if self.PlayerInfoMenu[playername] then + taskinfomenu = self.PlayerInfoMenu[playername] + taskinfomenu:RemoveSubMenus() + else + local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) + taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu) + self.PlayerInfoMenu[playername] = taskinfomenu + end + local ittypes = {} local itaskmenu = {} - local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu) + for _tasktype,_data in pairs(tasktypes) do ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu) local tasks = taskpertype[_tasktype] or {} From 83a7055065efc00c72825d175fe24689e0979d55 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 13 Sep 2022 17:19:29 +0200 Subject: [PATCH 017/603] #PLAYERTASKCONTROLLER * Clean up menu builds --- Moose Development/Moose/Ops/PlayerTask.lua | 116 +++++++++++---------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 2aa7b14ca..4dbc1899b 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -80,7 +80,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.1" +PLAYERTASK.version="0.1.2" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -110,7 +110,7 @@ function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType) self.conditionSuccess = {} self.conditionFailure = {} self.TaskController = nil -- Ops.PlayerTask#PLAYERTASKCONTROLLER - self.timestamp = timer.getTime() + self.timestamp = timer.getAbsTime() self.TTSType = TTSType or "close air support" if Repeat then @@ -262,7 +262,7 @@ function PLAYERTASK:IsDone() return IsDone end ---- [User] Get clients assigned list as table +--- [User] Get client names assigned as table of #strings -- @param #PLAYERTASK self -- @return #table clients -- @return #number clientcount @@ -273,6 +273,17 @@ function PLAYERTASK:GetClients() return clientlist, count end +--- [User] Get #CLIENT objects assigned as table +-- @param #PLAYERTASK self +-- @return #table clients +-- @return #number clientcount +function PLAYERTASK:GetClientObjects() + self:T(self.lid.."GetClientObjects") + local clientlist = self.Clients:GetDataTable() or {} + local count = self.Clients:Count() + return clientlist, count +end + --- [User] Count clients -- @param #PLAYERTASK self -- @return #number clientcount @@ -548,6 +559,7 @@ end -- @return #PLAYERTASK self function PLAYERTASK:onafterPlanned(From, Event, To) self:T({From, Event, To}) + self.timestamp = timer.getAbsTime() return self end @@ -559,6 +571,7 @@ end -- @return #PLAYERTASK self function PLAYERTASK:onafterRequested(From, Event, To) self:T({From, Event, To}) + self.timestamp = timer.getAbsTime() return self end @@ -570,6 +583,7 @@ end -- @return #PLAYERTASK self function PLAYERTASK:onafterExecuting(From, Event, To) self:T({From, Event, To}) + self.timestamp = timer.getAbsTime() return self end @@ -581,6 +595,7 @@ end -- @return #PLAYERTASK self function PLAYERTASK:onafterStop(From, Event, To) self:T({From, Event, To}) + self.timestamp = timer.getAbsTime() return self end @@ -597,6 +612,7 @@ function PLAYERTASK:onafterClientAdded(From, Event, To, Client) local text = string.format("Player %s joined task %03d!",Client:GetPlayerName() or "Generic",self.PlayerTaskNr) self:I(self.lid..text) end + self.timestamp = timer.getAbsTime() return self end @@ -611,6 +627,7 @@ function PLAYERTASK:onafterDone(From, Event, To) if self.TaskController then self.TaskController:__TaskDone(-1,self) end + self.timestamp = timer.getAbsTime() self:__Stop(-1) return self end @@ -626,6 +643,7 @@ function PLAYERTASK:onafterCancel(From, Event, To) if self.TaskController then self.TaskController:__TaskCancelled(-1,self) end + self.timestamp = timer.getAbsTime() self:__Done(-1) return self end @@ -644,6 +662,7 @@ function PLAYERTASK:onafterSuccess(From, Event, To) if self.TargetMarker then self.TargetMarker:Remove() end + self.timestamp = timer.getAbsTime() self:__Done(-1) return self end @@ -673,6 +692,7 @@ function PLAYERTASK:onafterFailed(From, Event, To) end self:__Done(-1) end + self.timestamp = timer.getAbsTime() return self end ------------------------------------------------------------------------------------------------------------------- @@ -1193,7 +1213,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.34" +PLAYERTASKCONTROLLER.version="0.1.35" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1740,7 +1760,9 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType() for _,_task in pairs(datatable) do local task = _task -- Ops.PlayerTask#PLAYERTASK local threat = task.Target:GetThreatLevelMax() - threattable[#threattable+1]={task=task,threat=threat} + if not task:IsDone() then + threattable[#threattable+1]={task=task,threat=threat} + end end table.sort(threattable, function (k1, k2) return k1.threat > k2.threat end ) @@ -1789,8 +1811,11 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue() self:T("*****Removing player " .. _id) self.TasksPerPlayer:PullByID(_id) end - local task = self.TaskQueue:PullByID(_id) -- Ops.PlayerTask#PLAYERTASK - task = nil + local TNow = timer.getAbsTime() + if TNow - task.timestamp > 10 then + local task = self.TaskQueue:PullByID(_id) -- Ops.PlayerTask#PLAYERTASK + task = nil + end end end end @@ -2540,19 +2565,13 @@ end -- @param Core.Menu#MENU_BASE topmenu -- @param #table tasktypes -- @param #table taskpertype --- @return #PLAYERTASKCONTROLLER self +-- @return #table taskinfomenu function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) + self:T(self.lid.."_BuildTaskInfoMenu") + local taskinfomenu = nil if self.taskinfomenu then - local taskinfomenu = nil - if self.PlayerInfoMenu[playername] then - taskinfomenu = self.PlayerInfoMenu[playername] - taskinfomenu:RemoveSubMenus() - else - local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) - taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu) - self.PlayerInfoMenu[playername] = taskinfomenu - end - + local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) + local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu) local ittypes = {} local itaskmenu = {} @@ -2588,19 +2607,21 @@ function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu end end end - return self + return taskinfomenu end --- [Internal] Build client menus -- @param #PLAYERTASKCONTROLLER self -- @param Wrapper.Client#CLIENT Client (optional) build for this client name only -- @param #boolean enforced +-- @param #boolean fromsuccess -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) +function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) self:T(self.lid.."_BuildMenus") local clients = self.ClientSet:GetAliveSet() local joinorabort = false + local timedbuild = false if Client then -- client + enforced -- join task or abort @@ -2624,7 +2645,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) local menuname = self.MenuName or longname local playerhastask = false - if self:_CheckPlayerHasTask(playername) then playerhastask = true end + if self:_CheckPlayerHasTask(playername) and not fromsuccess then playerhastask = true end local topmenu = nil self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort)) @@ -2641,14 +2662,15 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) self.PlayerMenu[playername]:RemoveSubMenus() self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) topmenu = self.PlayerMenu[playername] - elseif (not playerhastask) and enforced then + elseif (not playerhastask) or enforced then -- 4) last build > 30 secs? local T0 = timer.getAbsTime() local TDiff = T0-self.PlayerMenu[playername].MenuTag - self:T("TDiff = "..TDiff) + self:T("TDiff = "..string.format("%.2d",TDiff)) if TDiff >= self.holdmenutime then self.PlayerMenu[playername]:RemoveSubMenus() self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) + timedbuild = true end topmenu = self.PlayerMenu[playername] end @@ -2659,36 +2681,11 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) end - --[[ - if self.PlayerMenu[playername] then - if enforced and not playerhastask then - if self.PlayerMenu[playername].MenuTag then - -- don't build if < 30 secs ago - local T0 = timer.getAbsTime() - local TDiff = T0-self.PlayerMenu[playername].MenuTag - if TDiff > 30 then - -- allow rebuild - self.PlayerMenu[playername]:RemoveSubMenus() - self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) - end - else - self.PlayerMenu[playername]:RemoveSubMenus() - self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) - end - end - topmenu = self.PlayerMenu[playername] - else - topmenu = MENU_GROUP_DELAYED:New(group,menuname,nil) - self.PlayerMenu[playername] = topmenu - self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) - end - --]] - --- -- ACTIVE TASK MENU --- if playerhastask and enforced then - + --self:T("Building Active Task Menus for "..playername) local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale) local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale) local menumark = self.gettext:GetEntry("MENUMARK",self.locale) @@ -2705,14 +2702,17 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client) end local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client) - if self.activehasinfomenu and self.taskinfomenu then + --self:T("Building Active-Info Menus for "..playername) local tasktypes = self:_GetAvailableTaskTypes() local taskpertype = self:_GetTasksPerType() - self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) + if self.PlayerInfoMenu[playername] then + self.PlayerInfoMenu[playername]:RemoveSubMenus() + end + self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) end - - elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask) then + elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask and (timedbuild or joinorabort)) then + --self:T("Building Join Menus for "..playername) --- -- JOIN TASK MENU --- @@ -2754,7 +2754,11 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) end end if self.taskinfomenu then - self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) + --self:T("Building Join-Info Menus for "..playername) + if self.PlayerInfoMenu[playername] then + self.PlayerInfoMenu[playername]:RemoveSubMenus() + end + self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) end elseif self.TaskQueue:Count() == 0 then -- no tasks (yet) @@ -3102,6 +3106,10 @@ function PLAYERTASKCONTROLLER:onafterTaskSuccess(From, Event, To, Task) taskname = string.format(succtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) end + local clients=Task:GetClientObjects() + for _,client in pairs(clients) do + self:_BuildMenus(client,true,true) + end return self end From 498b7a033f1160a6513ba1252aa55b1051434dfa Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 14 Sep 2022 18:08:12 +0200 Subject: [PATCH 018/603] #AWACS * Align CallSign Generation --- Moose Development/Moose/Ops/Awacs.lua | 106 +++++++------------------- 1 file changed, 26 insertions(+), 80 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 617cff753..471301b5e 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -91,6 +91,8 @@ do -- @field #boolean PlayerGuidance if true additional callouts to guide/warn players -- @field #boolean ModernEra if true we get more intel on targets, and EPLR on the AIC -- @field #boolean callsignshort if true use short (group) callsigns, e.g. "Ghost 1", else "Ghost 1 1" +-- @field #boolean keepnumber if true, use the full string after # for a player custom callsign +-- @field #table callsignTranslations optional translations for callsigns -- @field #number MeldDistance 25nm - distance for "Meld" Call , usually shortly before the actual engagement -- @field #number TacDistance 30nm - distance for "TAC" Call -- @field #number ThreatDistance 15nm - distance to declare untargeted (new) threats @@ -495,7 +497,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.42", -- #string + version = "0.2.43", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -561,6 +563,8 @@ AWACS = { PlayerGuidance = true, ModernEra = true, callsignshort = true, + keepnumber = true, + callsignTranslations = nil, TacDistance = 45, MeldDistance = 35, ThreatDistance = 25, @@ -2222,39 +2226,29 @@ function AWACS:_GetCallSign(Group,GID, IsPlayer) local callsign = "Ghost 1" if Group and Group:IsAlive() then - local shortcallsign = Group:GetCallsign() or "unknown11"-- e.g.Uzi11, but we want Uzi 1 1 - local callsignroot = string.match(shortcallsign, '(%a+)') - self:I("CallSign = " .. callsignroot) - local groupname = Group:GetName() - local callnumber = string.match(shortcallsign, "(%d+)$" ) or "unknown11" - local callnumbermajor = string.char(string.byte(callnumber,1)) - local callnumberminor = string.char(string.byte(callnumber,2)) - local personalized = false - if IsPlayer and string.find(groupname,"#") then - -- personalized flight name in group naming - shortcallsign = string.match(groupname,"#([%a]+)") - personalized = true - end - if IsPlayer and string.find(Group:GetPlayerName(),"|") then - -- personalized flight name in group naming - shortcallsign = string.match(Group:GetPlayerName(),"| ([%a]+)") - personalized = true - end - if (not personalized) and self.callsignTranslations and self.callsignTranslations[callsignroot] then - shortcallsign = string.gsub(shortcallsign, callsignroot, self.callsignTranslations[callsignroot]) - end - - if self.callsignshort then - callsign = string.gsub(shortcallsign,callnumber,"").." "..callnumbermajor - else - callsign = string.gsub(shortcallsign,callnumber,"").." "..callnumbermajor.." "..callnumberminor - end - self:T("Generated Callsign for TTS = " .. callsign) - end - + callsign = Group:GetCustomCallSign(self.callsignshort,self.keepnumber,self.callsignTranslations) + end return callsign end +--- [User] Set player callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns. +-- @param #AWACS self +-- @param #boolean ShortCallsign If true, only call out the major flight number +-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name as-is, no amendments or numbers. +-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized +-- callsigns from playername or group name. +-- @return #AWACS self +function AWACS:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations) + if not ShortCallsign or ShortCallsign == false then + self.callsignshort = false + else + self.callsignshort = true + end + self.keepnumber = Keepnumber or false + self.callsignTranslations = CallsignTranslations + return self +end + --- [Internal] Update contact from cluster data -- @param #AWACS self -- @param #number CID Contact ID @@ -4303,55 +4297,7 @@ function AWACS:_ReadAssignedGroupFromTID(TaskID) end return nil end - ---- [Internal] Create new idle task from contact to pick up later --- @param #AWACS self --- @param #string Description Task Type --- @param #table Object Object of TARGET --- @param Ops.Intelligence#INTEL.Contact Contact --- @return #AWACS self -function AWACS:_CreateIdleTaskForContact(Description,Object,Contact) - self:T(self.lid.."_CreateIdleTaskForContact "..Description) - local task = {} -- #AWACS.ManagedTask - self.ManagedTaskID = self.ManagedTaskID + 1 - task.TID = self.ManagedTaskID - task.AssignedGroupID = 0 - task.Status = AWACS.TaskStatus.IDLE - task.ToDo = Description - task.Target = TARGET:New(Object) - task.Contact = Contact - task.ScreenText = Description - if Description == AWACS.TaskDescription.ANCHOR or Description == AWACS.TaskDescription.REANCHOR then - task.Target.Type = TARGET.ObjectType.ZONE - end - self.ManagedTasks:Push(task,task.TID) - return self -end - ---- [Internal] Create new idle task from cluster to pick up later --- @param #AWACS self --- @param #string Description Task Type --- @param #table Object Object of TARGET --- @param Ops.Intelligence#INTEL.Cluster Cluster --- @return #AWACS self -function AWACS:_CreateIdleTaskForCluster(Description,Object,Cluster) - self:T(self.lid.."_CreateIdleTaskForCluster "..Description) - local task = {} -- #AWACS.ManagedTask - self.ManagedTaskID = self.ManagedTaskID + 1 - task.TID = self.ManagedTaskID - task.AssignedGroupID = 0 - task.Status = AWACS.TaskStatus.IDLE - task.ToDo = Description - task.Target = TARGET:New(self.intel:GetClusterCoordinate(Cluster)) - task.Cluster = Cluster - task.ScreenText = Description - if Description == AWACS.TaskDescription.ANCHOR or Description == AWACS.TaskDescription.REANCHOR then - task.Target.Type = TARGET.ObjectType.ZONE - end - self.ManagedTasks:Push(task,task.TID) - return self -end - + --- [Internal] Create radio entry to tell players that CAP is on station in Anchor -- @param #AWACS self -- @param #number GID Group ID From 6226a2a2d7a850febc761b184279ab063f53b0a0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 15 Sep 2022 13:43:23 +0200 Subject: [PATCH 019/603] #ATIS * No radio queue when using SRS #FLIGHTCONTROL * Allow for multiple frequencies, modulations to be used * Use same callsign logic as AWACS and PLAYERTASK #FLIGHTGROUP * Nicefy message output --- Moose Development/Moose/Ops/ATIS.lua | 56 +++++------ Moose Development/Moose/Ops/FlightControl.lua | 97 ++++++++++++------- Moose Development/Moose/Ops/FlightGroup.lua | 4 +- 3 files changed, 96 insertions(+), 61 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 88e650d99..8c10b2f4b 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -586,7 +586,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.9.6" +ATIS.version = "0.9.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1185,32 +1185,34 @@ function ATIS:onafterStart( From, Event, To ) self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) ) -- Start radio queue. - self.radioqueue = RADIOQUEUE:New( self.frequency, self.modulation, string.format( "ATIS %s", self.airbasename ) ) - - -- Send coordinate is airbase coord. - self.radioqueue:SetSenderCoordinate( self.airbase:GetCoordinate() ) - - -- Set relay unit if we have one. - self.radioqueue:SetSenderUnitName( self.relayunitname ) - - -- Set radio power. - self.radioqueue:SetRadioPower( self.power ) - - -- Init numbers. - self.radioqueue:SetDigit( 0, ATIS.Sound.N0.filename, ATIS.Sound.N0.duration, self.soundpath ) - self.radioqueue:SetDigit( 1, ATIS.Sound.N1.filename, ATIS.Sound.N1.duration, self.soundpath ) - self.radioqueue:SetDigit( 2, ATIS.Sound.N2.filename, ATIS.Sound.N2.duration, self.soundpath ) - self.radioqueue:SetDigit( 3, ATIS.Sound.N3.filename, ATIS.Sound.N3.duration, self.soundpath ) - self.radioqueue:SetDigit( 4, ATIS.Sound.N4.filename, ATIS.Sound.N4.duration, self.soundpath ) - self.radioqueue:SetDigit( 5, ATIS.Sound.N5.filename, ATIS.Sound.N5.duration, self.soundpath ) - self.radioqueue:SetDigit( 6, ATIS.Sound.N6.filename, ATIS.Sound.N6.duration, self.soundpath ) - self.radioqueue:SetDigit( 7, ATIS.Sound.N7.filename, ATIS.Sound.N7.duration, self.soundpath ) - self.radioqueue:SetDigit( 8, ATIS.Sound.N8.filename, ATIS.Sound.N8.duration, self.soundpath ) - self.radioqueue:SetDigit( 9, ATIS.Sound.N9.filename, ATIS.Sound.N9.duration, self.soundpath ) - - -- Start radio queue. - self.radioqueue:Start( 1, 0.1 ) - + if not self.useSRS then + self.radioqueue = RADIOQUEUE:New( self.frequency, self.modulation, string.format( "ATIS %s", self.airbasename ) ) + + -- Send coordinate is airbase coord. + self.radioqueue:SetSenderCoordinate( self.airbase:GetCoordinate() ) + + -- Set relay unit if we have one. + self.radioqueue:SetSenderUnitName( self.relayunitname ) + + -- Set radio power. + self.radioqueue:SetRadioPower( self.power ) + + -- Init numbers. + self.radioqueue:SetDigit( 0, ATIS.Sound.N0.filename, ATIS.Sound.N0.duration, self.soundpath ) + self.radioqueue:SetDigit( 1, ATIS.Sound.N1.filename, ATIS.Sound.N1.duration, self.soundpath ) + self.radioqueue:SetDigit( 2, ATIS.Sound.N2.filename, ATIS.Sound.N2.duration, self.soundpath ) + self.radioqueue:SetDigit( 3, ATIS.Sound.N3.filename, ATIS.Sound.N3.duration, self.soundpath ) + self.radioqueue:SetDigit( 4, ATIS.Sound.N4.filename, ATIS.Sound.N4.duration, self.soundpath ) + self.radioqueue:SetDigit( 5, ATIS.Sound.N5.filename, ATIS.Sound.N5.duration, self.soundpath ) + self.radioqueue:SetDigit( 6, ATIS.Sound.N6.filename, ATIS.Sound.N6.duration, self.soundpath ) + self.radioqueue:SetDigit( 7, ATIS.Sound.N7.filename, ATIS.Sound.N7.duration, self.soundpath ) + self.radioqueue:SetDigit( 8, ATIS.Sound.N8.filename, ATIS.Sound.N8.duration, self.soundpath ) + self.radioqueue:SetDigit( 9, ATIS.Sound.N9.filename, ATIS.Sound.N9.duration, self.soundpath ) + + -- Start radio queue. + self.radioqueue:Start( 1, 0.1 ) + end + -- Handle airbase capture -- Handle events. self:HandleEvent( EVENTS.BaseCaptured ) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 840d2e3e2..b10b2471d 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -327,7 +327,7 @@ FLIGHTCONTROL.FlightStatus={ --- FlightControl class version. -- @field #string version -FLIGHTCONTROL.version="0.7.2" +FLIGHTCONTROL.version="0.7.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -357,8 +357,9 @@ FLIGHTCONTROL.version="0.7.2" -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a `#table` of multiple frequencies. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a `#table` of multiple modulations. -- @param #string PathToSRS Path to the directory, where SRS is located. +-- @param #number Port Port of SRS Server, defaults to 5002 -- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS) +function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #FLIGHTCONTROL @@ -406,15 +407,23 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS) self:SetMarkHoldingPattern(true) self:SetRunwayRepairtime() + -- Set SRS Port + self:SetSRSPort(Port or 5002) + + -- Set Callsign Options + self:SetCallSignOptions(true,true) + -- Init msrs queue. self.msrsqueue=MSRSQUEUE:New(self.alias) -- SRS for Tower. self.msrsTower=MSRS:New(PathToSRS, Frequency, Modulation) + self.msrsTower:SetPort(self.Port) self:SetSRSTower() -- SRS for Pilot. self.msrsPilot=MSRS:New(PathToSRS, Frequency, Modulation) + self.msrsPilot:SetPort(self.Port) self:SetSRSPilot() -- Wait at least 10 seconds after last radio message before calling the next status update. @@ -567,6 +576,15 @@ function FLIGHTCONTROL:SetFrequency(Frequency, Modulation) return self end +--- Set the SRS server port. +-- @param #FLIGHTCONTROL self +-- @param #number Port Port to be used. Defaults to 5002. +-- @return #FLIGHTCONTROL self +function FLIGHTCONTROL:SetSRSPort(Port) + self.Port = Port or 5002 + return self +end + --- Set SRS options for a given MSRS object. -- @param #FLIGHTCONTROL self -- @param Sound.SRS#MSRS msrs Moose SRS object. @@ -576,8 +594,9 @@ end -- @param #number Volume Volume. Default 1.0. -- @param #string Label Name under which SRS transmitts. -- @param #string PathToGoogleCredentials Path to google credentials json file. +-- @param #number Port Server port for SRS -- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:_SetSRSOptions(msrs, Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials) +function FLIGHTCONTROL:_SetSRSOptions(msrs, Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials, Port) -- Defaults: Gender=Gender or "female" @@ -592,6 +611,7 @@ function FLIGHTCONTROL:_SetSRSOptions(msrs, Gender, Culture, Voice, Volume, Labe msrs:SetLabel(Label) msrs:SetGoogle(PathToGoogleCredentials) msrs:SetCoalition(self:GetCoalition()) + msrs:SetPort(Port or self.Port or 5002) end return self @@ -981,33 +1001,6 @@ end --- On Before Update status. -- @param #FLIGHTCONTROL self function FLIGHTCONTROL:onbeforeStatusUpdate() - - --[[ - if self.Tlastmessage then - local Tnow=timer.getAbsTime() - - -- Time interval between last radio message. - local dT=Tnow-self.Tlastmessage - - if dT%d sec ago. Status update allowed", dT, self.dTmessage)) - end - end - ]] local Tqueue=self.msrsqueue:CalcTransmisstionDuration() @@ -2633,8 +2626,18 @@ function FLIGHTCONTROL:_PlayerRadioCheck(groupname) local callsign=self:_GetCallsignName(flight) -- Pilot radio check. - local text=string.format("%s, %s, radio check %.3f", self.alias, callsign, self.frequency) + local text = "" + if type(self.frequency) == "table" then + local multifreq = "" + for _,_entry in pairs(self.frequency) do + multifreq = string.format("%s%.2f, ",multifreq,_entry) + end + multifreq = string.gsub(multifreq,", $","") + text=string.format("%s, %s, radio check %s", self.alias, callsign, multifreq) + else + text=string.format("%s, %s, radio check %.3f", self.alias, callsign, self.frequency) + end -- Radio message. self:TransmissionPilot(text, flight) @@ -2713,7 +2716,17 @@ function FLIGHTCONTROL:_PlayerInfoAirbase(groupname) local text=string.format("Airbase %s Info:", self.airbasename) text=text..string.format("\nATC Status: %s", self:GetState()) - text=text..string.format("\nFrequency: %.3f %s", self.frequency, UTILS.GetModulationName(self.modulation)) + + if type(self.frequency) == "table" then + local multifreq = "" + for i=1,#self.frequency do + multifreq=string.format("%s%.2f %s, ",multifreq,self.frequency[i],UTILS.GetModulationName(self.modulation[i] or 0)) + end + text=string.gsub(text,", $","") + text=text..string.format("\nFrequencies: %s", multifreq) + else + text=text..string.format("\nFrequency: %.3f %s", self.frequency, UTILS.GetModulationName(self.modulation)) + end text=text..string.format("\nRunway Landing: %s", self:GetActiveRunwayText()) text=text..string.format("\nRunway Takeoff: %s", self:GetActiveRunwayText(true)) @@ -4458,13 +4471,31 @@ function FLIGHTCONTROL:_IsFlightOnRunway(flight) return nil end +--- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns. +-- @param #FLIGHTCONTROL self +-- @param #boolean ShortCallsign If true, only call out the major flight number. Default = `true`. +-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name for players as-is, no amendments or numbers. Default = `true`. +-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized +-- callsigns from playername or group name. +-- @return #FLIGHTCONTROL self +function FLIGHTCONTROL:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations) + if not ShortCallsign or ShortCallsign == false then + self.ShortCallsign = false + else + self.ShortCallsign = true + end + self.Keepnumber = Keepnumber or false + self.CallsignTranslations = CallsignTranslations + return self +end + --- Get callsign name of a given flight. -- @param #FLIGHTCONTROL self -- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. -- @return #string Callsign or "Ghostrider 1-1". function FLIGHTCONTROL:_GetCallsignName(flight) - local callsign=flight:GetCallsignName() + local callsign=flight:GetCallsignName(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) --local name=string.match(callsign, "%a+") --local number=string.match(callsign, "%d+") diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 342b34cb0..6c5d0bf2f 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -4495,8 +4495,10 @@ function FLIGHTGROUP:_PlayerSubtitles() -- Switch setting. playerData.subtitles=not playerData.subtitles + local onoff = playerData.subtitles == true and "ON" or "OFF" + -- Display message. - MESSAGE:New(string.format("%s, subtitles are now %s", playerData.name, tostring(playerData.subtitles)), 10, nil, true):ToGroup(self.group) + MESSAGE:New(string.format("%s, subtitles are now %s", playerData.name, onoff), 10, nil, true):ToGroup(self.group) else --TODO: Error From ab0a722f2c0c3ab42a34bffcd7cb5dd64206754b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 15 Sep 2022 13:50:19 +0200 Subject: [PATCH 020/603] #OPSGROUP * Allow for customized CallSigns #PLAYERTASK * SRS output finetuning --- Moose Development/Moose/Ops/OpsGroup.lua | 8 ++++++- Moose Development/Moose/Ops/PlayerTask.lua | 26 +++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 538ab4d42..4267f559a 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -11748,8 +11748,11 @@ end --- Get callsign of the first element alive. -- @param #OPSGROUP self +-- @param #boolean ShortCallsign If true, append major flight number only +-- @param #boolean Keepnumber (Player only) If true, and using a customized callsign in the #GROUP name after an #-sign, use all of that information. +-- @param #table CallsignTranslations (optional) Translation table between callsigns -- @return #string Callsign name, e.g. Uzi11, or "Ghostrider11". -function OPSGROUP:GetCallsignName() +function OPSGROUP:GetCallsignName(ShortCallsign,Keepnumber,CallsignTranslations) local element=self:GetElementAlive() @@ -11757,6 +11760,9 @@ function OPSGROUP:GetCallsignName() self:T2(self.lid..string.format("Callsign %s", tostring(element.callsign))) local name=element.callsign or "Ghostrider11" name=name:gsub("-", "") + if self.group:IsPlayer() or CallsignTranslations then + name=self.group:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) + end return name end diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 4dbc1899b..37455242d 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1213,7 +1213,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.35" +PLAYERTASKCONTROLLER.version="0.1.36" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1391,7 +1391,7 @@ end --- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns. -- @param #PLAYERTASKCONTROLLER self -- @param #boolean ShortCallsign If true, only call out the major flight number --- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name as-is, no amendments or numbers. +-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name for players as-is, no amendments or numbers. -- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized -- callsigns from playername or group name. -- @return #PLAYERTASKCONTROLLER self @@ -1406,6 +1406,22 @@ function PLAYERTASKCONTROLLER:SetCallSignOptions(ShortCallsign,Keepnumber,Callsi return self end +--- [Internal] Get text for text-to-speech. +-- Numbers are spaced out, e.g. "Heading 180" becomes "Heading 1 8 0 ". +-- @param #PLAYERTASKCONTROLLER self +-- @param #string text Original text. +-- @return #string Spoken text. +function PLAYERTASKCONTROLLER:_GetTextForSpeech(text) + + -- Space out numbers. + text=string.gsub(text,"%d","%1 ") + -- get rid of leading or trailing spaces + text=string.gsub(text,"^%s*","") + text=string.gsub(text,"%s*$","") + + return text +end + --- [User] Set repetition options for tasks -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff Set to `true` to switch on and `false` to switch off (defaults to true) @@ -1531,7 +1547,9 @@ function PLAYERTASKCONTROLLER:_GetPlayerName(Client) if not self.customcallsigns[playername] then local playergroup = Client:GetGroup() ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) - self.customcallsigns[playername] = ttsplayername + local newplayername = self:_GetTextForSpeech(ttsplayername) + self.customcallsigns[playername] = newplayername + ttsplayername = newplayername else ttsplayername = self.customcallsigns[playername] end @@ -1655,6 +1673,7 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) end playername = EventData.IniGroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber) end + playername = self:_GetTextForSpeech(playername) --local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext) local text = string.format(switchtext,self.MenuName or self.Name,playername,freqtext) self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation) @@ -2289,6 +2308,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task) --local m=MESSAGE:New(text,"10","Tasking"):ToAll() end if self.UseSRS then + self:I(self.lid..text) self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) end self.TasksPerPlayer:Push(Task,playername) From c461c1e2e3a19d892268415ac152af75cb56c677 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 15 Sep 2022 19:50:19 +0200 Subject: [PATCH 021/603] * coordtext fix --- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 37455242d..2202b73ee 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -2361,7 +2361,7 @@ function PLAYERTASKCONTROLLER:_FlashInfo() if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then CoordText = Coordinate:ToStringA2G(_client) else - c = Coordinate:ToStringA2A(_client) + CoordText = Coordinate:ToStringA2A(_client) end local targettxt = self.gettext:GetEntry("TARGET",self.locale) local text = "Target: "..CoordText From d8b0c1903fa133a9ef02904f6c5993fed5fdd92f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 19 Sep 2022 11:45:29 +0200 Subject: [PATCH 022/603] Changes --- .../Moose/Functional/Autolase.lua | 12 +++- Moose Development/Moose/Ops/CSAR.lua | 70 +++++++++++++------ Moose Development/Moose/Utilities/Utils.lua | 5 ++ 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 994a760ba..6ac1252e0 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -111,7 +111,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.1.20" +AUTOLASE.version = "0.1.21" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -430,6 +430,7 @@ function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Cultu self.SRS:SetCulture(self.Culture) self.SRS:SetPort(self.Port) self.SRS:SetVoice(self.Voice) + self.SRS:SetCoalition(self.coalition) if self.PathToGoogleKey then self.SRS:SetGoogle(self.PathToGoogleKey) end @@ -672,10 +673,13 @@ function AUTOLASE:ShowStatus(Group,Unit) if playername then local settings = _DATABASE:GetPlayerSettings(playername) if settings then + self:I("Get Settings ok!") if settings:IsA2G_MGRS() then locationstring = entry.coordinate:ToStringMGRS(settings) elseif settings:IsA2G_LL_DMS() then - locationstring = entry.coordinate:ToStringLLDMS() + locationstring = entry.coordinate:ToStringLLDMS(settings) + elseif settings:IsA2G_BR() then + locationstring = entry.coordinate:ToStringBR(Group:GetCoordinate() or Unit:GetCoordinate(),settings) end end end @@ -948,7 +952,9 @@ function AUTOLASE:onafterMonitor(From, Event, To) settings.MGRS_Accuracy = precision locationstring = unit:GetCoordinate():ToStringMGRS(settings) elseif _SETTINGS:IsA2G_LL_DMS() then - locationstring = unit:GetCoordinate():ToStringLLDMS() + locationstring = unit:GetCoordinate():ToStringLLDMS(_SETTINGS) + elseif _SETTINGS:IsA2G_BR() then + locationstring = unit:GetCoordinate():ToStringBULLS(self.coalition,_SETTINGS) end local laserspot = { -- #AUTOLASE.LaserSpot diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index d3d157d72..822d5a344 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -39,6 +39,7 @@ -- @field #number verbose Verbosity level. -- @field #string lid Class id string for output to DCS log file. -- @field #number coalition Coalition side number, e.g. `coalition.side.RED`. +-- @field Core.Set#SET_GROUP allheligroupset Set of CSAR heli groups. -- @extends Core.Fsm#FSM --- *Combat search and rescue (CSAR) are search and rescue operations that are carried out during war that are within or near combat zones.* (Wikipedia) @@ -129,9 +130,11 @@ -- mycsar.SRSVoice = nil -- SRS voice, relevant for Google TTS -- mycsar.SRSGPathToCredentials = nil -- Path to your Google credentials json file, set this if you want to use Google TTS -- mycsar.SRSVolume = 1 -- Volume, between 0 and 1 +-- mycsar.SRSGender = "male" -- male or female voice -- -- -- mycsar.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection. Requires mycsar.enableForAI to be set to true. --shagrat -- mycsar.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. +-- mycsar.allowbronco = false -- set to true to use the Bronco mod as a CSAR plane -- -- ## 3. Results -- @@ -228,6 +231,7 @@ CSAR = { rescuedpilots = 0, limitmaxdownedpilots = true, maxdownedpilots = 10, + allheligroupset = nil, } --- Downed pilots info. @@ -260,11 +264,12 @@ CSAR.AircraftType["Mi-24P"] = 8 CSAR.AircraftType["Mi-24V"] = 8 CSAR.AircraftType["Bell-47"] = 2 CSAR.AircraftType["UH-60L"] = 10 -CSAR.AircraftType["AH-64D_BLK_II"] = 2 +CSAR.AircraftType["AH-64D_BLK_II"] = 2 +CSAR.AircraftType["Bronco-OV-10A"] = 2 --- CSAR class version. -- @field #string version -CSAR.version="1.0.6" +CSAR.version="1.0.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -411,19 +416,23 @@ function CSAR:New(Coalition, Template, Alias) -- added 0.1.4 self.wetfeettemplate = nil self.usewetfeet = false + + -- added 0.1.8 + self.allowbronco = false -- set to true to use the Bronco mod as a CSAR plane -- WARNING - here\'ll be dragons -- for this to work you need to de-sanitize your mission environment in \Scripts\MissionScripting.lua -- needs SRS => 1.9.6 to work (works on the *server* side) self.useSRS = false -- Use FF\'s SRS integration - self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your server(!) + self.SRSPath = "E:\\Program Files\\DCS-SimpleRadio-Standalone" -- adjust your own path in your server(!) self.SRSchannel = 300 -- radio channel self.SRSModulation = radio.modulation.AM -- modulation self.SRSport = 5002 -- port self.SRSCulture = "en-GB" self.SRSVoice = nil self.SRSGPathToCredentials = nil - self.SRSVolume = 1 + self.SRSVolume = 1.0 -- volume 0.0 to 1.0 + self.SRSGender = "male" -- male or female ------------------------ --- Pseudo Functions --- @@ -925,7 +934,16 @@ function CSAR:_EventHandler(EventData) local _unit = _event.IniUnit local _group = _event.IniGroup - if _unit:IsHelicopter() or _group:IsHelicopter() then + + local function IsBronco(Group) + local grp = Group -- Wrapper.Group#GROUP + local typename = grp:GetTypeName() + self:T(typename) + if typename == "Bronco-OV-10A" then return true end + return false + end + + if _unit:IsHelicopter() or _group:IsHelicopter() or IsBronco(_group) then self:_AddMedevacMenuItem() end @@ -1580,21 +1598,7 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak, _overrid end -- integrate SRS if _speak and self.useSRS then - local srstext = SOUNDTEXT:New(_text) - local path = self.SRSPath - local modulation = self.SRSModulation - local channel = self.SRSchannel - local msrs = MSRS:New(path,channel,modulation) - msrs:SetPort(self.SRSport) - msrs:SetLabel("CSAR") - msrs:SetCulture(self.SRSCulture) - msrs:SetCoalition(self.coalition) - msrs:SetVoice(self.SRSVoice) - if self.SRSGPathToCredentials then - msrs:SetGoogle(self.SRSGPathToCredentials) - end - msrs:SetVolume(self.SRSVolume) - msrs:PlaySoundText(srstext, 2) + self.SRSQueue:NewTransmission(_text,nil,self.msrs,nil,2) end return self end @@ -1894,7 +1898,7 @@ function CSAR:_AddMedevacMenuItem() self:T(self.lid .. " _AddMedevacMenuItem") local coalition = self.coalition - local allheligroupset = self.allheligroupset + local allheligroupset = self.allheligroupset -- Core.Set#SET_GROUP local _allHeliGroups = allheligroupset:GetSetObjects() -- rebuild units table @@ -2106,7 +2110,11 @@ function CSAR:onafterStart(From, Event, To) self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler) self:HandleEvent(EVENTS.PilotDead, self._EventHandler) - if self.useprefix then + + if self.allowbronco then + local prefixes = self.csarPrefix or {} + self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefixes):FilterStart() + elseif self.useprefix then local prefixes = self.csarPrefix or {} self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefixes):FilterCategoryHelicopter():FilterStart() else @@ -2116,6 +2124,24 @@ function CSAR:onafterStart(From, Event, To) if self.wetfeettemplate then self.usewetfeet = true end + if self.useSRS then + local path = self.SRSPath + local modulation = self.SRSModulation + local channel = self.SRSchannel + self.msrs = MSRS:New(path,channel,modulation) + self.msrs:SetPort(self.SRSport) + self.msrs:SetLabel("CSAR") + self.msrs:SetCulture(self.SRSCulture) + self.msrs:SetCoalition(self.coalition) + self.msrs:SetVoice(self.SRSVoice) + self.msrs:SetGender(self.SRSGender) + if self.SRSGPathToCredentials then + self.msrs:SetGoogle(self.SRSGPathToCredentials) + end + self.msrs:SetVolume(self.SRSVolume) + self.msrs:SetLabel("CSAR") + self.SRSQueue = MSRSQUEUE:New("CSAR") + end self:__Status(-10) return self end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 5efc91010..85b6e0d6f 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1849,6 +1849,11 @@ function UTILS.IsLoadingDoorOpen( unit_name ) return true -- no doors on this one ;) end + if type_name == "Bronco-OV-10A" then + BASE:T(unit_name .. " front door(s) are open") + return true -- no doors on this one ;) + end + return false end -- nil From 4f4182f57411565103b765f33e600079d73efb8f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 20 Sep 2022 10:48:13 +0200 Subject: [PATCH 023/603] #RANGE * Add using SRS --- Moose Development/Moose/Functional/Range.lua | 89 +++++++++++++++++++- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 35cbdddd6..a1b19dca4 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -833,7 +833,7 @@ function RANGE:onafterStart() end -- Init range control. - if self.rangecontrolfreq then + if self.rangecontrolfreq and not self.useSRS then -- Radio queue. self.rangecontrol = RADIOQUEUE:New( self.rangecontrolfreq, nil, self.rangename ) @@ -859,7 +859,7 @@ function RANGE:onafterStart() self.rangecontrol:Start( 1, 0.1 ) -- Init range control. - if self.instructorfreq then + if self.instructorfreq and not self.useSRS then -- Radio queue. self.instructor = RADIOQUEUE:New( self.instructorfreq, nil, self.rangename ) @@ -1177,7 +1177,88 @@ function RANGE:TrackMissilesOFF() return self end ---- Enable range control and set frequency. +--- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary. +-- @param #RANGE self +-- @param #string PathToSRS Path to SRS directory. +-- @param #number Coalition Coalition side, e.g. coalition.side.BLUE or coalition.side.RED +-- @param #number Frequency Frequency to use, defaults to 256 (same as rangecontrol) +-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM +-- @param #number Port SRS port. Default 5002. +-- @param #number Volume Volume, between 0.0 and 1.0. Defaults to 1.0 +-- @param #string PathToGoogleKey Path to Google TTS credentials. +-- @return #RANGE self +function RANGE:SetSRS(PathToSRS, Coalition, Frequency, Modulation, Port, Volume, PathToGoogleKey) + if PathToSRS then + + self.useSRS=true + + self.controlmsrs=MSRS:New(PathToSRS, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0) + self.controlmsrs:SetPort(Port) + self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE) + self.controlmsrs:SetLabel("RANGEC") + self.controlsrsQ = MSRSQUEUE:New("CONTROL") + + self.instructmsrs=MSRS:New(PathToSRS, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0) + self.instructmsrs:SetPort(Port) + self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE) + self.instructmsrs:SetLabel("RANGEI") + self.instructsrsQ = MSRSQUEUE:New("INSTRUCT") + + else + self:E(self.lid..string.format("ERROR: No SRS path specified!")) + end + return self +end + +--- (SRS) Set range control frequency and voice. +-- @param #RANGE self +-- @param #number frequency Frequency in MHz. Default 256 MHz. +-- @param #number modulation Modulation, defaults to radio.modulation.AM. +-- @param #string voice Voice. +-- @param #string culture Culture, defaults to "en-US". +-- @param #string gender Gender, defaults to "female". +-- @param #string relayunitname Name of the unit used for transmission location. +-- @return #RANGE self +function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender, relayunitname ) + self.rangecontrolfreq = frequency or 256 + self.controlmsrs:SetFrequencies(self.rangecontrolfreq) + self.controlmsrs:SetModulations(modulation or radio.modulation.AM) + self.controlmsrs:SetVoice(voice) + self.controlmsrs:SetCulture(culture or "en-US") + self.controlmsrs:SetGender(gender or "female") + if relayunitname then + local unit = UNIT:FindByName(relayunitname) + local Coordinate = unit:GetCoordinate() + self.controlmsrs:SetCoordinate(Coordinate) + end + return self +end + +--- (SRS) Set range instructor frequency and voice. +-- @param #RANGE self +-- @param #number frequency Frequency in MHz. Default 305 MHz. +-- @param #number modulation Modulation, defaults to radio.modulation.AM. +-- @param #string voice Voice. +-- @param #string culture Culture, defaults to "en-US". +-- @param #string gender Gender, defaults to "male". +-- @param #string relayunitname Name of the unit used for transmission location. +-- @return #RANGE self +function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gender, relayunitname ) + self.instructorfreq = frequency or 305 + self.instructmsrs:SetFrequencies(self.instructorfreq) + self.instructmsrs:SetModulations(modulation or radio.modulation.AM) + self.instructmsrs:SetVoice(voice) + self.instructmsrs:SetCulture(culture or "en-US") + self.instructmsrs:SetGender(gender or "male") + if relayunitname then + local unit = UNIT:FindByName(relayunitname) + local Coordinate = unit:GetCoordinate() + self.instructmsrs:SetCoordinate(Coordinate) + end + return self +end + +--- Enable range control and set frequency (non-SRS). -- @param #RANGE self -- @param #number frequency Frequency in MHz. Default 256 MHz. -- @param #string relayunitname Name of the unit used for transmission. @@ -1188,7 +1269,7 @@ function RANGE:SetRangeControl( frequency, relayunitname ) return self end ---- Enable instructor radio and set frequency. +--- Enable instructor radio and set frequency (non-SRS). -- @param #RANGE self -- @param #number frequency Frequency in MHz. Default 305 MHz. -- @param #string relayunitname Name of the unit used for transmission. From 9f7729fa0f36ee976255c4cff3a8a87db84f7d9c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 20 Sep 2022 15:51:36 +0200 Subject: [PATCH 024/603] #AIRBASE, ATIS * less noise in log #SRS * Added enumerator for voices MSRS.Voices.Microsoft... and MSRS.Voices.Google... #RANGE * Added SRS support, some bug fixing --- Moose Development/Moose/Core/Settings.lua | 2 +- Moose Development/Moose/Functional/Range.lua | 176 +++++++++++++------ Moose Development/Moose/Ops/ATIS.lua | 12 +- Moose Development/Moose/Sound/SRS.lua | 101 +++++++++++ Moose Development/Moose/Wrapper/Airbase.lua | 10 +- 5 files changed, 235 insertions(+), 66 deletions(-) diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 72cf73a0c..8dd213952 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -315,7 +315,7 @@ do -- SETTINGS -- @param #SETTINGS self -- @return #boolean true if imperial. function SETTINGS:IsImperial() - return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsMetric()) + return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsImperial()) end --- Sets the SETTINGS LL accuracy. diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 898e4c137..ff0da9d6e 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -45,7 +45,8 @@ -- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- -- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536), [Ciribob](https://forums.eagle.ru/member.php?u=112175) --- +-- ### SRS Additions: Applevangelist +-- -- === -- @module Functional.Range -- @image Range.JPG @@ -101,6 +102,10 @@ -- @field #boolean targetsheet If true, players can save their target sheets. Rangeboss will not work if targetsheets do not save. -- @field #string targetpath Path where to save the target sheets. -- @field #string targetprefix File prefix for target sheet files. +-- @field Sound.SRS#MSRS controlmsrs +-- @field Sound.SRS#MSRSQUEUE controlsrsQ +-- @field Sound.SRS#MSRS instructmsrs +-- @field Sound.SRS#MSRSQUEUE instructsrsQ -- @extends Core.Fsm#FSM --- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven @@ -225,6 +230,11 @@ -- -- By default, the sound files are placed in the "Range Soundfiles/" folder inside the mission (.miz) file. Another folder can be specified via the @{#RANGE.SetSoundfilesPath}(*path*) function. -- +-- ## Voice output via SRS +-- +-- Alternatively, the voice output can be fully done via SRS, **no sound file additions needed**. Set up SRS with @{#RANGE.SetSRS}(). Range control and instructor frequencies and voices can then be +-- set via @{#RANGE.SetSRSRangeControl}() and @{#RANGE.SetSRSRangeInstructor}() +-- -- # Persistence -- -- To automatically save bombing results to disk, use the @{#RANGE.SetAutosave}() function. Bombing results will be saved as csv file in your "Saved Games\DCS.openbeta\Logs" directory. @@ -568,7 +578,7 @@ RANGE.MenuF10Root = nil --- Range script version. -- @field #string version -RANGE.version = "2.4.0" +RANGE.version = "2.5.0" -- TODO list: -- TODO: Verbosity level for messages. @@ -1180,14 +1190,14 @@ end --- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary. -- @param #RANGE self -- @param #string PathToSRS Path to SRS directory. +-- @param #number Port SRS port. Default 5002. -- @param #number Coalition Coalition side, e.g. coalition.side.BLUE or coalition.side.RED -- @param #number Frequency Frequency to use, defaults to 256 (same as rangecontrol) -- @param #number Modulation Modulation to use, defaults to radio.modulation.AM --- @param #number Port SRS port. Default 5002. -- @param #number Volume Volume, between 0.0 and 1.0. Defaults to 1.0 -- @param #string PathToGoogleKey Path to Google TTS credentials. -- @return #RANGE self -function RANGE:SetSRS(PathToSRS, Coalition, Frequency, Modulation, Port, Volume, PathToGoogleKey) +function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey) if PathToSRS then self.useSRS=true @@ -1226,10 +1236,11 @@ function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender self.controlmsrs:SetVoice(voice) self.controlmsrs:SetCulture(culture or "en-US") self.controlmsrs:SetGender(gender or "female") + self.rangecontrol = true if relayunitname then local unit = UNIT:FindByName(relayunitname) local Coordinate = unit:GetCoordinate() - self.controlmsrs:SetCoordinate(Coordinate) + self.rangecontrolrelayname = relayunitname end return self end @@ -1250,10 +1261,12 @@ function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gen self.instructmsrs:SetVoice(voice) self.instructmsrs:SetCulture(culture or "en-US") self.instructmsrs:SetGender(gender or "male") + self.instructor = true if relayunitname then local unit = UNIT:FindByName(relayunitname) local Coordinate = unit:GetCoordinate() self.instructmsrs:SetCoordinate(Coordinate) + self.instructorrelayname = relayunitname end return self end @@ -1891,7 +1904,11 @@ function RANGE:OnEventShot( EventData ) if EventData.IniDCSUnit == nil then return end - + + if EventData.IniPlayerName == nil then + return + end + -- Weapon data. local _weapon = EventData.Weapon:getTypeName() -- should be the same as Event.WeaponTypeName local _weaponStrArray = UTILS.Split( _weapon, "%." ) @@ -2084,7 +2101,11 @@ function RANGE:OnEventShot( EventData ) if self.rangecontrol then -- weapon impacted too far from the nearest target! No Score! - self.rangecontrol:NewTransmission( RANGE.Sound.RCWeaponImpactedTooFar.filename, RANGE.Sound.RCWeaponImpactedTooFar.duration, self.soundpath, nil, nil, _message, self.subduration ) + if self.useSRS then + self.controlsrsQ:NewTransmission(_message,nil,self.controlmsrs,nil,1) + else + self.rangecontrol:NewTransmission( RANGE.Sound.RCWeaponImpactedTooFar.filename, RANGE.Sound.RCWeaponImpactedTooFar.duration, self.soundpath, nil, nil, _message, self.subduration ) + end end else @@ -2167,19 +2188,26 @@ end function RANGE:onafterEnterRange( From, Event, To, player ) if self.instructor and self.rangecontrol then - - -- Range control radio frequency split. - local RF = UTILS.Split( string.format( "%.3f", self.rangecontrolfreq ), "." ) - - -- Radio message that player entered the range - -- You entered the bombing range. For hit assessment, contact the range controller. - self.instructor:NewTransmission( RANGE.Sound.IREnterRange.filename, RANGE.Sound.IREnterRange.duration, self.soundpath ) - self.instructor:Number2Transmission( RF[1] ) - if tonumber( RF[2] ) > 0 then - self.instructor:NewTransmission( RANGE.Sound.IRDecimal.filename, RANGE.Sound.IRDecimal.duration, self.soundpath ) - self.instructor:Number2Transmission( RF[2] ) + + if self.useSRS then + local text = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f MHz", self.rangecontrolfreq) + local ttstext = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f mega hertz.", self.rangecontrolfreq) + local group = player.client:GetGroup() + self.instructsrsQ:NewTransmission(ttstext,nil,self.instructmsrs,nil,1,{group},text,10) + else + -- Range control radio frequency split. + local RF = UTILS.Split( string.format( "%.3f", self.rangecontrolfreq ), "." ) + + -- Radio message that player entered the range + -- You entered the bombing range. For hit assessment, contact the range controller at xy MHz + self.instructor:NewTransmission( RANGE.Sound.IREnterRange.filename, RANGE.Sound.IREnterRange.duration, self.soundpath ) + self.instructor:Number2Transmission( RF[1] ) + if tonumber( RF[2] ) > 0 then + self.instructor:NewTransmission( RANGE.Sound.IRDecimal.filename, RANGE.Sound.IRDecimal.duration, self.soundpath ) + self.instructor:Number2Transmission( RF[2] ) + end + self.instructor:NewTransmission( RANGE.Sound.IRMegaHertz.filename, RANGE.Sound.IRMegaHertz.duration, self.soundpath ) end - self.instructor:NewTransmission( RANGE.Sound.IRMegaHertz.filename, RANGE.Sound.IRMegaHertz.duration, self.soundpath ) end end @@ -2194,7 +2222,13 @@ function RANGE:onafterExitRange( From, Event, To, player ) if self.instructor then -- You left the bombing range zone. Have a nice day! - self.instructor:NewTransmission( RANGE.Sound.IRExitRange.filename, RANGE.Sound.IRExitRange.duration, self.soundpath ) + if self.useSRS then + local text = "You left the bombing range zone. Have a nice day!" + local group = player.client:GetGroup() + self.instructsrsQ:NewTransmission(text,nil,self.instructmsrs,nil,1,{group},text,10) + else + self.instructor:NewTransmission( RANGE.Sound.IRExitRange.filename, RANGE.Sound.IRExitRange.duration, self.soundpath ) + end end end @@ -2224,32 +2258,37 @@ function RANGE:onafterImpact( From, Event, To, result, player ) text = text .. string.format( " %s hit.", result.quality ) if self.rangecontrol then - self.rangecontrol:NewTransmission( RANGE.Sound.RCImpact.filename, RANGE.Sound.RCImpact.duration, self.soundpath, nil, nil, text, self.subduration ) - self.rangecontrol:Number2Transmission( string.format( "%03d", result.radial ), nil, 0.1 ) - self.rangecontrol:NewTransmission( RANGE.Sound.RCDegrees.filename, RANGE.Sound.RCDegrees.duration, self.soundpath ) - self.rangecontrol:NewTransmission( RANGE.Sound.RCFor.filename, RANGE.Sound.RCFor.duration, self.soundpath ) - self.rangecontrol:Number2Transmission( string.format( "%d", UTILS.MetersToFeet( result.distance ) ) ) - self.rangecontrol:NewTransmission( RANGE.Sound.RCFeet.filename, RANGE.Sound.RCFeet.duration, self.soundpath ) - if result.quality == "POOR" then - self.rangecontrol:NewTransmission( RANGE.Sound.RCPoorHit.filename, RANGE.Sound.RCPoorHit.duration, self.soundpath, nil, 0.5 ) - elseif result.quality == "INEFFECTIVE" then - self.rangecontrol:NewTransmission( RANGE.Sound.RCIneffectiveHit.filename, RANGE.Sound.RCIneffectiveHit.duration, self.soundpath, nil, 0.5 ) - elseif result.quality == "GOOD" then - self.rangecontrol:NewTransmission( RANGE.Sound.RCGoodHit.filename, RANGE.Sound.RCGoodHit.duration, self.soundpath, nil, 0.5 ) - elseif result.quality == "EXCELLENT" then - self.rangecontrol:NewTransmission( RANGE.Sound.RCExcellentHit.filename, RANGE.Sound.RCExcellentHit.duration, self.soundpath, nil, 0.5 ) + + if self.useSRS then + local group = player.client:GetGroup() + self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1,{group},text,10) + else + self.rangecontrol:NewTransmission( RANGE.Sound.RCImpact.filename, RANGE.Sound.RCImpact.duration, self.soundpath, nil, nil, text, self.subduration ) + self.rangecontrol:Number2Transmission( string.format( "%03d", result.radial ), nil, 0.1 ) + self.rangecontrol:NewTransmission( RANGE.Sound.RCDegrees.filename, RANGE.Sound.RCDegrees.duration, self.soundpath ) + self.rangecontrol:NewTransmission( RANGE.Sound.RCFor.filename, RANGE.Sound.RCFor.duration, self.soundpath ) + self.rangecontrol:Number2Transmission( string.format( "%d", UTILS.MetersToFeet( result.distance ) ) ) + self.rangecontrol:NewTransmission( RANGE.Sound.RCFeet.filename, RANGE.Sound.RCFeet.duration, self.soundpath ) + if result.quality == "POOR" then + self.rangecontrol:NewTransmission( RANGE.Sound.RCPoorHit.filename, RANGE.Sound.RCPoorHit.duration, self.soundpath, nil, 0.5 ) + elseif result.quality == "INEFFECTIVE" then + self.rangecontrol:NewTransmission( RANGE.Sound.RCIneffectiveHit.filename, RANGE.Sound.RCIneffectiveHit.duration, self.soundpath, nil, 0.5 ) + elseif result.quality == "GOOD" then + self.rangecontrol:NewTransmission( RANGE.Sound.RCGoodHit.filename, RANGE.Sound.RCGoodHit.duration, self.soundpath, nil, 0.5 ) + elseif result.quality == "EXCELLENT" then + self.rangecontrol:NewTransmission( RANGE.Sound.RCExcellentHit.filename, RANGE.Sound.RCExcellentHit.duration, self.soundpath, nil, 0.5 ) + end end - end -- Unit. - if player.unitname then + if player.unitname and not self.useSRS then -- Get unit. local unit = UNIT:FindByName( player.unitname ) -- Send message. - self:_DisplayMessageToGroup( unit, text, nil, true ) + self:_DisplayMessageToGroup( unit, text, nil, true ) self:T( self.id .. text ) end @@ -2761,7 +2800,7 @@ function RANGE:_DisplayRangeInfo( _unitname ) -- Check if we have a player. if unit and playername then - + self:I(playername) -- Message text. local text = "" @@ -2830,7 +2869,8 @@ function RANGE:_DisplayRangeInfo( _unitname ) if self.instructorrelayname then local relay = UNIT:FindByName( self.instructorrelayname ) if relay then - alive = tostring( relay:IsAlive() ) + --alive = tostring( relay:IsAlive() ) + alive = relay:IsAlive() and "ok" or "N/A" end end text = text .. string.format( "Instructor %.3f MHz (Relay=%s)\n", self.instructorfreq, alive ) @@ -2841,6 +2881,7 @@ function RANGE:_DisplayRangeInfo( _unitname ) local relay = UNIT:FindByName( self.rangecontrolrelayname ) if relay then alive = tostring( relay:IsAlive() ) + alive = relay:IsAlive() and "ok" or "N/A" end end text = text .. string.format( "Control %.3f MHz (Relay=%s)\n", self.rangecontrolfreq, alive ) @@ -3125,12 +3166,18 @@ function RANGE:_CheckInZone( _unitName ) -- Send message. self:_DisplayMessageToGroup( _unit, _msg, nil, true ) - + if self.rangecontrol then - -- You left the strafing zone too quickly! No score! - self.rangecontrol:NewTransmission( RANGE.Sound.RCLeftStrafePitTooQuickly.filename, RANGE.Sound.RCLeftStrafePitTooQuickly.duration, self.soundpath ) + if self.useSRS then + local group = _unit:GetGroup() + local text = "You left the strafing zone too quickly! No score!" + --self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1,{group},text,10) + self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1) + else + -- You left the strafing zone too quickly! No score! + self.rangecontrol:NewTransmission( RANGE.Sound.RCLeftStrafePitTooQuickly.filename, RANGE.Sound.RCLeftStrafePitTooQuickly.duration, self.soundpath ) + end end - else -- Get current ammo. @@ -3194,10 +3241,13 @@ function RANGE:_CheckInZone( _unitName ) -- Message text. local _text = string.format( "%s, hits on target %s: %d", self:_myname( _unitName ), _result.zone.name, _result.hits ) + local ttstext = string.format( "%s, hits on target %s: %d.", self:_myname( _unitName ), _result.zone.name, _result.hits ) if shots and accur then _text = _text .. string.format( "\nTotal rounds fired %d. Accuracy %.1f %%.", shots, accur ) + ttstext = ttstext .. string.format( ". Total rounds fired %d. Accuracy %.1f percent.", shots, accur ) end _text = _text .. string.format( "\n%s", resulttext ) + ttstext = ttstext .. string.format( " %s", resulttext ) -- Send message. self:_DisplayMessageToGroup( _unit, _text ) @@ -3229,16 +3279,20 @@ function RANGE:_CheckInZone( _unitName ) -- Voice over. if self.rangecontrol then - self.rangecontrol:NewTransmission( RANGE.Sound.RCHitsOnTarget.filename, RANGE.Sound.RCHitsOnTarget.duration, self.soundpath ) - self.rangecontrol:Number2Transmission( string.format( "%d", _result.hits ) ) - if shots and accur then - self.rangecontrol:NewTransmission( RANGE.Sound.RCTotalRoundsFired.filename, RANGE.Sound.RCTotalRoundsFired.duration, self.soundpath, nil, 0.2 ) - self.rangecontrol:Number2Transmission( string.format( "%d", shots ), nil, 0.2 ) - self.rangecontrol:NewTransmission( RANGE.Sound.RCAccuracy.filename, RANGE.Sound.RCAccuracy.duration, self.soundpath, nil, 0.2 ) - self.rangecontrol:Number2Transmission( string.format( "%d", UTILS.Round( accur, 0 ) ) ) - self.rangecontrol:NewTransmission( RANGE.Sound.RCPercent.filename, RANGE.Sound.RCPercent.duration, self.soundpath ) + if self.useSRS then + self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,1) + else + self.rangecontrol:NewTransmission( RANGE.Sound.RCHitsOnTarget.filename, RANGE.Sound.RCHitsOnTarget.duration, self.soundpath ) + self.rangecontrol:Number2Transmission( string.format( "%d", _result.hits ) ) + if shots and accur then + self.rangecontrol:NewTransmission( RANGE.Sound.RCTotalRoundsFired.filename, RANGE.Sound.RCTotalRoundsFired.duration, self.soundpath, nil, 0.2 ) + self.rangecontrol:Number2Transmission( string.format( "%d", shots ), nil, 0.2 ) + self.rangecontrol:NewTransmission( RANGE.Sound.RCAccuracy.filename, RANGE.Sound.RCAccuracy.duration, self.soundpath, nil, 0.2 ) + self.rangecontrol:Number2Transmission( string.format( "%d", UTILS.Round( accur, 0 ) ) ) + self.rangecontrol:NewTransmission( RANGE.Sound.RCPercent.filename, RANGE.Sound.RCPercent.duration, self.soundpath ) + end + self.rangecontrol:NewTransmission( _sound.filename, _sound.duration, self.soundpath, nil, 0.5 ) end - self.rangecontrol:NewTransmission( _sound.filename, _sound.duration, self.soundpath, nil, 0.5 ) end -- Set strafe status to nil. @@ -3277,7 +3331,11 @@ function RANGE:_CheckInZone( _unitName ) local _msg = string.format( "%s, rolling in on strafe pit %s.", self:_myname( _unitName ), target.name ) if self.rangecontrol then - self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath ) + if self.useSRS then + self.controlsrsQ:NewTransmission(_msg,nil,self.controlmsrs,nil,1) + else + self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath ) + end end -- Send message. @@ -4004,6 +4062,7 @@ function RANGE:_GetPlayerUnitAndName( _unitName ) self:T2( { DCSunit = DCSunit, unit = unit, playername = playername } ) if DCSunit and unit and playername then + self:F2(playername) return unit, playername end @@ -4020,13 +4079,22 @@ end -- @param #string unitname Name of the player unit. function RANGE:_myname( unitname ) self:F2( unitname ) - + local pname = "Ghost 1 1" local unit = UNIT:FindByName( unitname ) + if unit then + local grp = unit:GetGroup() + if grp then + pname = grp:GetCustomCallSign(true,true) + end + end + --[[ local pname = unit:GetPlayerName() -- local csign = unit:GetCallsign() -- return string.format("%s (%s)", csign, pname) return string.format( "%s", pname ) + --]] + return pname end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index dc3f698d6..663b0020a 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -745,7 +745,7 @@ end -- @return #ATIS self function ATIS:SetSoundfilesPath( path ) self.soundpath = tostring( path or "ATIS Soundfiles/" ) - self:I( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) ) + self:T( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) ) return self end @@ -756,7 +756,7 @@ end -- @return #ATIS self function ATIS:SetRadioRelayUnitName( unitname ) self.relayunitname = unitname - self:I( self.lid .. string.format( "Setting radio relay unit to %s", self.relayunitname ) ) + self:T( self.lid .. string.format( "Setting radio relay unit to %s", self.relayunitname ) ) return self end @@ -851,7 +851,7 @@ function ATIS:SetRunwayHeadingsMagnetic( headings ) end -- Add runway heading to table. - self:I( self.lid .. string.format( "Adding user specified magnetic runway heading %s", heading ) ) + self:T( self.lid .. string.format( "Adding user specified magnetic runway heading %s", heading ) ) table.insert( self.runwaymag, heading ) local h = self:GetRunwayWithoutLR( heading ) @@ -873,7 +873,7 @@ function ATIS:SetRunwayHeadingsMagnetic( headings ) end -- Add inverse runway heading to table. - self:I( self.lid .. string.format( "Adding user specified magnetic runway heading %s (inverse)", head2 ) ) + self:T( self.lid .. string.format( "Adding user specified magnetic runway heading %s (inverse)", head2 ) ) table.insert( self.runwaymag, head2 ) end @@ -1245,7 +1245,7 @@ function ATIS:onafterStatus( From, Event, To ) else text = text .. string.format( ", Relay unit=%s (alive=%s)", tostring( self.relayunitname ), relayunitstatus ) end - self:I( self.lid .. text ) + self:T( self.lid .. text ) self:__Status( -60 ) end @@ -2433,7 +2433,7 @@ end -- @return #string Runway heading without left or right, *e.g.* "31". function ATIS:GetRunwayWithoutLR( runway ) local rwywo = runway:gsub( "%D+", "" ) - -- self:I(string.format("FF runway=%s ==> rwywo=%s", runway, rwywo)) + -- self:T(string.format("FF runway=%s ==> rwywo=%s", runway, rwywo)) return rwywo end diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 0d91736c0..9f831d505 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -143,6 +143,107 @@ MSRS = { -- @field #string version MSRS.version="0.1.0" +--- Voices +-- @type Voices +MSRS.Voices = { + Microsoft = { + ["Hedda"] = "Microsoft Hedda Desktop", -- de-DE + ["Hazel"] = "Microsoft Hazel Desktop", -- en-GB + ["David"] = "Microsoft David Desktop", -- en-US + ["Zira"] = "Microsoft Zira Desktop", -- en-US + ["Hortense"] = "Microsoft Hortense Desktop", --fr-FR + }, + Google = { + Standard = { + ["en-AU-Standard-A"] = 'en-AU-Standard-A', -- [1] FEMALE + ["en-AU-Standard-B"] = 'en-AU-Standard-B', -- [2] MALE + ["en-AU-Standard-C"] = 'en-AU-Standard-C', -- [3] FEMALE + ["en-AU-Standard-D"] = 'en-AU-Standard-D', -- [4] MALE + ["en-IN-Standard-A"] = 'en-IN-Standard-A', -- [5] FEMALE + ["en-IN-Standard-B"] = 'en-IN-Standard-B', -- [6] MALE + ["en-IN-Standard-C"] = 'en-IN-Standard-C', -- [7] MALE + ["en-IN-Standard-D"] = 'en-IN-Standard-D', -- [8] FEMALE + ["en-GB-Standard-A"] = 'en-GB-Standard-A', -- [9] FEMALE + ["en-GB-Standard-B"] = 'en-GB-Standard-B', -- [10] MALE + ["en-GB-Standard-C"] = 'en-GB-Standard-C', -- [11] FEMALE + ["en-GB-Standard-D"] = 'en-GB-Standard-D', -- [12] MALE + ["en-GB-Standard-F"] = 'en-GB-Standard-F', -- [13] FEMALE + ["en-US-Standard-A"] = 'en-US-Standard-A', -- [14] MALE + ["en-US-Standard-B"] = 'en-US-Standard-B', -- [15] MALE + ["en-US-Standard-C"] = 'en-US-Standard-C', -- [16] FEMALE + ["en-US-Standard-D"] = 'en-US-Standard-D', -- [17] MALE + ["en-US-Standard-E"] = 'en-US-Standard-E', -- [18] FEMALE + ["en-US-Standard-F"] = 'en-US-Standard-F', -- [19] FEMALE + ["en-US-Standard-G"] = 'en-US-Standard-G', -- [20] FEMALE + ["en-US-Standard-H"] = 'en-US-Standard-H', -- [21] FEMALE + ["en-US-Standard-I"] = 'en-US-Standard-I', -- [22] MALE + ["en-US-Standard-J"] = 'en-US-Standard-J', -- [23] MALE + ["fr-FR-Standard-A"] = "fr-FR-Standard-A", -- Female + ["fr-FR-Standard-B"] = "fr-FR-Standard-B", -- Male + ["fr-FR-Standard-C"] = "fr-FR-Standard-C", -- Female + ["fr-FR-Standard-D"] = "fr-FR-Standard-D", -- Male + ["fr-FR-Standard-E"] = "fr-FR-Standard-E", -- Female + ["de-DE-Standard-A"] = "de-DE-Standard-A", -- Female + ["de-DE-Standard-B"] = "de-DE-Standard-B", -- Male + ["de-DE-Standard-C"] = "de-DE-Standard-C", -- Female + ["de-DE-Standard-D"] = "de-DE-Standard-D", -- Male + ["de-DE-Standard-E"] = "de-DE-Standard-E", -- Male + ["de-DE-Standard-F"] = "de-DE-Standard-F", -- Female + ["es-ES-Standard-A"] = "es-ES-Standard-A", -- Female + ["es-ES-Standard-B"] = "es-ES-Standard-B", -- Male + ["es-ES-Standard-C"] = "es-ES-Standard-C", -- Female + ["es-ES-Standard-D"] = "es-ES-Standard-D", -- Female + ["it-IT-Standard-A"] = "it-IT-Standard-A", -- Female + ["it-IT-Standard-B"] = "it-IT-Standard-B", -- Female + ["it-IT-Standard-C"] = "it-IT-Standard-C", -- Male + ["it-IT-Standard-D"] = "it-IT-Standard-D", -- Male + }, + Wavenet = { + ["en-AU-Wavenet-A"] = 'en-AU-Wavenet-A', -- [1] FEMALE + ["en-AU-Wavenet-B"] = 'en-AU-Wavenet-B', -- [2] MALE + ["en-AU-Wavenet-C"] = 'en-AU-Wavenet-C', -- [3] FEMALE + ["en-AU-Wavenet-D"] = 'en-AU-Wavenet-D', -- [4] MALE + ["en-IN-Wavenet-A"] = 'en-IN-Wavenet-A', -- [5] FEMALE + ["en-IN-Wavenet-B"] = 'en-IN-Wavenet-B', -- [6] MALE + ["en-IN-Wavenet-C"] = 'en-IN-Wavenet-C', -- [7] MALE + ["en-IN-Wavenet-D"] = 'en-IN-Wavenet-D', -- [8] FEMALE + ["en-GB-Wavenet-A"] = 'en-GB-Wavenet-A', -- [9] FEMALE + ["en-GB-Wavenet-B"] = 'en-GB-Wavenet-B', -- [10] MALE + ["en-GB-Wavenet-C"] = 'en-GB-Wavenet-C', -- [11] FEMALE + ["en-GB-Wavenet-D"] = 'en-GB-Wavenet-D', -- [12] MALE + ["en-GB-Wavenet-F"] = 'en-GB-Wavenet-F', -- [13] FEMALE + ["en-US-Wavenet-A"] = 'en-US-Wavenet-A', -- [14] MALE + ["en-US-Wavenet-B"] = 'en-US-Wavenet-B', -- [15] MALE + ["en-US-Wavenet-C"] = 'en-US-Wavenet-C', -- [16] FEMALE + ["en-US-Wavenet-D"] = 'en-US-Wavenet-D', -- [17] MALE + ["en-US-Wavenet-E"] = 'en-US-Wavenet-E', -- [18] FEMALE + ["en-US-Wavenet-F"] = 'en-US-Wavenet-F', -- [19] FEMALE + ["en-US-Wavenet-G"] = 'en-US-Wavenet-G', -- [20] FEMALE + ["en-US-Wavenet-H"] = 'en-US-Wavenet-H', -- [21] FEMALE + ["en-US-Wavenet-I"] = 'en-US-Wavenet-I', -- [22] MALE + ["en-US-Wavenet-J"] = 'en-US-Wavenet-J', -- [23] MALE + ["fr-FR-Wavenet-A"] = "fr-FR-Wavenet-A", -- Female + ["fr-FR-Wavenet-B"] = "fr-FR-Wavenet-B", -- Male + ["fr-FR-Wavenet-C"] = "fr-FR-Wavenet-C", -- Female + ["fr-FR-Wavenet-D"] = "fr-FR-Wavenet-D", -- Male + ["fr-FR-Wavenet-E"] = "fr-FR-Wavenet-E", -- Female + ["de-DE-Wavenet-A"] = "de-DE-Wavenet-A", -- Female + ["de-DE-Wavenet-B"] = "de-DE-Wavenet-B", -- Male + ["de-DE-Wavenet-C"] = "de-DE-Wavenet-C", -- Female + ["de-DE-Wavenet-D"] = "de-DE-Wavenet-D", -- Male + ["de-DE-Wavenet-E"] = "de-DE-Wavenet-E", -- Male + ["de-DE-Wavenet-F"] = "de-DE-Wavenet-F", -- Female + ["es-ES-Wavenet-B"] = "es-ES-Wavenet-B", -- Male + ["es-ES-Wavenet-C"] = "es-ES-Wavenet-C", -- Female + ["es-ES-Wavenet-D"] = "es-ES-Wavenet-D", -- Female + ["it-IT-Wavenet-A"] = "it-IT-Wavenet-A", -- Female + ["it-IT-Wavenet-B"] = "it-IT-Wavenet-B", -- Female + ["it-IT-Wavenet-C"] = "it-IT-Wavenet-C", -- Male + ["it-IT-Wavenet-D"] = "it-IT-Wavenet-D", -- Male + } , + }, + } + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 2036df9cc..4ef5d9731 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1455,14 +1455,14 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, --_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied))) if occupied then - self:I(string.format("%s: Parking spot id %d occupied.", airport, _termid)) + self:T(string.format("%s: Parking spot id %d occupied.", airport, _termid)) else - self:I(string.format("%s: Parking spot id %d free.", airport, _termid)) + self:T(string.format("%s: Parking spot id %d free.", airport, _termid)) if nvalid<_nspots then table.insert(validspots, {Coordinate=_spot, TerminalID=_termid}) end nvalid=nvalid+1 - self:I(string.format("%s: Parking spot id %d free. Nfree=%d/%d.", airport, _termid, nvalid,_nspots)) + self:T(string.format("%s: Parking spot id %d free. Nfree=%d/%d.", airport, _termid, nvalid,_nspots)) end end -- loop over units @@ -1980,7 +1980,7 @@ function AIRBASE:SetActiveRunwayLanding(Name, PreferLeft) end if runway then - self:I(string.format("%s: Setting active runway for landing as %s", self.AirbaseName, self:GetRunwayName(runway))) + self:T(string.format("%s: Setting active runway for landing as %s", self.AirbaseName, self:GetRunwayName(runway))) else self:E("ERROR: Could not set the runway for landing!") end @@ -2028,7 +2028,7 @@ function AIRBASE:SetActiveRunwayTakeoff(Name, PreferLeft) end if runway then - self:I(string.format("%s: Setting active runway for takeoff as %s", self.AirbaseName, self:GetRunwayName(runway))) + self:T(string.format("%s: Setting active runway for takeoff as %s", self.AirbaseName, self:GetRunwayName(runway))) else self:E("ERROR: Could not set the runway for takeoff!") end From 13d9562bcdaa98645faea7fe03941351df2cf277 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 20 Sep 2022 17:16:20 +0200 Subject: [PATCH 025/603] #ATIS * Added MSRSQueue to avoid overlaps --- Moose Development/Moose/Ops/ATIS.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 663b0020a..867404df2 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -586,7 +586,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.9.7" +ATIS.version = "0.9.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1137,6 +1137,7 @@ function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port) self.msrs:SetPort(Port) self.msrs:SetCoalition(self:GetCoalition()) self.msrs:SetLabel("ATIS") + self.msrsQ = MSRSQUEUE:New("ATIS") if self.dTQueueCheck<=10 then self:SetQueueUpdateTime(90) end @@ -2285,8 +2286,10 @@ function ATIS:onafterReport( From, Event, To, Text ) -- Debug output. self:T( "SRS TTS: " .. text ) - -- Play text-to-speech report. - self.msrs:PlayText( text ) + -- Play text-to-speech report. + local duration = STTS.getSpeechTime(text,0.95) + self.msrsQ:NewTransmission(text,duration,self.msrs,nil,2) + --self.msrs:PlayText( text ) end From 69b70bfed6f460ab2c39bcd80787c84e0c4acaf9 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 21 Sep 2022 11:43:54 +0200 Subject: [PATCH 026/603] Update Spawn.lua (#1786) Update function SPAWN:_SpawnCleanUpScheduler() to better cater for Helos --- Moose Development/Moose/Core/Spawn.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index b22a27939..dae176097 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3365,10 +3365,12 @@ end -- @return #boolean True = Continue Scheduler function SPAWN:_SpawnCleanUpScheduler() self:F( { "CleanUp Scheduler:", self.SpawnTemplatePrefix } ) - + local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup() self:T( { "CleanUp Scheduler:", SpawnGroup, SpawnCursor } ) + local IsHelo = SpawnGroup:IsHelicopter() + while SpawnGroup do local SpawnUnits = SpawnGroup:GetUnits() @@ -3383,7 +3385,7 @@ function SPAWN:_SpawnCleanUpScheduler() self:T( { SpawnUnitName, Stamp } ) if Stamp.Vec2 then - if SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1 then + if (SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1) or IsHelo then local NewVec2 = SpawnUnit:GetVec2() if (Stamp.Vec2.x == NewVec2.x and Stamp.Vec2.y == NewVec2.y) or (SpawnUnit:GetLife() <= 1) then -- If the plane is not moving or dead , and is on the ground, assign it with a timestamp... @@ -3402,7 +3404,7 @@ function SPAWN:_SpawnCleanUpScheduler() Stamp.Time = nil end else - if SpawnUnit:InAir() == false then + if SpawnUnit:InAir() == false or IsHelo then Stamp.Vec2 = SpawnUnit:GetVec2() if (SpawnUnit:GetVelocityKMH() < 1) then Stamp.Time = timer.getTime() From 22a5f05e326742a7f03ab7af5f5ce7e9f67cadaa Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 22 Sep 2022 10:57:26 +0200 Subject: [PATCH 027/603] #SPAWN * Fix for InitCleanup() --- Moose Development/Moose/Core/Spawn.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index dae176097..3536596f0 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3369,10 +3369,12 @@ function SPAWN:_SpawnCleanUpScheduler() local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup() self:T( { "CleanUp Scheduler:", SpawnGroup, SpawnCursor } ) - local IsHelo = SpawnGroup:IsHelicopter() + local IsHelo = false while SpawnGroup do - + + IsHelo = SpawnGroup:IsHelicopter() + local SpawnUnits = SpawnGroup:GetUnits() for UnitID, UnitData in pairs( SpawnUnits ) do @@ -3386,7 +3388,7 @@ function SPAWN:_SpawnCleanUpScheduler() if Stamp.Vec2 then if (SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1) or IsHelo then - local NewVec2 = SpawnUnit:GetVec2() + local NewVec2 = SpawnUnit:GetVec2() or {x=0, y=0} if (Stamp.Vec2.x == NewVec2.x and Stamp.Vec2.y == NewVec2.y) or (SpawnUnit:GetLife() <= 1) then -- If the plane is not moving or dead , and is on the ground, assign it with a timestamp... if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then @@ -3404,8 +3406,8 @@ function SPAWN:_SpawnCleanUpScheduler() Stamp.Time = nil end else - if SpawnUnit:InAir() == false or IsHelo then - Stamp.Vec2 = SpawnUnit:GetVec2() + if SpawnUnit:InAir() == false or (IsHelo and SpawnUnit:GetLife() <= 1) then + Stamp.Vec2 = SpawnUnit:GetVec2() or {x=0, y=0} if (SpawnUnit:GetVelocityKMH() < 1) then Stamp.Time = timer.getTime() end From 276805ab28ef6fa8c02a9348cd8d7e3aa5a6ac9b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 22 Sep 2022 17:30:44 +0200 Subject: [PATCH 028/603] CTLD * make troops mass count --- Moose Development/Moose/Ops/CTLD.lua | 74 ++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 522dd1efe..eb9914fea 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -23,6 +23,7 @@ -- @image OPS_CTLD.jpg -- Date: Feb 2022 +-- Last Update Sep 2022 do @@ -306,7 +307,8 @@ CTLD_ENGINEERING = { end -do +do + ------------------------------------------------------ --- **CTLD_CARGO** class, extends Core.Base#BASE -- @type CTLD_CARGO @@ -314,7 +316,7 @@ do -- @field #number ID ID of this cargo. -- @field #string Name Name for menu. -- @field #table Templates Table of #POSITIONABLE objects. --- @field #CTLD_CARGO.Enum CargoType Enumerator of Type. +-- @field #string CargoType Enumerator of Type. -- @field #boolean HasBeenMoved Flag for moving. -- @field #boolean LoadDirectly Flag for direct loading. -- @field #number CratesNeeded Crates needed to build. @@ -325,8 +327,9 @@ do -- @field #string Subcategory Sub-category name. -- @extends Core.Base#BASE + --- --- @field #CTLD_CARGO +-- @field CTLD_CARGO CTLD_CARGO = { ClassName = "CTLD_CARGO", ID = 0, @@ -343,9 +346,15 @@ CTLD_CARGO = { Mark = nil, } - --- --- Define cargo types. - -- @field Enum + -- @type CTLD_CARGO.Enum + -- @field #string VEHICLE + -- @field #string TROOPS + -- @field #string FOB + -- @field #string CRATE + -- @field #string REPAIR + -- @field #string ENGINEERS + -- @field #string STATIC CTLD_CARGO.Enum = { VEHICLE = "Vehicle", -- #string vehicles TROOPS = "Troops", -- #string troops @@ -542,7 +551,7 @@ CTLD_CARGO = { --- Query crate type for STATIC -- @param #CTLD_CARGO self - -- @param #boolean + -- @return #boolean function CTLD_CARGO:IsStatic() if self.CargoType == "Static" then return true @@ -551,19 +560,35 @@ CTLD_CARGO = { end end + --- Add mark + -- @param #CTLD_CARGO self + -- @return #CTLD_CARGO self function CTLD_CARGO:AddMark(Mark) self.Mark = Mark return self end + --- Get mark + -- @param #CTLD_CARGO self + -- @return #string Mark function CTLD_CARGO:GetMark(Mark) return self.Mark end + --- Wipe mark + -- @param #CTLD_CARGO self + -- @return #CTLD_CARGO self function CTLD_CARGO:WipeMark() self.Mark = nil return self end + + --- Get overall mass of a cargo object, i.e. crates needed x mass per crate + -- @param #CTLD_CARGO self + -- @return #number mass + function CTLD_CARGO:GetNetMass() + return self.CratesNeeded * self.PerCrateMass + end end @@ -1062,7 +1087,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.10" +CTLD.version="1.0.11" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1532,6 +1557,8 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) local instock = Cargotype:GetStock() local cgoname = Cargotype:GetName() local cgotype = Cargotype:GetType() + local cgonetmass = Cargotype:GetNetMass() + local maxloadable = self:_GetMaxLoadableMass(Unit) if type(instock) == "number" and tonumber(instock) <= 0 and tonumber(instock) ~= -1 then -- nothing left over self:_SendMessage(string.format("Sorry, all %s are gone!", cgoname), 10, false, Group) @@ -1583,6 +1610,9 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) if troopsize + numberonboard > trooplimit then self:_SendMessage("Sorry, we\'re crammed already!", 10, false, Group) return + elseif maxloadable < cgonetmass then + self:_SendMessage("Sorry, that\'s too heavy to load!", 10, false, Group) + return else self.CargoCounter = self.CargoCounter + 1 local loadcargotype = CTLD_CARGO:New(self.CargoCounter, Cargotype.Name, Cargotype.Templates, cgotype, true, true, Cargotype.CratesNeeded,nil,nil,Cargotype.PerCrateMass) @@ -2144,11 +2174,14 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist, _ignoreweight) 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 + maxloadable = maxmass - loadedmass + --]] + maxloadable = self:_GetMaxLoadableMass(_unit) end self:T(self.lid .. " Max loadable mass: " .. maxloadable) for _,_cargoobject in pairs (existingcrates) do @@ -2326,6 +2359,21 @@ function CTLD:_GetUnitCargoMass(Unit) return loadedmass end +--- (Internal) Function to calculate max loadable mass left over. +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit +-- @return #number maxloadable Max loadable mass in kg +function CTLD:_GetMaxLoadableMass(Unit) + self:T(self.lid .. " _GetMaxLoadableMass") + if not Unit then return 0 end + local loadable = 0 + local loadedmass = self:_GetUnitCargoMass(Unit) + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + local maxmass = capabilities.cargoweightlimit or 2000 -- max 2 tons + loadable = maxmass - loadedmass + return loadable +end + --- (Internal) Function to calculate and set Unit internal cargo mass -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit @@ -2333,9 +2381,6 @@ function CTLD:_UpdateUnitCargoMass(Unit) self:T(self.lid .. " _UpdateUnitCargoMass") local calculatedMass = self:_GetUnitCargoMass(Unit) Unit:SetUnitInternalCargo(calculatedMass) - --local report = REPORT:New("Loadmaster report") - --report:Add("Carrying " .. calculatedMass .. "Kg") - --self:_SendMessage(report:Text(),10,false,Unit:GetGroup()) return self end @@ -2353,6 +2398,7 @@ function CTLD:_ListCargo(Group, Unit) local cratelimit = capabilities.cratelimit -- #number local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo local loadedmass = self:_GetUnitCargoMass(Unit) -- #number + local maxloadable = self:_GetMaxLoadableMass(Unit) if self.Loaded_Cargo[unitname] then local no_troops = loadedcargo.Troopsloaded or 0 local no_crates = loadedcargo.Cratesloaded or 0 @@ -2387,11 +2433,11 @@ function CTLD:_ListCargo(Group, Unit) report:Add(" N O N E") end report:Add("------------------------------------------------------------") - report:Add("Total Mass: ".. loadedmass .. " kg") + report:Add("Total Mass: ".. loadedmass .. " kg. Loadable: "..maxloadable.." kg.") local text = report:Text() self:_SendMessage(text, 30, true, Group) else - self:_SendMessage(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d",trooplimit,cratelimit), 10, false, Group) + self:_SendMessage(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d | Weight limit %d kgs",trooplimit,cratelimit,maxloadable), 10, false, Group) end return self end @@ -3125,7 +3171,7 @@ function CTLD:_RefreshF10Menus() --- [Internal] Function to check if a template exists in the mission. -- @param #CTLD self -- @param #table temptable Table of string names --- @return #boolen outcome +-- @return #boolean outcome function CTLD:_CheckTemplates(temptable) self:T(self.lid .. " _CheckTemplates") local outcome = true From ba6f16943d2b97eff61bda217ffdcc0e154f1c9e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 23 Sep 2022 10:28:20 +0200 Subject: [PATCH 029/603] #CSAR * Added option to change top menu name --- Moose Development/Moose/Ops/CSAR.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 822d5a344..264bb1625 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -98,23 +98,21 @@ -- mycsar.useprefix = true -- Requires CSAR helicopter #GROUP names to have the prefix(es) defined below. -- mycsar.csarPrefix = { "helicargo", "MEDEVAC"} -- #GROUP name prefixes used for useprefix=true - DO NOT use # in helicopter names in the Mission Editor! -- mycsar.verbose = 0 -- set to > 1 for stats output for debugging. --- -- (added 0.1.4) limit amount of downed pilots spawned by **ejection** events +-- -- limit amount of downed pilots spawned by **ejection** events -- mycsar.limitmaxdownedpilots = true -- mycsar.maxdownedpilots = 10 --- -- (added 0.1.8) - allow to set far/near distance for approach and optionally pilot must open doors +-- -- allow to set far/near distance for approach and optionally pilot must open doors -- mycsar.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters -- mycsar.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters -- mycsar.pilotmustopendoors = false -- switch to true to enable check of open doors --- -- (added 0.1.9) -- mycsar.suppressmessages = false -- switch off all messaging if you want to do your own --- -- (added 0.1.11) -- mycsar.rescuehoverheight = 20 -- max height for a hovering rescue in meters -- mycsar.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters --- -- (added 0.1.12) -- -- Country codes for spawned pilots -- mycsar.countryblue= country.id.USA -- mycsar.countryred = country.id.RUSSIA -- mycsar.countryneutral = country.id.UN_PEACEKEEPERS +-- mycsar.topmenuname = "CSAR" -- set the menu entry name -- -- ## 2.1 Experimental Features -- @@ -218,7 +216,7 @@ CSAR = { smokeMarkers = {}, -- tracks smoke markers for groups heliVisibleMessage = {}, -- tracks if the first message has been sent of the heli being visible heliCloseMessage = {}, -- tracks heli close message ie heli < 500m distance - max_units = 6, --number of pilots that can be carried + max_units = 6, -- number of pilots that can be carried hoverStatus = {}, -- tracks status of a helis hover above a downed pilot pilotDisabled = {}, -- tracks what aircraft a pilot is disabled for pilotLives = {}, -- tracks how many lives a pilot has @@ -232,6 +230,7 @@ CSAR = { limitmaxdownedpilots = true, maxdownedpilots = 10, allheligroupset = nil, + topmenuname = "CSAR", } --- Downed pilots info. @@ -1923,7 +1922,8 @@ function CSAR:_AddMedevacMenuItem() local groupname = _group:GetName() if self.addedTo[groupname] == nil then self.addedTo[groupname] = true - local _rootPath = MENU_GROUP:New(_group,"CSAR") + local menuname = self.topmenuname or "CSAR" + local _rootPath = MENU_GROUP:New(_group,menuname) local _rootMenu1 = MENU_GROUP_COMMAND:New(_group,"List Active CSAR",_rootPath, self._DisplayActiveSAR,self,_unitName) local _rootMenu2 = MENU_GROUP_COMMAND:New(_group,"Check Onboard",_rootPath, self._CheckOnboard,self,_unitName) local _rootMenu3 = MENU_GROUP_COMMAND:New(_group,"Request Signal Flare",_rootPath, self._SignalFlare,self,_unitName) From d17f4cf46c8d98cfd39d1d30701a4874aa1c2790 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 25 Sep 2022 14:11:40 +0200 Subject: [PATCH 030/603] #AWACS - give full frequency in info --- Moose Development/Moose/Ops/Awacs.lua | 2 +- Moose Development/Moose/Sound/SRS.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 454e9ec21..b9e614289 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -2966,7 +2966,7 @@ function AWACS:_ShowAwacsInfo(Group) local report = REPORT:New("Info") report:Add("====================") report:Add(string.format("AWACS %s",self.callsigntxt)) - report:Add(string.format("Radio: %d %s",self.Frequency,UTILS.GetModulationName(self.Modulation))) + report:Add(string.format("Radio: %.3f %s",self.Frequency,UTILS.GetModulationName(self.Modulation))) report:Add(string.format("Bulls Alias: %s",self.AOName)) report:Add(string.format("Coordinate: %s",self.AOCoordinate:ToStringLLDDM())) report:Add("====================") diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 4cc2b5176..12b7ff22a 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -815,7 +815,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp --local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT") -- Command. - local command=string.format('"%s\\%s" -f %s -m %s -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume) + local command=string.format('"%s\\%s" -f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume) -- Set voice or gender/culture. if voice then From 4355b3a2c8afc8438fd1b4803cb6a760d42e9eaf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 25 Sep 2022 14:40:42 +0200 Subject: [PATCH 031/603] SRS - correct enumerator --- Moose Development/Moose/Sound/SRS.lua | 166 +++++++++++++------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 9ed2258bf..bc1bb67dc 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -155,91 +155,91 @@ MSRS.Voices = { }, Google = { Standard = { - ["en-AU-Standard-A"] = 'en-AU-Standard-A', -- [1] FEMALE - ["en-AU-Standard-B"] = 'en-AU-Standard-B', -- [2] MALE - ["en-AU-Standard-C"] = 'en-AU-Standard-C', -- [3] FEMALE - ["en-AU-Standard-D"] = 'en-AU-Standard-D', -- [4] MALE - ["en-IN-Standard-A"] = 'en-IN-Standard-A', -- [5] FEMALE - ["en-IN-Standard-B"] = 'en-IN-Standard-B', -- [6] MALE - ["en-IN-Standard-C"] = 'en-IN-Standard-C', -- [7] MALE - ["en-IN-Standard-D"] = 'en-IN-Standard-D', -- [8] FEMALE - ["en-GB-Standard-A"] = 'en-GB-Standard-A', -- [9] FEMALE - ["en-GB-Standard-B"] = 'en-GB-Standard-B', -- [10] MALE - ["en-GB-Standard-C"] = 'en-GB-Standard-C', -- [11] FEMALE - ["en-GB-Standard-D"] = 'en-GB-Standard-D', -- [12] MALE - ["en-GB-Standard-F"] = 'en-GB-Standard-F', -- [13] FEMALE - ["en-US-Standard-A"] = 'en-US-Standard-A', -- [14] MALE - ["en-US-Standard-B"] = 'en-US-Standard-B', -- [15] MALE - ["en-US-Standard-C"] = 'en-US-Standard-C', -- [16] FEMALE - ["en-US-Standard-D"] = 'en-US-Standard-D', -- [17] MALE - ["en-US-Standard-E"] = 'en-US-Standard-E', -- [18] FEMALE - ["en-US-Standard-F"] = 'en-US-Standard-F', -- [19] FEMALE - ["en-US-Standard-G"] = 'en-US-Standard-G', -- [20] FEMALE - ["en-US-Standard-H"] = 'en-US-Standard-H', -- [21] FEMALE - ["en-US-Standard-I"] = 'en-US-Standard-I', -- [22] MALE - ["en-US-Standard-J"] = 'en-US-Standard-J', -- [23] MALE - ["fr-FR-Standard-A"] = "fr-FR-Standard-A", -- Female - ["fr-FR-Standard-B"] = "fr-FR-Standard-B", -- Male - ["fr-FR-Standard-C"] = "fr-FR-Standard-C", -- Female - ["fr-FR-Standard-D"] = "fr-FR-Standard-D", -- Male - ["fr-FR-Standard-E"] = "fr-FR-Standard-E", -- Female - ["de-DE-Standard-A"] = "de-DE-Standard-A", -- Female - ["de-DE-Standard-B"] = "de-DE-Standard-B", -- Male - ["de-DE-Standard-C"] = "de-DE-Standard-C", -- Female - ["de-DE-Standard-D"] = "de-DE-Standard-D", -- Male - ["de-DE-Standard-E"] = "de-DE-Standard-E", -- Male - ["de-DE-Standard-F"] = "de-DE-Standard-F", -- Female - ["es-ES-Standard-A"] = "es-ES-Standard-A", -- Female - ["es-ES-Standard-B"] = "es-ES-Standard-B", -- Male - ["es-ES-Standard-C"] = "es-ES-Standard-C", -- Female - ["es-ES-Standard-D"] = "es-ES-Standard-D", -- Female - ["it-IT-Standard-A"] = "it-IT-Standard-A", -- Female - ["it-IT-Standard-B"] = "it-IT-Standard-B", -- Female - ["it-IT-Standard-C"] = "it-IT-Standard-C", -- Male - ["it-IT-Standard-D"] = "it-IT-Standard-D", -- Male + ["en_AU_Standard_A"] = 'en-AU-Standard-A', -- [1] FEMALE + ["en_AU_Standard_B"] = 'en-AU-Standard-B', -- [2] MALE + ["en_AU_Standard_C"] = 'en-AU-Standard-C', -- [3] FEMALE + ["en_AU_Standard_D"] = 'en-AU-Standard-D', -- [4] MALE + ["en_IN_Standard_A"] = 'en-IN-Standard-A', -- [5] FEMALE + ["en_IN_Standard_B"] = 'en-IN-Standard-B', -- [6] MALE + ["en_IN_Standard_C"] = 'en-IN-Standard-C', -- [7] MALE + ["en_IN_Standard_D"] = 'en-IN-Standard-D', -- [8] FEMALE + ["en_GB_Standard_A"] = 'en-GB-Standard-A', -- [9] FEMALE + ["en_GB_Standard_B"] = 'en-GB-Standard-B', -- [10] MALE + ["en_GB_Standard_C"] = 'en-GB-Standard-C', -- [11] FEMALE + ["en_GB_Standard_D"] = 'en-GB-Standard-D', -- [12] MALE + ["en_GB_Standard_F"] = 'en-GB-Standard-F', -- [13] FEMALE + ["en_US_Standard_A"] = 'en-US-Standard-A', -- [14] MALE + ["en_US_Standard_B"] = 'en-US-Standard-B', -- [15] MALE + ["en_US_Standard_C"] = 'en-US-Standard-C', -- [16] FEMALE + ["en_US_Standard_D"] = 'en-US-Standard-D', -- [17] MALE + ["en_US_Standard_E"] = 'en-US-Standard-E', -- [18] FEMALE + ["en_US_Standard_F"] = 'en-US-Standard-F', -- [19] FEMALE + ["en_US_Standard_G"] = 'en-US-Standard-G', -- [20] FEMALE + ["en_US_Standard_H"] = 'en-US-Standard-H', -- [21] FEMALE + ["en_US_Standard_I"] = 'en-US-Standard-I', -- [22] MALE + ["en_US_Standard_J"] = 'en-US-Standard-J', -- [23] MALE + ["fr_FR_Standard_A"] = "fr-FR-Standard-A", -- Female + ["fr_FR_Standard_B"] = "fr-FR-Standard-B", -- Male + ["fr_FR_Standard_C"] = "fr-FR-Standard-C", -- Female + ["fr_FR_Standard_D"] = "fr-FR-Standard-D", -- Male + ["fr_FR_Standard_E"] = "fr-FR-Standard-E", -- Female + ["de_DE_Standard_A"] = "de-DE-Standard-A", -- Female + ["de_DE_Standard_B"] = "de-DE-Standard-B", -- Male + ["de_DE_Standard_C"] = "de-DE-Standard-C", -- Female + ["de_DE_Standard_D"] = "de-DE-Standard-D", -- Male + ["de_DE_Standard_E"] = "de-DE-Standard-E", -- Male + ["de_DE_Standard_F"] = "de-DE-Standard-F", -- Female + ["es_ES_Standard_A"] = "es-ES-Standard-A", -- Female + ["es_ES_Standard_B"] = "es-ES-Standard-B", -- Male + ["es_ES_Standard_C"] = "es-ES-Standard-C", -- Female + ["es_ES_Standard_D"] = "es-ES-Standard-D", -- Female + ["it_IT_Standard_A"] = "it-IT-Standard-A", -- Female + ["it_IT_Standard_B"] = "it-IT-Standard-B", -- Female + ["it_IT_Standard_C"] = "it-IT-Standard-C", -- Male + ["it_IT_Standard_D"] = "it-IT-Standard-D", -- Male }, Wavenet = { - ["en-AU-Wavenet-A"] = 'en-AU-Wavenet-A', -- [1] FEMALE - ["en-AU-Wavenet-B"] = 'en-AU-Wavenet-B', -- [2] MALE - ["en-AU-Wavenet-C"] = 'en-AU-Wavenet-C', -- [3] FEMALE - ["en-AU-Wavenet-D"] = 'en-AU-Wavenet-D', -- [4] MALE - ["en-IN-Wavenet-A"] = 'en-IN-Wavenet-A', -- [5] FEMALE - ["en-IN-Wavenet-B"] = 'en-IN-Wavenet-B', -- [6] MALE - ["en-IN-Wavenet-C"] = 'en-IN-Wavenet-C', -- [7] MALE - ["en-IN-Wavenet-D"] = 'en-IN-Wavenet-D', -- [8] FEMALE - ["en-GB-Wavenet-A"] = 'en-GB-Wavenet-A', -- [9] FEMALE - ["en-GB-Wavenet-B"] = 'en-GB-Wavenet-B', -- [10] MALE - ["en-GB-Wavenet-C"] = 'en-GB-Wavenet-C', -- [11] FEMALE - ["en-GB-Wavenet-D"] = 'en-GB-Wavenet-D', -- [12] MALE - ["en-GB-Wavenet-F"] = 'en-GB-Wavenet-F', -- [13] FEMALE - ["en-US-Wavenet-A"] = 'en-US-Wavenet-A', -- [14] MALE - ["en-US-Wavenet-B"] = 'en-US-Wavenet-B', -- [15] MALE - ["en-US-Wavenet-C"] = 'en-US-Wavenet-C', -- [16] FEMALE - ["en-US-Wavenet-D"] = 'en-US-Wavenet-D', -- [17] MALE - ["en-US-Wavenet-E"] = 'en-US-Wavenet-E', -- [18] FEMALE - ["en-US-Wavenet-F"] = 'en-US-Wavenet-F', -- [19] FEMALE - ["en-US-Wavenet-G"] = 'en-US-Wavenet-G', -- [20] FEMALE - ["en-US-Wavenet-H"] = 'en-US-Wavenet-H', -- [21] FEMALE - ["en-US-Wavenet-I"] = 'en-US-Wavenet-I', -- [22] MALE - ["en-US-Wavenet-J"] = 'en-US-Wavenet-J', -- [23] MALE - ["fr-FR-Wavenet-A"] = "fr-FR-Wavenet-A", -- Female - ["fr-FR-Wavenet-B"] = "fr-FR-Wavenet-B", -- Male - ["fr-FR-Wavenet-C"] = "fr-FR-Wavenet-C", -- Female - ["fr-FR-Wavenet-D"] = "fr-FR-Wavenet-D", -- Male - ["fr-FR-Wavenet-E"] = "fr-FR-Wavenet-E", -- Female - ["de-DE-Wavenet-A"] = "de-DE-Wavenet-A", -- Female - ["de-DE-Wavenet-B"] = "de-DE-Wavenet-B", -- Male - ["de-DE-Wavenet-C"] = "de-DE-Wavenet-C", -- Female - ["de-DE-Wavenet-D"] = "de-DE-Wavenet-D", -- Male - ["de-DE-Wavenet-E"] = "de-DE-Wavenet-E", -- Male - ["de-DE-Wavenet-F"] = "de-DE-Wavenet-F", -- Female - ["es-ES-Wavenet-B"] = "es-ES-Wavenet-B", -- Male - ["es-ES-Wavenet-C"] = "es-ES-Wavenet-C", -- Female - ["es-ES-Wavenet-D"] = "es-ES-Wavenet-D", -- Female - ["it-IT-Wavenet-A"] = "it-IT-Wavenet-A", -- Female - ["it-IT-Wavenet-B"] = "it-IT-Wavenet-B", -- Female - ["it-IT-Wavenet-C"] = "it-IT-Wavenet-C", -- Male - ["it-IT-Wavenet-D"] = "it-IT-Wavenet-D", -- Male + ["en_AU_Wavenet_A"] = 'en-AU-Wavenet-A', -- [1] FEMALE + ["en_AU_Wavenet_B"] = 'en-AU-Wavenet-B', -- [2] MALE + ["en_AU_Wavenet_C"] = 'en-AU-Wavenet-C', -- [3] FEMALE + ["en_AU_Wavenet_D"] = 'en-AU-Wavenet-D', -- [4] MALE + ["en_IN_Wavenet_A"] = 'en-IN-Wavenet-A', -- [5] FEMALE + ["en_IN_Wavenet_B"] = 'en-IN-Wavenet-B', -- [6] MALE + ["en_IN_Wavenet_C"] = 'en-IN-Wavenet-C', -- [7] MALE + ["en_IN_Wavenet_D"] = 'en-IN-Wavenet-D', -- [8] FEMALE + ["en_GB_Wavenet_A"] = 'en-GB-Wavenet-A', -- [9] FEMALE + ["en_GB_Wavenet_B"] = 'en-GB-Wavenet-B', -- [10] MALE + ["en_GB_Wavenet_C"] = 'en-GB-Wavenet-C', -- [11] FEMALE + ["en_GB_Wavenet_D"] = 'en-GB-Wavenet-D', -- [12] MALE + ["en_GB_Wavenet_F"] = 'en-GB-Wavenet-F', -- [13] FEMALE + ["en_US_Wavenet_A"] = 'en-US-Wavenet-A', -- [14] MALE + ["en_US_Wavenet_B"] = 'en-US-Wavenet-B', -- [15] MALE + ["en_US_Wavenet_C"] = 'en-US-Wavenet-C', -- [16] FEMALE + ["en_US_Wavenet_D"] = 'en-US-Wavenet-D', -- [17] MALE + ["en_US_Wavenet_E"] = 'en-US-Wavenet-E', -- [18] FEMALE + ["en_US_Wavenet_F"] = 'en-US-Wavenet-F', -- [19] FEMALE + ["en_US_Wavenet_G"] = 'en-US-Wavenet-G', -- [20] FEMALE + ["en_US_Wavenet_H"] = 'en-US-Wavenet-H', -- [21] FEMALE + ["en_US_Wavenet_I"] = 'en-US-Wavenet-I', -- [22] MALE + ["en_US_Wavenet_J"] = 'en-US-Wavenet-J', -- [23] MALE + ["fr_FR_Wavenet_A"] = "fr-FR-Wavenet-A", -- Female + ["fr_FR_Wavenet_B"] = "fr-FR-Wavenet-B", -- Male + ["fr_FR_Wavenet_C"] = "fr-FR-Wavenet-C", -- Female + ["fr_FR_Wavenet_D"] = "fr-FR-Wavenet-D", -- Male + ["fr_FR_Wavenet_E"] = "fr-FR-Wavenet-E", -- Female + ["de_DE_Wavenet_A"] = "de-DE-Wavenet-A", -- Female + ["de_DE_Wavenet_B"] = "de-DE-Wavenet-B", -- Male + ["de_DE_Wavenet_C"] = "de-DE-Wavenet-C", -- Female + ["de_DE_Wavenet_D"] = "de-DE-Wavenet-D", -- Male + ["de_DE_Wavenet_E"] = "de-DE-Wavenet-E", -- Male + ["de_DE_Wavenet_F"] = "de-DE-Wavenet-F", -- Female + ["es_ES_Wavenet_B"] = "es-ES-Wavenet-B", -- Male + ["es_ES_Wavenet_C"] = "es-ES-Wavenet-C", -- Female + ["es_ES_Wavenet_D"] = "es-ES-Wavenet-D", -- Female + ["it_IT_Wavenet_A"] = "it-IT-Wavenet-A", -- Female + ["it_IT_Wavenet_B"] = "it-IT-Wavenet-B", -- Female + ["it_IT_Wavenet_C"] = "it-IT-Wavenet-C", -- Male + ["it_IT_Wavenet_D"] = "it-IT-Wavenet-D", -- Male } , }, } From e4002233ef013adf9914221894181e4aec404ae6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 25 Sep 2022 14:40:47 +0200 Subject: [PATCH 032/603] #FlightControl - Option to set Google key once on New() --- Moose Development/Moose/Ops/FlightControl.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index b10b2471d..54fe4ef31 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -358,8 +358,9 @@ FLIGHTCONTROL.version="0.7.3" -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a `#table` of multiple modulations. -- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #number Port Port of SRS Server, defaults to 5002 +-- @param #string GoogleKey Path to the Google JSON-Key. -- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port) +function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port, GoogleKey) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #FLIGHTCONTROL @@ -419,11 +420,13 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port) -- SRS for Tower. self.msrsTower=MSRS:New(PathToSRS, Frequency, Modulation) self.msrsTower:SetPort(self.Port) + self.msrsTower:SetGoogle(GoogleKey) self:SetSRSTower() -- SRS for Pilot. self.msrsPilot=MSRS:New(PathToSRS, Frequency, Modulation) self.msrsPilot:SetPort(self.Port) + self.msrsPilot:SetGoogle(GoogleKey) self:SetSRSPilot() -- Wait at least 10 seconds after last radio message before calling the next status update. From 8806c91dd905ca77a97b384623331062da488db8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 25 Sep 2022 14:41:00 +0200 Subject: [PATCH 033/603] ATIS - added google option --- Moose Development/Moose/Ops/ATIS.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 867404df2..591129617 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1126,8 +1126,9 @@ end -- @param #string Culture Culture, e.g. "en-GB" (default). -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. -- @param #number Port SRS port. Default 5002. +-- @param #string GoogleKey Path to Google JSON-Key. -- @return #ATIS self -function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port) +function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) if PathToSRS then self.useSRS=true self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation) @@ -1137,6 +1138,7 @@ function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port) self.msrs:SetPort(Port) self.msrs:SetCoalition(self:GetCoalition()) self.msrs:SetLabel("ATIS") + self.msrs:SetGoogle(GoogleKey) self.msrsQ = MSRSQUEUE:New("ATIS") if self.dTQueueCheck<=10 then self:SetQueueUpdateTime(90) From 60d25ea1317179d70ca738477e7510f4cb8936a0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 25 Sep 2022 15:04:24 +0200 Subject: [PATCH 034/603] #AWACS - corrected aggressive --- Moose Development/Moose/Ops/Awacs.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index b9e614289..e1e9bea0b 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -327,7 +327,7 @@ do -- * @{#AWACS.SetRadarBlur}() : Set the radar blur faktor in percent. -- * @{#AWACS.SetColdWar}() : Set to cold war - no fill-ins, no EPLRS, VID as standard. -- * @{#AWACS.SetModernEraDefensive}() : Set to modern, EPLRS, BVR/IFF engagement, fill-ins. --- * @{#AWACS.SetModernEraAgressive}() : Set to modern, EPLRS, BVR/IFF engagement, fill-ins. +-- * @{#AWACS.SetModernEraAggressive}() : Set to modern, EPLRS, BVR/IFF engagement, fill-ins. -- * @{#AWACS.SetPolicingModern}() : Set to modern, EPLRS, VID engagement, fill-ins. -- * @{#AWACS.SetPolicingColdWar}() : Set to cold war, no EPLRS, VID engagement, no fill-ins. -- * @{#AWACS.SetInterceptTimeline}() : Set distances for TAC, Meld and Threat range calls. @@ -1714,7 +1714,7 @@ end --- [User] Set AWACS to Modern Era standards - ROE to BVR, ROT to return fire. Radar blur 15%. -- @param #AWACS self -- @return #AWACS self -function AWACS:SetModernEraAgressive() +function AWACS:SetModernEraAggressive() self.ModernEra = true self.AwacsROT = AWACS.ROT.RETURNFIRE self.AwacsROE = AWACS.ROE.BVR From 811c37549a9d20454d50fca2618769d5da211f2b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 27 Sep 2022 11:15:44 +0200 Subject: [PATCH 035/603] #ATIS * Fixed SetILS report not working * Use new AIRBASE additions to set takeoff/landing runway * Fixed Visibility is reported twice * Added SetReportmBar() to report for mBar/hpa QNH/QFE even if not metric * Added option to output additional freetext information when using SRS SetAdditionalInformation() #1792 --- Moose Development/Moose/Ops/ATIS.lua | 107 ++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 591129617..96c67805c 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -91,6 +91,7 @@ -- @field #boolean useSRS If true, use SRS for transmission. -- @field Sound.SRS#MSRS msrs Moose SRS object. -- @field #number dTQueueCheck Time interval to check the radio queue. Default 5 sec or 90 sec if SRS is used. +-- @field #boolean ReportmBar Report mBar/hpa even if not metric, i.e. for Mirage flights -- @extends Core.Fsm#FSM --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -344,6 +345,7 @@ ATIS = { usemarker = nil, markerid = nil, relHumidity = nil, + ReportmBar = false, } --- NATO alphabet. @@ -586,15 +588,18 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.9.8" +ATIS.version = "0.9.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Add new Normany airfields. +-- TODO: Add new Normandy airfields. -- TODO: Zulu time --> Zulu in output. -- TODO: Correct fog for elevation. +-- DONE: Use new AIRBASE system to set start/landing runway +-- DONE: SetILS doesn't work +-- DONE: Visibility reported twice over SRS -- DONE: Add text report for output. -- DONE: Add stop FMS functions. -- NOGO: Use local time. Not realisitc! @@ -651,6 +656,7 @@ function ATIS:New(AirbaseName, Frequency, Modulation) self:SetMapMarks( false ) self:SetRelativeHumidity() self:SetQueueUpdateTime() + self:SetReportmBar(false) -- Start State. self:SetStartState( "Stopped" ) @@ -774,13 +780,40 @@ function ATIS:SetTowerFrequencies( freqs ) return self end ---- Set active runway. This can be used if the automatic runway determination via the wind direction gives incorrect results. +--- Set active runway for **landing** operations. This can be used if the automatic runway determination via the wind direction gives incorrect results. -- For example, use this if there are two runways with the same directions. -- @param #ATIS self -- @param #string runway Active runway, *e.g.* "31L". -- @return #ATIS self function ATIS:SetActiveRunway( runway ) self.activerunway = tostring( runway ) + local prefer = nil + if string.find(string.lower(runway),"l") then + prefer = true + elseif string.find(string.lower(runway),"r") then + prefer = false + end + self.airbase:SetActiveRunway(runway,prefer) + return self +end + +--- Set the active runway for landing. +-- @param #ATIS self +-- @param #string runway : Name of the runway, e.g. "31" or "02L" or "90R". If not given, the runway is determined from the wind direction. +-- @param #boolean preferleft : If true, perfer the left runway. If false, prefer the right runway. If nil (default), do not care about left or right. +-- @return #ATIS self +function ATIS:SetActiveRunwayLanding(runway, preferleft) + self.airbase:SetActiveRunwayLanding(runway,preferleft) + return self +end + +--- Set the active runway for take-off. +-- @param #ATIS self +-- @param #string runway : Name of the runway, e.g. "31" or "02L" or "90R". If not given, the runway is determined from the wind direction. +-- @param #boolean preferleft : If true, perfer the left runway. If false, prefer the right runway. If nil (default), do not care about left or right. +-- @return #ATIS self +function ATIS:SetActiveRunwayTakeoff(runway,preferleft) + self.airbase:SetActiveRunwayTakeoff(runway,preferleft) return self end @@ -947,6 +980,28 @@ function ATIS:SetAltimeterQNH( switch ) return self end +--- Additionally report altimeter QNH/QFE in hPa, even if not set to metric. +-- @param #ATIS self +-- @param #boolean switch If true or nil, report mBar/hPa in addition. +-- @return #ATIS self +function ATIS:SetReportmBar(switch) + if switch == true or switch == nil then + self.ReportmBar = true + else + self.ReportmBar = false + end + return self +end + +--- Additionally report free text, only working with SRS(!) +-- @param #ATIS self +-- @param #string text The text to report at the end of the ATIS message, e.g. runway closure, warnings, etc. +-- @return #ATIS self +function ATIS:SetAdditionalInformation(text) + self.AdditionalInformation = text + return self +end + --- Suppresses QFE readout. Default is to report both QNH and QFE. -- @param #ATIS self -- @return #ATIS self @@ -1327,7 +1382,10 @@ function ATIS:onafterBroadcast( From, Event, To ) qnh = Q / 100 end - + + local mBarqnh = qnh + local mBarqfe = qfe + -- Convert to inHg. if self.PmmHg then qfe = UTILS.hPa2mmHg( qfe ) @@ -1778,7 +1836,9 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle - + --self:I("Line 1811") + --self:I(alltext) + -- Visibility if self.metric then subtitle = string.format( "Visibility %s km", VISIBILITY ) @@ -1795,7 +1855,10 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle - + --self:I("Line 1830") + --self:I(alltext) + + subtitle = "" -- Weather phenomena local wp = false local wpsub = "" @@ -1895,8 +1958,11 @@ function ATIS:onafterBroadcast( From, Event, To ) end end end - alltext = alltext .. ";\n" .. subtitle + --self:I("Line 1932") + alltext = alltext .. ";\n" .. subtitle + --self:I(alltext) + subtitle = "" -- Temperature if self.TDegF then if temperature < 0 then @@ -1924,8 +1990,10 @@ function ATIS:onafterBroadcast( From, Event, To ) self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 ) end end + --self:I("Line 1962") alltext = alltext .. ";\n" .. subtitle - + --self:I(alltext) + -- Dew point if self.TDegF then if dewpoint < 0 then @@ -1953,6 +2021,8 @@ function ATIS:onafterBroadcast( From, Event, To ) self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 ) end end + --self:I("Line 1992") + --self:I(alltext) alltext = alltext .. ";\n" .. subtitle -- Altimeter QNH/QFE. @@ -1977,6 +2047,15 @@ function ATIS:onafterBroadcast( From, Event, To ) end end end + + if self.ReportmBar and not self.metric then + if self.qnhonly then + subtitle = string.format( "%s;\nAltimeter %d hPa", subtitle, mBarqnh ) + else + subtitle = string.format( "%s;\nAltimeter: QNH %d, QFE %d hPa", subtitle, mBarqnh, mBarqfe) + end + end + local _ALTIMETER = subtitle if not self.useSRS then self:Transmission( ATIS.Sound.Altimeter, 1.0, subtitle ) @@ -2009,6 +2088,8 @@ function ATIS:onafterBroadcast( From, Event, To ) end end end + --self:I("Line 2049") + --self:I(alltext) alltext = alltext .. ";\n" .. subtitle -- Active runway. @@ -2136,7 +2217,9 @@ function ATIS:onafterBroadcast( From, Event, To ) end -- ILS + --self:I({ils=self.ils}) local ils=self:GetNavPoint(self.ils, runwayLanding, rwyLandingLeft) + --self:I({ils=ils,runwayLanding=runwayLanding, rwyLandingLeft=rwyLandingLeft}) if ils then subtitle = string.format( "ILS frequency %.2f MHz", ils.frequency ) if not self.useSRS then @@ -2151,6 +2234,7 @@ function ATIS:onafterBroadcast( From, Event, To ) self:Transmission( ATIS.Sound.MegaHertz, 0.2 ) end alltext = alltext .. ";\n" .. subtitle + --self:I(alltext) end -- Outer NDB @@ -2240,7 +2324,12 @@ function ATIS:onafterBroadcast( From, Event, To ) end alltext = alltext .. ";\n" .. subtitle end - + + -- additional info, if any + if self.useSRS and self.AdditionalInformation then + alltext = alltext .. ";\n"..self.AdditionalInformation + end + -- Advice on initial... subtitle = string.format( "Advise on initial contact, you have information %s", NATO ) if not self.useSRS then From 5e5f1398fad920560e580f7422a3571e2d559a35 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 28 Sep 2022 11:49:56 +0200 Subject: [PATCH 036/603] #ZONE_CAPTURE_COALITION - allow zone to be a ZONE_POLYGON #RANGE Messaging changes in case >1 player per group --- Moose Development/Moose/Core/Zone.lua | 296 +++++++++++++++++- Moose Development/Moose/Functional/Range.lua | 66 ++-- .../Moose/Functional/ZoneCaptureCoalition.lua | 2 +- .../Moose/Functional/ZoneGoal.lua | 13 +- 4 files changed, 340 insertions(+), 37 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 081fad214..145c13155 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -910,9 +910,6 @@ function ZONE_RADIUS:GetVec3( Height ) end - - - --- Scan the zone for the presence of units of the given ObjectCategories. -- Note that **only after** a zone has been scanned, the zone can be evaluated by: -- @@ -921,7 +918,6 @@ end -- * @{ZONE_RADIUS.IsSomeInZoneOfCoalition}(): Scan if there is some presence of units in the zone of the given coalition. -- * @{ZONE_RADIUS.IsNoneInZoneOfCoalition}(): Scan if there isn't any presence of units in the zone of an other coalition than the given one. -- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty. --- @{#ZONE_RADIUS. -- @param #ZONE_RADIUS self -- @param ObjectCategories An array of categories of the objects to find in the zone. E.g. `{Object.Category.UNIT}` -- @param UnitCategories An array of unit categories of the objects to find in the zone. E.g. `{Unit.Category.GROUND_UNIT,Unit.Category.SHIP}` @@ -953,7 +949,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) if ZoneObject then local ObjectCategory = ZoneObject:getCategory() - + --local name=ZoneObject:getName() --env.info(string.format("Zone object %s", tostring(name))) --self:E(ZoneObject) @@ -2417,6 +2413,296 @@ function ZONE_POLYGON:FindByName( ZoneName ) return ZoneFound end +--- Scan the zone for the presence of units of the given ObjectCategories. Does **not** scan for scenery at the moment. +-- Note that **only after** a zone has been scanned, the zone can be evaluated by: +-- +-- * @{ZONE_POLYGON.IsAllInZoneOfCoalition}(): Scan the presence of units in the zone of a coalition. +-- * @{ZONE_POLYGON.IsAllInZoneOfOtherCoalition}(): Scan the presence of units in the zone of an other coalition. +-- * @{ZONE_POLYGON.IsSomeInZoneOfCoalition}(): Scan if there is some presence of units in the zone of the given coalition. +-- * @{ZONE_POLYGON.IsNoneInZoneOfCoalition}(): Scan if there isn't any presence of units in the zone of an other coalition than the given one. +-- * @{ZONE_POLYGON.IsNoneInZone}(): Scan if the zone is empty. +-- @param #ZONE_POLYGON self +-- @param ObjectCategories An array of categories of the objects to find in the zone. E.g. `{Object.Category.UNIT}` +-- @param UnitCategories An array of unit categories of the objects to find in the zone. E.g. `{Unit.Category.GROUND_UNIT,Unit.Category.SHIP}` +-- @usage +-- myzone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT}) +-- local IsAttacked = myzone:IsSomeInZoneOfCoalition( self.Coalition ) +function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories ) + + self.ScanData = {} + self.ScanData.Coalitions = {} + self.ScanData.Scenery = {} + self.ScanData.Units = {} + + local function EvaluateZone( ZoneObject ) + + if ZoneObject then + + local ObjectCategory = ZoneObject:getCategory() + + if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then + + local CoalitionDCSUnit = ZoneObject:getCoalition() + + local Include = false + if not UnitCategories then + -- Anything found is included. + Include = true + else + -- Check if found object is in specified categories. + local CategoryDCSUnit = ZoneObject:getDesc().category + + for UnitCategoryID, UnitCategory in pairs( UnitCategories ) do + if UnitCategory == CategoryDCSUnit then + Include = true + break + end + end + + end + + if Include then + + local CoalitionDCSUnit = ZoneObject:getCoalition() + + -- This coalition is inside the zone. + self.ScanData.Coalitions[CoalitionDCSUnit] = true + + self.ScanData.Units[ZoneObject] = ZoneObject + + self:F2( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } ) + end + end + + --[[ + -- no scenery possible at the moment + if ObjectCategory == Object.Category.SCENERY then + local SceneryType = ZoneObject:getTypeName() + local SceneryName = ZoneObject:getName() + self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {} + self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject ) + self:T( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } ) + end + --]] + end + + return true + end + + -- Search objects. + local inzoneunits = SET_UNIT:New():FilterZones({self}):FilterOnce() + local inzonestatics = SET_STATIC:New():FilterZones({self}):FilterOnce() + + inzoneunits:ForEach( + function(unit) + local Unit = unit --Wrapper.Unit#UNIT + local DCS = Unit:GetDCSObject() + EvaluateZone(DCS) + end + ) + + inzonestatics:ForEach( + function(static) + local Static = static --Wrapper.Static#STATIC + local DCS = Static:GetDCSObject() + EvaluateZone(DCS) + end + ) + +end + +--- Count the number of different coalitions inside the zone. +-- @param #ZONE_POLYGON self +-- @return #table Table of DCS units and DCS statics inside the zone. +function ZONE_POLYGON:GetScannedUnits() + return self.ScanData.Units +end + +--- Get a set of scanned units. +-- @param #ZONE_POLYGON self +-- @return Core.Set#SET_UNIT Set of units and statics inside the zone. +function ZONE_POLYGON:GetScannedSetUnit() + + local SetUnit = SET_UNIT:New() + + if self.ScanData then + for ObjectID, UnitObject in pairs( self.ScanData.Units ) do + local UnitObject = UnitObject -- DCS#Unit + if UnitObject:isExist() then + local FoundUnit = UNIT:FindByName( UnitObject:getName() ) + if FoundUnit then + SetUnit:AddUnit( FoundUnit ) + else + local FoundStatic = STATIC:FindByName( UnitObject:getName() ) + if FoundStatic then + SetUnit:AddUnit( FoundStatic ) + end + end + end + end + end + + return SetUnit +end + +--- Get a set of scanned units. +-- @param #ZONE_POLYGON self +-- @return Core.Set#SET_GROUP Set of groups. +function ZONE_POLYGON:GetScannedSetGroup() + + self.ScanSetGroup=self.ScanSetGroup or SET_GROUP:New() --Core.Set#SET_GROUP + + self.ScanSetGroup.Set={} + + if self.ScanData then + for ObjectID, UnitObject in pairs( self.ScanData.Units ) do + local UnitObject = UnitObject -- DCS#Unit + if UnitObject:isExist() then + + local FoundUnit=UNIT:FindByName(UnitObject:getName()) + if FoundUnit then + local group=FoundUnit:GetGroup() + self.ScanSetGroup:AddGroup(group) + end + end + end + end + + return self.ScanSetGroup +end + +--- Count the number of different coalitions inside the zone. +-- @param #ZONE_POLYGON self +-- @return #number Counted coalitions. +function ZONE_POLYGON:CountScannedCoalitions() + + local Count = 0 + + for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do + Count = Count + 1 + end + + return Count +end + +--- Check if a certain coalition is inside a scanned zone. +-- @param #ZONE_POLYGON self +-- @param #number Coalition The coalition id, e.g. coalition.side.BLUE. +-- @return #boolean If true, the coalition is inside the zone. +function ZONE_POLYGON:CheckScannedCoalition( Coalition ) + if Coalition then + return self.ScanData.Coalitions[Coalition] + end + return nil +end + +--- Get Coalitions of the units in the Zone, or Check if there are units of the given Coalition in the Zone. +-- Returns nil if there are none to two Coalitions in the zone! +-- Returns one Coalition if there are only Units of one Coalition in the Zone. +-- Returns the Coalition for the given Coalition if there are units of the Coalition in the Zone. +-- @param #ZONE_POLYGON self +-- @return #table +function ZONE_POLYGON:GetScannedCoalition( Coalition ) + + if Coalition then + return self.ScanData.Coalitions[Coalition] + else + local Count = 0 + local ReturnCoalition = nil + + for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do + Count = Count + 1 + ReturnCoalition = CoalitionID + end + + if Count ~= 1 then + ReturnCoalition = nil + end + + return ReturnCoalition + end +end + +--- Get scanned scenery type (currently not implemented in ZONE_POLYGON) +-- @param #ZONE_POLYGON self +-- @return #table Table of DCS scenery type objects. +function ZONE_POLYGON:GetScannedSceneryType( SceneryType ) + return self.ScanData.Scenery[SceneryType] +end + +--- Get scanned scenery table (currently not implemented in ZONE_POLYGON) +-- @param #ZONE_POLYGON self +-- @return #table Table of DCS scenery objects. +function ZONE_POLYGON:GetScannedScenery() + return self.ScanData.Scenery +end + +--- Is All in Zone of Coalition? +-- Check if only the specifed coalition is inside the zone and noone else. +-- @param #ZONE_POLYGON self +-- @param #number Coalition Coalition ID of the coalition which is checked to be the only one in the zone. +-- @return #boolean True, if **only** that coalition is inside the zone and no one else. +-- @usage +-- self.Zone:Scan() +-- local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition ) +function ZONE_POLYGON:IsAllInZoneOfCoalition( Coalition ) + return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == true +end + +--- Is All in Zone of Other Coalition? +-- Check if only one coalition is inside the zone and the specified coalition is not the one. +-- You first need to use the @{#ZONE_POLYGON.Scan} method to scan the zone before it can be evaluated! +-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set. +-- @param #ZONE_POLYGON self +-- @param #number Coalition Coalition ID of the coalition which is not supposed to be in the zone. +-- @return #boolean True, if and only if only one coalition is inside the zone and the specified coalition is not it. +-- @usage +-- self.Zone:Scan() +-- local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition ) +function ZONE_POLYGON:IsAllInZoneOfOtherCoalition( Coalition ) + return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == nil +end + +--- Is Some in Zone of Coalition? +-- Check if more than one coaltion is inside the zone and the specifed coalition is one of them. +-- You first need to use the @{#ZONE_POLYGON.Scan} method to scan the zone before it can be evaluated! +-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set. +-- @param #ZONE_POLYGON self +-- @param #number Coalition ID of the coaliton which is checked to be inside the zone. +-- @return #boolean True if more than one coalition is inside the zone and the specified coalition is one of them. +-- @usage +-- self.Zone:Scan() +-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition ) +function ZONE_POLYGON:IsSomeInZoneOfCoalition( Coalition ) + return self:CountScannedCoalitions() > 1 and self:GetScannedCoalition( Coalition ) == true +end + +--- Is None in Zone of Coalition? +-- You first need to use the @{#ZONE_POLYGON.Scan} method to scan the zone before it can be evaluated! +-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set. +-- @param #ZONE_POLYGON self +-- @param Coalition +-- @return #boolean +-- @usage +-- self.Zone:Scan() +-- local IsOccupied = self.Zone:IsNoneInZoneOfCoalition( self.Coalition ) +function ZONE_POLYGON:IsNoneInZoneOfCoalition( Coalition ) + return self:GetScannedCoalition( Coalition ) == nil +end + +--- Is None in Zone? +-- You first need to use the @{#ZONE_POLYGON.Scan} method to scan the zone before it can be evaluated! +-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set. +-- @param #ZONE_POLYGON self +-- @return #boolean +-- @usage +-- self.Zone:Scan() +-- local IsEmpty = self.Zone:IsNoneInZone() +function ZONE_POLYGON:IsNoneInZone() + return self:CountScannedCoalitions() == 0 +end + + do -- ZONE_ELASTIC --- @type ZONE_ELASTIC diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 35697c148..542be6b70 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -2570,7 +2570,7 @@ function RANGE:_DisplayMyStrafePitResults( _unitName ) self:F( _unitName ) -- Get player unit and name - local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) + local _unit, _playername, _multiplayer = self:_GetPlayerUnitAndName( _unitName ) if _unit and _playername then @@ -2622,7 +2622,7 @@ function RANGE:_DisplayMyStrafePitResults( _unitName ) end -- Send message to group. - self:_DisplayMessageToGroup( _unit, _message, nil, true, true ) + self:_DisplayMessageToGroup( _unit, _message, nil, true, true, _multiplayer ) end end @@ -2633,7 +2633,7 @@ function RANGE:_DisplayStrafePitResults( _unitName ) self:F( _unitName ) -- Get player unit and name. - local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) + local _unit, _playername, _multiplayer = self:_GetPlayerUnitAndName( _unitName ) -- Check if we have a unit which is a player. if _unit and _playername then @@ -2680,7 +2680,7 @@ function RANGE:_DisplayStrafePitResults( _unitName ) end -- Send message. - self:_DisplayMessageToGroup( _unit, _message, nil, true, true ) + self:_DisplayMessageToGroup( _unit, _message, nil, true, true, _multiplayer ) end end @@ -2691,7 +2691,7 @@ function RANGE:_DisplayMyBombingResults( _unitName ) self:F( _unitName ) -- Get player unit and name. - local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) + local _unit, _playername, _multiplayer = self:_GetPlayerUnitAndName( _unitName ) if _unit and _playername then @@ -2737,7 +2737,7 @@ function RANGE:_DisplayMyBombingResults( _unitName ) end -- Send message. - self:_DisplayMessageToGroup( _unit, _message, nil, true, true ) + self:_DisplayMessageToGroup( _unit, _message, nil, true, true, _multiplayer ) end end @@ -2751,7 +2751,7 @@ function RANGE:_DisplayBombingResults( _unitName ) local _playerResults = {} -- Get player unit and name. - local _unit, _player = self:_GetPlayerUnitAndName( _unitName ) + local _unit, _player, _multiplayer = self:_GetPlayerUnitAndName( _unitName ) -- Check if we have a unit with a player. if _unit and _player then @@ -2795,7 +2795,7 @@ function RANGE:_DisplayBombingResults( _unitName ) end -- Send message. - self:_DisplayMessageToGroup( _unit, _message, nil, true, true ) + self:_DisplayMessageToGroup( _unit, _message, nil, true, true, _multiplayer ) end end @@ -2806,7 +2806,7 @@ function RANGE:_DisplayRangeInfo( _unitname ) self:F( _unitname ) -- Get player unit and player name. - local unit, playername = self:_GetPlayerUnitAndName( _unitname ) + local unit, playername, _multiplayer = self:_GetPlayerUnitAndName( _unitname ) -- Check if we have a player. if unit and playername then @@ -2901,7 +2901,7 @@ function RANGE:_DisplayRangeInfo( _unitname ) text = text .. textdelay -- Send message to player group. - self:_DisplayMessageToGroup( unit, text, nil, true, true ) + self:_DisplayMessageToGroup( unit, text, nil, true, true, _multiplayer ) -- Debug output. self:T2( self.id .. text ) @@ -2916,7 +2916,7 @@ function RANGE:_DisplayBombTargets( _unitname ) self:F( _unitname ) -- Get player unit and player name. - local _unit, _playername = self:_GetPlayerUnitAndName( _unitname ) + local _unit, _playername, _multiplayer = self:_GetPlayerUnitAndName( _unitname ) -- Check if we have a player. if _unit and _playername then @@ -2948,7 +2948,7 @@ function RANGE:_DisplayBombTargets( _unitname ) end end - self:_DisplayMessageToGroup( _unit, _text, 120, true, true ) + self:_DisplayMessageToGroup( _unit, _text, 120, true, true, _multiplayer ) end end @@ -2959,7 +2959,7 @@ function RANGE:_DisplayStrafePits( _unitname ) self:F( _unitname ) -- Get player unit and player name. - local _unit, _playername = self:_GetPlayerUnitAndName( _unitname ) + local _unit, _playername, _multiplayer = self:_GetPlayerUnitAndName( _unitname ) -- Check if we have a player. if _unit and _playername then @@ -2988,7 +2988,7 @@ function RANGE:_DisplayStrafePits( _unitname ) _text = _text .. string.format( "\n- %s: heading %03d°\n%s", _strafepit.name, heading, mycoord ) end - self:_DisplayMessageToGroup( _unit, _text, nil, true, true ) + self:_DisplayMessageToGroup( _unit, _text, nil, true, true, _multiplayer ) end end @@ -2999,7 +2999,7 @@ function RANGE:_DisplayRangeWeather( _unitname ) self:F( _unitname ) -- Get player unit and player name. - local unit, playername = self:_GetPlayerUnitAndName( _unitname ) + local unit, playername, _multiplayer = self:_GetPlayerUnitAndName( _unitname ) -- Check if we have a player. if unit and playername then @@ -3048,7 +3048,7 @@ function RANGE:_DisplayRangeWeather( _unitname ) end -- Send message to player group. - self:_DisplayMessageToGroup( unit, text, nil, true, true ) + self:_DisplayMessageToGroup( unit, text, nil, true, true, _multiplayer ) -- Debug output. self:T2( self.id .. text ) @@ -3666,7 +3666,8 @@ end -- @param #number _time Duration how long the message is displayed. -- @param #boolean _clear Clear up old messages. -- @param #boolean display If true, display message regardless of player setting "Messages Off". -function RANGE:_DisplayMessageToGroup( _unit, _text, _time, _clear, display ) +-- @param #boolean _togroup If true, display the message to the group in any case +function RANGE:_DisplayMessageToGroup( _unit, _text, _time, _clear, display, _togroup ) self:F( { unit = _unit, text = _text, time = _time, clear = _clear } ) -- Defaults @@ -3694,8 +3695,13 @@ function RANGE:_DisplayMessageToGroup( _unit, _text, _time, _clear, display ) local playermessage = self.PlayerSettings[playername].messages -- Send message to player if messages enabled and not only for the examiner. + if _gid and (playermessage == true or display) and (not self.examinerexclusive) then - local m = MESSAGE:New(_text,_time,nil,_clear):ToUnit(_unit) + if _togroup and _grp then + local m = MESSAGE:New(_text,_time,nil,_clear):ToGroup(_grp) + else + local m = MESSAGE:New(_text,_time,nil,_clear):ToUnit(_unit) + end end -- Send message to examiner. @@ -3715,7 +3721,7 @@ end function RANGE:_SmokeBombImpactOnOff( unitname ) self:F( unitname ) - local unit, playername = self:_GetPlayerUnitAndName( unitname ) + local unit, playername, _multiplayer = self:_GetPlayerUnitAndName( unitname ) if unit and playername then local text if self.PlayerSettings[playername].smokebombimpact == true then @@ -3736,7 +3742,7 @@ end function RANGE:_SmokeBombDelayOnOff( unitname ) self:F( unitname ) - local unit, playername = self:_GetPlayerUnitAndName( unitname ) + local unit, playername, _multiplayer = self:_GetPlayerUnitAndName( unitname ) if unit and playername then local text if self.PlayerSettings[playername].delaysmoke == true then @@ -3757,7 +3763,7 @@ end function RANGE:_MessagesToPlayerOnOff( unitname ) self:F( unitname ) - local unit, playername = self:_GetPlayerUnitAndName( unitname ) + local unit, playername, _multiplayer = self:_GetPlayerUnitAndName( unitname ) if unit and playername then local text if self.PlayerSettings[playername].messages == true then @@ -3778,7 +3784,7 @@ function RANGE:_TargetsheetOnOff( _unitname ) self:F2( _unitname ) -- Get player unit and player name. - local unit, playername = self:_GetPlayerUnitAndName( _unitname ) + local unit, playername, _multiplayer = self:_GetPlayerUnitAndName( _unitname ) -- Check if we have a player. if unit and playername then @@ -3820,7 +3826,7 @@ end function RANGE:_FlareDirectHitsOnOff( unitname ) self:F( unitname ) - local unit, playername = self:_GetPlayerUnitAndName( unitname ) + local unit, playername, _multiplayer = self:_GetPlayerUnitAndName( unitname ) if unit and playername then local text if self.PlayerSettings[playername].flaredirecthits == true then @@ -4039,12 +4045,14 @@ end -- @param #string _unitName Name of the player unit. -- @return Wrapper.Unit#UNIT Unit of player. -- @return #string Name of the player. --- @return nil If player does not exist. +-- @return #boolean If true, group has > 1 player in it function RANGE:_GetPlayerUnitAndName( _unitName ) self:F2( _unitName ) if _unitName ~= nil then - + + local multiplayer = false + -- Get DCS unit from its name. local DCSunit = Unit.getByName( _unitName ) @@ -4056,7 +4064,11 @@ function RANGE:_GetPlayerUnitAndName( _unitName ) self:T2( { DCSunit = DCSunit, unit = unit, playername = playername } ) if DCSunit and unit and playername then self:F2(playername) - return unit, playername + local grp = unit:GetGroup() + if grp and grp:CountAliveUnits() > 1 then + multiplayer = true + end + return unit, playername, multiplayer end end @@ -4064,7 +4076,7 @@ function RANGE:_GetPlayerUnitAndName( _unitName ) end -- Return nil if we could not find a player. - return nil, nil + return nil, nil, nil end --- Returns a string which consists of the player name. diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index 9be2c35bf..e8abc02dd 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -363,7 +363,7 @@ do -- ZONE_CAPTURE_COALITION --- ZONE_CAPTURE_COALITION Constructor. -- @param #ZONE_CAPTURE_COALITION self - -- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved. + -- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved. Alternatively, can be handed as the name of late activated group describing a @{ZONE_POLYGON} with its waypoints. -- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. -- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}. -- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS. diff --git a/Moose Development/Moose/Functional/ZoneGoal.lua b/Moose Development/Moose/Functional/ZoneGoal.lua index cd55b2603..eb72c4221 100644 --- a/Moose Development/Moose/Functional/ZoneGoal.lua +++ b/Moose Development/Moose/Functional/ZoneGoal.lua @@ -59,10 +59,15 @@ do -- Zone -- @param Core.Zone#ZONE_RADIUS Zone A @{Zone} object with the goal to be achieved. -- @return #ZONE_GOAL function ZONE_GOAL:New( Zone ) - - local self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL - self:F( { Zone = Zone } ) - + + BASE:I({Zone=Zone}) + local self = BASE:Inherit( self, BASE:New()) + if type(Zone) == "string" then + self = BASE:Inherit( self, ZONE_POLYGON:NewFromGroupName(Zone) ) + else + self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL + self:F( { Zone = Zone } ) + end -- Goal object. self.Goal = GOAL:New() From 7d938ac8fa3433ce44cb9e3fa8d0f11e30140f11 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 28 Sep 2022 13:06:54 +0200 Subject: [PATCH 037/603] #PLAYERTASK - a target can only be smoked again after 5 mins (that's how long smoke lasts) #PLAYERTASKCONTROLLER - added option to hide smoke&flare menus --- Moose Development/Moose/Ops/PlayerTask.lua | 38 +++++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 2202b73ee..18ed99d5e 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -49,6 +49,7 @@ do -- @field #table conditionFailure = {}, -- @field Ops.PlayerTask#PLAYERTASKCONTROLLER TaskController -- @field #number timestamp +-- @field #number lastsmoketime -- @extends Core.Fsm#FSM @@ -76,11 +77,12 @@ PLAYERTASK = { conditionFailure = {}, TaskController = nil, timestamp = 0, + lastsmoketime = 0, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.2" +PLAYERTASK.version="0.1.3" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -112,6 +114,7 @@ function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType) self.TaskController = nil -- Ops.PlayerTask#PLAYERTASKCONTROLLER self.timestamp = timer.getAbsTime() self.TTSType = TTSType or "close air support" + self.lastsmoketime = 0 if Repeat then self.Repeat = true @@ -392,10 +395,13 @@ end function PLAYERTASK:SmokeTarget(Color) self:T(self.lid.."SmokeTarget") local color = Color or SMOKECOLOR.Red - if self.Target then + if not self.lastsmoketime then self.lastsmoketime = 0 end + local TDiff = timer.getAbsTime() - self.lastsmoketime + if self.Target and TDiff > 299 then local coordinate = self.Target:GetCoordinate() if coordinate then coordinate:Smoke(color) + self.lastsmoketime = timer.getAbsTime() end end return self @@ -754,6 +760,7 @@ do -- @field #table PlayerFlashMenu -- @field #table PlayerJoinMenu -- @field #table PlayerInfoMenu +-- @field #boolean noflaresmokemenu -- @extends Core.Fsm#FSM --- @@ -1055,6 +1062,7 @@ PLAYERTASKCONTROLLER = { PlayerFlashMenu = {}, PlayerJoinMenu = {}, PlayerInfoMenu = {}, + noflaresmokemenu = false, } --- @@ -1213,7 +1221,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.36" +PLAYERTASKCONTROLLER.version="0.1.37" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1267,6 +1275,8 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.ShortCallsign = true self.Keepnumber = false self.CallsignTranslations = nil + + self.noflaresmokemenu = false if ClientFilter then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart() @@ -1388,6 +1398,24 @@ function PLAYERTASKCONTROLLER:SetAllowFlashDirection(OnOff) return self end +--- [User] Do not show menu entries to smoke or flare targets +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetDisableSmokeFlareTask() + self:T(self.lid.."SetDisableSmokeFlareTask") + self.noflaresmokemenu = true + return self +end + +--- [User] Show menu entries to smoke or flare targets (on by default!) +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetEnableSmokeFlareTask() + self:T(self.lid.."SetEnableSmokeFlareTask") + self.noflaresmokemenu = false + return self +end + --- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns. -- @param #PLAYERTASKCONTROLLER self -- @param #boolean ShortCallsign If true, only call out the major flight number @@ -2716,8 +2744,8 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) local active = MENU_GROUP_DELAYED:New(group,menuactive,topmenu) local info = MENU_GROUP_COMMAND_DELAYED:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client) local mark = MENU_GROUP_COMMAND_DELAYED:New(group,menumark,active,self._MarkTask,self,group,client) - if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then - -- no smoking/flaring here if A2A + if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A or self.noflaresmokemenu then + -- no smoking/flaring here if A2A or designer has set to false local smoke = MENU_GROUP_COMMAND_DELAYED:New(group,menusmoke,active,self._SmokeTask,self,group,client) local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client) end From 26025bb2a11072b3a5f8b6e90fc185a077c954c9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 29 Sep 2022 16:42:08 +0200 Subject: [PATCH 038/603] #SPOT - Set relative position to start lasing #ZONE_POLYGON - Added `ZONE_POLYGON:NewFromPointsArray( ZoneName, PointsArray )` --- Moose Development/Moose/Core/Spot.lua | 18 ++++++++++++++++-- Moose Development/Moose/Core/Zone.lua | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index b8dfa0e75..2384df073 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -249,8 +249,10 @@ do local RecceDcsUnit = self.Recce:GetDCSObject() - self.SpotIR = Spot.createInfraRed( RecceDcsUnit, { x = 0, y = 2, z = 0 }, Target:GetPointVec3():AddY(1):GetVec3() ) - self.SpotLaser = Spot.createLaser( RecceDcsUnit, { x = 0, y = 2, z = 0 }, Target:GetPointVec3():AddY(1):GetVec3(), LaserCode ) + local relativespot = self.relstartpos or { x = 0, y = 2, z = 0 } + + self.SpotIR = Spot.createInfraRed( RecceDcsUnit, relativespot, Target:GetPointVec3():AddY(1):GetVec3() ) + self.SpotLaser = Spot.createLaser( RecceDcsUnit, relativespot, Target:GetPointVec3():AddY(1):GetVec3(), LaserCode ) if Duration then self.ScheduleID = self.LaseScheduler:Schedule( self, StopLase, {self}, Duration ) @@ -368,4 +370,16 @@ do return self.Lasing end + --- Set laser start position relative to the lasing unit. + -- @param #SPOT self + -- @param #table position Start position of the laser relative to the lasing unit. Default is { x = 0, y = 2, z = 0 } + -- @return #SPOT self + -- @usage + -- -- Set lasing position to be the position of the optics of the Gazelle M: + -- myspot:SetRelativeStartPosition({ x = 1.7, y = 1.2, z = 0 }) + function SPOT:SetRelativeStartPosition(position) + self.relstartpos = position or { x = 0, y = 2, z = 0 } + return self + end + end \ No newline at end of file diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 0919d6e7d..296dbb3ba 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2381,6 +2381,21 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup ) return self end +--- Constructor to create a ZONE_POLYGON instance, taking the zone name and an array of DCS#Vec2, forming a polygon. +-- @param #ZONE_POLYGON self +-- @param #string ZoneName Name of the zone. +-- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCS#Vec2}, forming a polygon. +-- @return #ZONE_POLYGON self +function ZONE_POLYGON:NewFromPointsArray( ZoneName, PointsArray ) + + local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) ) + self:F( { ZoneName, self._.Polygon } ) + + -- Zone objects are added to the _DATABASE and SET_ZONE objects. + _EVENTDISPATCHER:CreateEventNewZone( self ) + + return self +end --- Constructor to create a ZONE_POLYGON instance, taking the zone name and the **name** of the @{Wrapper.Group#GROUP} defined within the Mission Editor. -- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON. From c8600cd24bb5d81772ccd1694388414885f49a1c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 30 Sep 2022 14:42:17 +0200 Subject: [PATCH 039/603] *SET * Added IsInSet() --- Moose Development/Moose/Core/Set.lua | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 1f43c2659..4e279d56f 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -813,14 +813,31 @@ do -- SET_BASE return true end - --- Decides whether to include the Object. + --- Decides whether an object is in the SET -- @param #SET_BASE self -- @param #table Object -- @return #SET_BASE self - function SET_BASE:IsInSet( ObjectName ) + function SET_BASE:IsInSet( Object ) self:F3( Object ) - - return true + local outcome = false + local name = Object:GetName() + self:ForEach( + function(object) + if object:GetName() == name then + outcome = true + end + end + ) + return outcome + end + + --- Decides whether an object is **not** in the SET + -- @param #SET_BASE self + -- @param #table Object + -- @return #SET_BASE self + function SET_BASE:IsNotInSet( Object ) + self:F3( Object ) + return not self:IsInSet(Object) end --- Gets a string with all the object names. From c7baaa26f21ea94c014543244d056239482fa459 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 30 Sep 2022 18:47:28 +0200 Subject: [PATCH 040/603] #PLAYERRECCE * Initial Release --- Moose Development/Moose/Modules.lua | 1 + Moose Development/Moose/Ops/PlayerRecce.lua | 1041 +++++++++++++++++++ Moose Setup/Moose.files | 1 + 3 files changed, 1043 insertions(+) create mode 100644 Moose Development/Moose/Ops/PlayerRecce.lua diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index a7588315b..d9decff69 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -104,6 +104,7 @@ __Moose.Include( 'Scripts/Moose/Ops/OpsTransport.lua' ) __Moose.Include( 'Scripts/Moose/Ops/OpsZone.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Platoon.lua' ) __Moose.Include( 'Scripts/Moose/Ops/PlayerTask.lua' ) +__Moose.Include( 'Scripts/Moose/Ops/PlayerRecce.lua' ) __Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' ) __Moose.Include( 'Scripts/Moose/Ops/RescueHelo.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Squadron.lua' ) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua new file mode 100644 index 000000000..9d4e771f0 --- /dev/null +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -0,0 +1,1041 @@ +--- **Ops** - Defines an extensive API to manage 3D points in the DCS World 3D simulation space. +-- +-- ## Features: +-- +-- * Allow a player in the Gazelle to detect, smoke, flare, lase and report ground units to others. +-- * Implements visual detection from the helo +-- * Implements optical detection via the Vivianne system and lasing +-- +-- === +-- +-- # Demo Missions +-- +-- ### Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/). +-- +-- === +-- +-- +-- ### Authors: +-- +-- * Applevengelist (Design & Programming) +-- +-- === +-- +-- @module Ops.PlayerRecce +-- @image @image Detection.JPG + +------------------------------------------------------------------------------------------------------------------- +-- PLAYERRECCE +-- TODO: PLAYERRECCE +-- TODO: A lot... +------------------------------------------------------------------------------------------------------------------- + +--- PLAYERRECCE class. +-- @type PLAYERRECCE +-- @field #string ClassName Name of the class. +-- @field #boolean verbose Switch verbosity. +-- @field #string lid Class id string for output to DCS log file. +-- @field #string version +-- @field #table ViewZone +-- @field #table ViewZoneVisual +-- @field Core.Set#SET_CLIENT PlayerSet +-- @field #string Name +-- @field #number Coalition +-- @field #string CoalitionName +-- @field #boolean debug +-- @field #table LaserSpots +-- @field #table UnitLaserCodes +-- @field #table LaserCodes +-- @field #table ClientMenus +-- @field #table OnStation +-- @field #number minthreatlevel +-- @field #number lasingtime +-- @field #table AutoLase +-- @field Core.Set#SET_CLIENT AttackSet +-- @extends Core.Fsm#FSM + +--- +-- +-- *It is our attitude at the beginning of a difficult task which, more than anything else, which will affect its successful outcome.* (William James) +-- +-- === +-- +-- # PLAYERRECCE +-- +-- * Simplifies defining, executing and controlling of Player tasks +-- * TBD +-- +-- If you have questions or suggestions, please visit the [MOOSE Discord](https://discord.gg/AeYAkHP) channel. +-- +-- +-- @field #PLAYERRECCE +PLAYERRECCE = { + ClassName = "PLAYERRECCE", + verbose = true, + lid = nil, + version = "0.0.1", + ViewZone = {}, + ViewZoneVisual = {}, + PlayerSet = nil, + debug = true, + LaserSpots = {}, + UnitLaserCodes = {}, + LaserCodes = {}, + ClientMenus = {}, + OnStation = {}, + minthreatlevel = 0, + lasingtime = 60, + AutoLase = {}, + AttackSet = nil, +} + +--- +-- @type LaserRelativePos +-- @field #string typename Unit type name +PLAYERRECCE.LaserRelativePos = { + ["SA342M"] = { x = 1.7, y = 1.2, z = 0 }, + ["SA342Mistral"] = { x = 1.7, y = 1.2, z = 0 }, + ["SA342Minigun"] = { x = 1.7, y = 1.2, z = 0 }, + ["SA342L"] = { x = 1.7, y = 1.2, z = 0 }, +} + +--- +-- @type MaxViewDistance +-- @field #string typename Unit type name +PLAYERRECCE.MaxViewDistance = { + ["SA342M"] = 5000, + ["SA342Mistral"] = 5000, + ["SA342Minigun"] = 5000, + ["SA342L"] = 5000, +} + +--- +-- @type Cameraheight +-- @field #string typename Unit type name +PLAYERRECCE.Cameraheight = { + ["SA342M"] = 2.85, + ["SA342Mistral"] = 2.85, + ["SA342Minigun"] = 2.85, + ["SA342L"] = 2.85, +} + +--- +-- @type CanLase +-- @field #string typename Unit type name +PLAYERRECCE.CanLase = { + ["SA342M"] = true, + ["SA342Mistral"] = true, + ["SA342Minigun"] = false, + ["SA342L"] = true, +} + +--- +-- @param #PLAYERRECCE self +-- @return #PLAYERRECCE self +function PLAYERRECCE:New(Name, Coalition, PlayerSet) + + -- Inherit everything from FSM class. + local self=BASE:Inherit(self, FSM:New()) -- #PLAYERRECCE + + self.Name = Name or "Blue FACA" + self.Coalition = Coalition or coalition.side.BLUE + self.CoalitionName = UTILS.GetCoalitionName(Coalition) + self.PlayerSet = PlayerSet + + self.lid=string.format("PlayerForwardController %s %s | ", self.Name, self.version) + + self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes + self.lasingtime = 60 + + self.minthreatlevel = 0 + + -- FSM start state is STOPPED. + self:SetStartState("Stopped") + + self:AddTransition("Stopped", "Start", "Running") + self:AddTransition("*", "Status", "*") + self:AddTransition("*", "RecceOnStation", "*") + self:AddTransition("*", "RecceOffStation", "*") + self:AddTransition("*", "TargetDetected", "*") + self:AddTransition("*", "TargetsSmoked", "*") + self:AddTransition("*", "TargetsFlared", "*") + self:AddTransition("*", "TargetLasing", "*") + self:AddTransition("*", "TargetLOSLost", "*") + self:AddTransition("*", "TargetReport", "*") + self:AddTransition("*", "TargetReportSent", "*") + self:AddTransition("Running", "Stop", "Stopped") + + -- Player Events + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) + self:HandleEvent(EVENTS.Ejection, self._EventHandler) + self:HandleEvent(EVENTS.Crash, self._EventHandler) + self:HandleEvent(EVENTS.PilotDead, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) + + self:__Start(-1) + local starttime = math.random(5,10) + self:__Status(-starttime) + + return self +end + +--- [Internal] Event handling +-- @param #PLAYERRECCE self +-- @param Core.Event#EVENTDATA EventData +-- @return #PLAYERRECCE self +function PLAYERRECCE:_EventHandler(EventData) + self:T(self.lid.."_EventHandler: "..EventData.id) + if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then + if EventData.IniPlayerName then + self:I(self.lid.."Event for player: "..EventData.IniPlayerName) + if self.ClientMenus[EventData.IniPlayerName] then + self.ClientMenus[EventData.IniPlayerName]:Remove() + end + self.ClientMenus[EventData.IniPlayerName] = nil + self.LaserSpots[EventData.IniPlayerName] = nil + self.OnStation[EventData.IniPlayerName] = false + end + elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then + if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then + self:I(self.lid.."Event for player: "..EventData.IniPlayerName) + self.UnitLaserCodes[EventData.IniPlayerName] = 1688 + self.ClientMenus[EventData.IniPlayerName] = nil + self.LaserSpots[EventData.IniPlayerName] = nil + self.OnStation[EventData.IniPlayerName] = false + self:_BuildMenus() + end + end + return self +end + +--- [User] Set a table of possible laser codes. +-- Each new RECCE can select a code from this table, default is 1688. +-- @param #PLAYERRECCE self +-- @param #list<#number> LaserCodes +-- @return #PLAYERRECCE +function PLAYERRECCE:SetLaserCodes( LaserCodes ) + self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes } + return self +end + +--- [User] Set a set of clients which will receive target reports +-- @param #PLAYERRECCE self +-- @param Core.Set#SET_CLIENT AttackSet +-- @return #PLAYERRECCE +function PLAYERRECCE:SetAttackSet(AttackSet) + self.AttackSet = AttackSet + return self +end + +--- [Internal] Get the view parameters from a Gazelle camera +-- @param #PLAYERRECCE self +-- @param Wrapper.Unit#UNIT Gazelle +-- @return #number cameraheading in degrees. +-- @return #number cameranodding in degrees. +-- @return #number maxview in meters. +-- @return #boolean cameraison If true, camera is on, else off. +function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle) + self:I(self.lid.."GetGazelleVivianneSight") + local unit = Gazelle -- Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + local dcsunit = Unit.getByName(Gazelle:GetName()) + local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg + local vivivertical = dcsunit:getDrawArgumentValue(216) or 0 -- L/Mistral/Minigun model has no 216, ca 10deg up (=1) and down (=-1) + local vivioff = false + -- -1 = -180, 1 = 180 + -- Actual view -0,66 to 0,66 + -- Nick view -0,98 to 0,98 for +/- 30° + if vivihorizontal < -0.7 then + vivihorizontal = -0.7 + vivioff = true + return 0,0,0,false + elseif vivihorizontal > 0.7 then + vivihorizontal = 0.7 + vivioff = true + return 0,0,0,false + end + local horizontalview = vivihorizontal * -180 + local verticalview = vivivertical * -30 -- ca +/- 30° + local heading = unit:GetHeading() + local viviheading = (heading+horizontalview)%360 + local maxview = self:_GetActualMaxLOSight(unit,viviheading, verticalview,vivioff) + return viviheading, verticalview, maxview, not vivioff + end + return 0,0,0,false +end + +--- [Internal] Get the max line of sight based on unit head and camera nod via trigonometrie. Returns 0 if camera is off. +-- @param #PLAYERRECCE self +-- @param Wrapper.Unit#UNIT unit The unit which LOS we want +-- @param #number vheading Heading where the unit or camera is looking +-- @param #number vnod Nod down in degrees +-- @param #boolean vivoff Camera on or off +-- @return #number maxview Max view distance in meters +function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff) + self:I(self.lid.."_GetActualMaxLOSight") + if vivoff then return 0 end + local maxview = 0 + if unit and unit:IsAlive() then + local typename = unit:GetTypeName() + maxview = self.MaxViewDistance[typename] or 5000 + local CamHeight = self.Cameraheight[typename] or 0 + if vnod > 0 then + -- Looking down + -- determine max distance we're looking at + local beta = 90 + local gamma = math.floor(90-vnod) + local alpha = math.floor(180-beta-gamma) + local a = unit:GetHeight()-unit:GetCoordinate():GetLandHeight()+CamHeight + local b = a / math.sin(math.rad(alpha)) + local c = b * math.sin(math.rad(gamma)) + maxview = c*1.2 -- +20% + end + end + return maxview +end + +--- [Internal] Build a ZONE_POLYGON from a given viewport of a unit +-- @param #PLAYERRECCE self +-- @param Wrapper.Unit#UNIT unit The unit which is looking +-- @param #number vheading Heading where the unit or camera is looking +-- @param #number vnod Nod down in degrees +-- @param #number maxview Max line of sight, depending on height +-- @param #number angle Angle left/right to be added to heading to form a triangle +-- @param #boolean camon Camera is switched on +-- @param #boolean draw Draw the zone on the F10 map +-- @return Core.Zone#ZONE_POLYGON ViewZone or nil if camera is off +function PLAYERRECCE:_GetViewZone(unit, vheading, vnod, maxview, angle, camon, draw) + self:I(self.lid.."_GetViewZone") + local viewzone = nil + if not camon then return nil end + if unit and unit:IsAlive() then + local unitname = unit:GetName() + if self.ViewZone[unitname] then + self.ViewZone[unitname]:UndrawZone() + end + --local vheading, vnod, maxview, vivon = self:GetGazelleVivianneSight(unit) + local startpos = unit:GetCoordinate() + local heading1 = (vheading+angle)%360 + local heading2 = (vheading-angle)%360 + local pos1 = startpos:Translate(maxview,heading1) + local pos2 = startpos:Translate(maxview,heading2) + local array = {} + table.insert(array,startpos:GetVec2()) + table.insert(array,pos1:GetVec2()) + table.insert(array,pos2:GetVec2()) + viewzone = ZONE_POLYGON:NewFromPointsArray(unitname,array) + if draw then + viewzone:DrawZone(-1,{0,0,1},nil,nil,nil,1) + self.ViewZone[unitname] = viewzone + end + end + return viewzone +end + +--- [Internal] +--@param #PLAYERRECCE self +--@param Wrapper.Unit#UNIT unit The FACA unit +--@param #boolean camera If true, use the unit's camera for targets in sight +--@return Core.Set#SET_UNIT Set of targets, can be empty! +--@return #number count Count of targets +function PLAYERRECCE:_GetTargetSet(unit,camera) + self:I(self.lid.."_GetTargetSet") + local finaltargets = SET_UNIT:New() + local finalcount = 0 + local heading,nod,maxview,angle = 0,30,5000,10 + local camon = true + local typename = unit:GetTypeName() + local name = unit:GetName() + if string.find(typename,"SA342") and camera then + heading,nod,maxview,camon = self:_GetGazelleVivianneSight(unit) + angle=10 + else + -- visual + heading = unit:GetHeading() + nod,maxview,camon = 10,1000,true + angle = 45 + end + local zone = self:_GetViewZone(unit,heading,nod,maxview,angle,camon) + if zone then + local redcoalition = "red" + if self.Coalition == coalition.side.RED then + redcoalition = "blue" + end + -- determine what we can see + local startpos = unit:GetCoordinate() + local targetset = SET_UNIT:New():FilterCategories("ground"):FilterActive(true):FilterZones({zone}):FilterCoalitions(redcoalition):FilterOnce() + self:I("Prefilter Target Count = "..targetset:CountAlive()) + -- TODO - Threat level filter? + -- TODO - Min distance from unit? + targetset:ForEach( + function(_unit) + local _unit = _unit -- Wrapper.Unit#UNIT + local _unitpos = _unit:GetCoordinate() + if startpos:IsLOS(_unitpos) then + self:I("Adding to final targets: ".._unit:GetName()) + finaltargets:Add(_unit:GetName(),_unit) + end + end + ) + finalcount = finaltargets:CountAlive() + self:I(string.format("%s Unit: %s | Targets in view %s",self.lid,name,finalcount)) + end + return finaltargets, finalcount, zone +end + +---[Internal] +--@param #PLAYERRECCE self +--@param Core.Set#SET_UNIT targetset Set of targets, can be empty! +--@return Wrapper.Unit#UNIT Target +function PLAYERRECCE:_GetHVTTarget(targetset) + self:I(self.lid.."_GetHVTTarget") + + -- get one target + -- local target = targetset:GetRandom() -- Wrapper.Unit#UNIT + + -- sort units + local unitsbythreat = {} + local minthreat = self.minthreatlevel or 0 + for _,_unit in pairs(targetset.Set) do + local unit = _unit -- Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + local threat = unit:GetThreatLevel() + if threat >= minthreat then + -- prefer radar units + if unit:HasAttribute("RADAR_BAND1_FOR_ARM") or unit:HasAttribute("RADAR_BAND2_FOR_ARM") or unit:HasAttribute("Optical Tracker") then + threat = 11 + end + table.insert(unitsbythreat,{unit,threat}) + end + end + end + + table.sort(unitsbythreat, function(a,b) + local aNum = a[2] -- Coin value of a + local bNum = b[2] -- Coin value of b + return aNum > bNum -- Return their comparisons, < for ascending, > for descending + end) + + return unitsbythreat[1][1] +end + +--- [Internal] +--@param #PLAYERRECCE self +--@param Wrapper.Client#CLIENT client The FACA unit +--@param Core.Set#SET_UNIT targetset Set of targets, can be empty! +--@return #PLAYERRECCE self +function PLAYERRECCE:_LaseTarget(client,targetset) + self:I(self.lid.."_LaseTarget") + -- get one target + local target = self:_GetHVTTarget(targetset) -- Wrapper.Unit#UNIT + local playername = client:GetPlayerName() + local laser = nil -- Core.Spot#SPOT + -- set laser + if not self.LaserSpots[playername] then + laser = SPOT:New(client) + if not self.UnitLaserCodes[playername] then + self.UnitLaserCodes[playername] = 1688 + end + laser.LaserCode = self.UnitLaserCodes[playername] or 1688 + --function laser:OnAfterLaseOff(From,Event,To) + --MESSAGE:New("Finished lasing",15,"Info"):ToClient(client) + --end + self.LaserSpots[playername] = laser + else + laser = self.LaserSpots[playername] + end + if not laser:IsLasing() and target then + local relativecam = self.LaserRelativePos[client:GetTypeName()] + laser:SetRelativeStartPosition(relativecam) + local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688 + local lasingtime = self.lasingtime or 60 + local targettype = target:GetTypeName() + laser:LaseOn(target,lasercode,lasingtime) + --MESSAGE:New(string.format("Lasing Target %s with Code %d",targettype,lasercode),15,"Info"):ToClient(client) + self:__TargetLasing(-1,client,target,lasercode,lasingtime) + else + -- still looking at target? + local oldtarget=laser.Target + if targetset:IsNotInSet(oldtarget) then + -- lost LOS + local targettype = oldtarget:GetTypeName() + laser:LaseOff() + self:__TargetLOSLost(-1,client,oldtarget) + --MESSAGE:New(string.format("Lost LOS on target %s!",targettype),15,"Info"):ToClient(client) + end + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_SetClientLaserCode(client,group,playername,code) + self:I(self.lid.."_SetClientLaserCode") + self.UnitLaserCodes[playername] = code or 1688 + if self.ClientMenus[playername] then + self.ClientMenus[playername]:Remove() + self.ClientMenus[playername]=nil + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_SwitchOnStation(client,group,playername) + self:I(self.lid.."_SwitchOnStation") + if not self.OnStation[playername] then + self.OnStation[playername] = true + self:__RecceOnStation(-1,client,playername) + else + self.OnStation[playername] = false + self:__RecceOffStation(-1,client,playername) + end + if self.ClientMenus[playername] then + self.ClientMenus[playername]:Remove() + self.ClientMenus[playername]=nil + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_SwitchLasing(client,group,playername) + self:I(self.lid.."_SwitchLasing") + if not self.AutoLase[playername] then + self.AutoLase[playername] = true + MESSAGE:New("Lasing is now ON",10,self.Name or "FACA"):ToClient(client) + else + self.AutoLase[playername] = false + MESSAGE:New("Lasing is now OFF",10,self.Name or "FACA"):ToClient(client) + end + if self.ClientMenus[playername] then + self.ClientMenus[playername]:Remove() + self.ClientMenus[playername]=nil + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_WIP(client,group,playername) + self:I(self.lid.."_WIP") + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_SmokeTargets(client,group,playername) + self:I(self.lid.."_SmokeTargets") + local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT + local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT + cameraset:AddSet(visualset) + self:__TargetsSmoked(-1,client,playername,cameraset) + local highsmoke = SMOKECOLOR.Orange + local medsmoke = SMOKECOLOR.White + local lowsmoke = SMOKECOLOR.Green + local lasersmoke = SMOKECOLOR.Red + local laser = self.LaserSpots[playername] -- Core.Spot#SPOT + -- laser targer gets extra smoke + if laser and laser.Target and laser.Target:IsAlive() then + laser.Target:GetCoordinate():Smoke(lasersmoke) + if cameraset:IsInSet(laser.Target) then + cameraset:Remove(laser.Target:GetName(),true) + end + end + -- smoke everything else + for _,_unit in pairs(cameraset.Set) do + local unit = _unit --Wrapper.Unit#UNIT + if unit then + local coord = unit:GetCoordinate() + local threat = unit:GetThreatLevel() + if coord then + local color = lowsmoke + if threat > 7 then + color = medsmoke + elseif threat > 2 then + color = lowsmoke + end + coord:Smoke(color) + end + end + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_FlareTargets(client,group,playername) + self:I(self.lid.."_SmokeTargets") + local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT + local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT + cameraset:AddSet(visualset) + self:__TargetsFlared(-1,client,playername,cameraset) + local highsmoke = FLARECOLOR.Yellow + local medsmoke = FLARECOLOR.White + local lowsmoke = FLARECOLOR.Green + local lasersmoke = FLARECOLOR.Red + local laser = self.LaserSpots[playername] -- Core.Spot#SPOT + -- laser targer gets extra smoke + if laser and laser.Target and laser.Target:IsAlive() then + laser.Target:GetCoordinate():Flare(lasersmoke) + if cameraset:IsInSet(laser.Target) then + cameraset:Remove(laser.Target:GetName(),true) + end + end + -- smoke everything else + for _,_unit in pairs(cameraset.Set) do + local unit = _unit --Wrapper.Unit#UNIT + if unit then + local coord = unit:GetCoordinate() + local threat = unit:GetThreatLevel() + if coord then + local color = lowsmoke + if threat > 7 then + color = medsmoke + elseif threat > 2 then + color = lowsmoke + end + coord:Flare(color) + end + end + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_ReportLaserTargets(client,group,playername) +self:I(self.lid.."_ReportLaserTargets") + local targetset, number = self:_GetTargetSet(client,true) + if number > 0 and self.AutoLase[playername] then + local Settings = ( client and _DATABASE:GetPlayerSettings( playername ) ) or _SETTINGS + local target = self:_GetHVTTarget(targetset) -- the one we're lasing + local ThreatLevel = target:GetThreatLevel() + local ThreatLevelText = "high" + if ThreatLevel > 3 and ThreatLevel < 8 then + ThreatLevelText = "medium" + elseif ThreatLevel <= 3 then + ThreatLevelText = "low" + end + local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel + local report = REPORT:New("Lasing Report") + report:Add(string.rep("-",15)) + report:Add("Target type: "..target:GetTypeName()) + report:Add("Threat Level: "..ThreatGraph.." ("..ThreatLevelText..")") + report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings)) + --report:Add("Location: "..target:GetCoordinate():ToStringA2G(client,Settings)) + report:Add("Laser Code: "..self.UnitLaserCodes[playername] or 1688) + report:Add(string.rep("-",15)) + local text = report:Text() + self:__TargetReport(-1,client,targetset,target,text) + else + local report = REPORT:New("Lasing Report") + report:Add(string.rep("-",15)) + report:Add("N O T A R G E T S") + report:Add(string.rep("-",15)) + local text = report:Text() + self:__TargetReport(-1,client,nil,nil,text) + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_ReportVisualTargets(client,group,playername) + self:I(self.lid.."_ReportVisualTargets") + local targetset, number = self:_GetTargetSet(client,false) + if number > 0 then + local Settings = ( client and _DATABASE:GetPlayerSettings( playername ) ) or _SETTINGS + local ThreatLevel = targetset:CalculateThreatLevelA2G() + local ThreatLevelText = "high" + if ThreatLevel > 3 and ThreatLevel < 8 then + ThreatLevelText = "medium" + elseif ThreatLevel <= 3 then + ThreatLevelText = "low" + end + local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel + local report = REPORT:New("Target Report") + report:Add(string.rep("-",15)) + report:Add("Target count: "..number) + report:Add("Threat Level: "..ThreatGraph.." ("..ThreatLevelText..")") + report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings)) + --report:Add("Location: "..client:GetCoordinate():ToStringA2G(client,Settings)) + report:Add(string.rep("-",15)) + local text = report:Text() + self:__TargetReport(-1,client,targetset,nil,text) + else + local report = REPORT:New("Target Report") + report:Add(string.rep("-",15)) + report:Add("N O T A R G E T S") + report:Add(string.rep("-",15)) + local text = report:Text() + self:__TargetReport(-1,client,nil,nil,text) + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #PLAYERRECCE self +function PLAYERRECCE:_BuildMenus() + self:I(self.lid.."_BuildMenus") + local clients = self.PlayerSet -- Core.Set#SET_CLIENT + local clientset = clients:GetSetObjects() + for _,_client in pairs(clientset) do + local client = _client -- Wrapper.Client#CLIENT + if client and client:IsAlive() then + local playername = client:GetPlayerName() + if not self.UnitLaserCodes[playername] then + self:_SetClientLaserCode(nil,nil,playername,1688) + end + local group = client:GetGroup() + if not self.ClientMenus[playername] then + local canlase = self.CanLase[client:GetTypeName()] + self.ClientMenus[playername] = MENU_GROUP:New(group,self.Name or "RECCE") + local txtonstation = self.OnStation[playername] and "ON" or "OFF" + local text = string.format("Switch On-Station (%s)",txtonstation) + local onstationmenu = MENU_GROUP_COMMAND:New(group,text,self.ClientMenus[playername],self._SwitchOnStation,self,client,group,playername) + if self.OnStation[playername] then + local smokemenu = MENU_GROUP_COMMAND:New(group,"Smoke Targets",self.ClientMenus[playername],self._SmokeTargets,self,client,group,playername) + local smokemenu = MENU_GROUP_COMMAND:New(group,"Flare Targets",self.ClientMenus[playername],self._FlareTargets,self,client,group,playername) + if canlase then + local txtonstation = self.AutoLase[playername] and "ON" or "OFF" + local text = string.format("Switch Lasing (%s)",txtonstation) + local lasemenu = MENU_GROUP_COMMAND:New(group,text,self.ClientMenus[playername],self._SwitchLasing,self,client,group,playername) + end + local targetmenu = MENU_GROUP:New(group,"Target Report",self.ClientMenus[playername]) + if canlase then + local reportL = MENU_GROUP_COMMAND:New(group,"Laser Target",targetmenu,self._ReportLaserTargets,self,client,group,playername) + end + local reportV = MENU_GROUP_COMMAND:New(group,"Visual Targets",targetmenu,self._ReportVisualTargets,self,client,group,playername) + if canlase then + local lasecodemenu = MENU_GROUP:New(group,"Set Laser Code",self.ClientMenus[playername]) + local codemenu = {} + for _,_code in pairs(self.LaserCodes) do + --self._SetClientLaserCode,self,client,group,playername) + if _code == self.UnitLaserCodes[playername] then + _code = tostring(_code).."(*)" + end + codemenu[playername.._code] = MENU_GROUP_COMMAND:New(group,tostring(_code),lasecodemenu,self._SetClientLaserCode,self,client,group,playername,_code) + end + end + end + end + end + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Core.Set#SET_UNIT targetset +-- @param Wrapper.Client#CLIENT client +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_CheckNewTargets(targetset,client,playername) + self:I(self.lid.."_CheckNewTargets") + targetset:ForEachUnit( + function(unit) + if unit and unit:IsAlive() then + if not unit.PlayerRecceDetected then + unit.PlayerRecceDetected = { + detected = true, + recce = client, + playername = playername, + timestamp = timer.getTime() + } + self:__TargetDetected(-1,unit,client,playername) + end + end + end + ) + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterStatus(From, Event, To) + self:I({From, Event, To}) + + self:_BuildMenus() + + self.PlayerSet:ForEachClient( + function(Client) + local client = Client -- Wrapper.Client#CLIENT + local playername = client:GetPlayerName() + if client and client:IsAlive() and self.OnStation[playername] then + -- targets on camera + local targetset, targetcount, tzone = self:_GetTargetSet(client,true) + if targetset then + if self.ViewZone[playername] then + self.ViewZone[playername]:UndrawZone() + end + if self.debug and tzone then + self.ViewZone[playername]=tzone:DrawZone(self.Coalition,{0,0,1},nil,nil,nil,1) + end + end + self:I({targetcount=targetcount}) + -- visual targets + local vistargetset, vistargetcount, viszone = self:_GetTargetSet(client,false) + if vistargetset then + if self.ViewZoneVisual[playername] then + self.ViewZoneVisual[playername]:UndrawZone() + end + if self.debug and viszone then + self.ViewZoneVisual[playername]=viszone:DrawZone(self.Coalition,{1,0,0},nil,nil,nil,3) + end + end + self:I({targetcount=targetcount}) + -- lase targets on camera + if targetcount > 0 then + if self.CanLase[client:GetTypeName()] and self.AutoLase[playername] then + -- DONE move to lase at will + self:_LaseTarget(client,targetset) + end + end + -- Report new targets + targetset:AddSet(vistargetset) + self:_CheckNewTargets(targetset,client,playername) + end + end + ) + + self:__Status(-10) + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param #string Playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername) + self:I({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(true,true) + local coord = Client:GetCoordinate() + local coordtext = coord:ToStringBULLS(self.Coalition) + local text = string.format("All stations, FACA %s on station\nat %s!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param #string Playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername) + self:I({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(true,true) + local coord = Client:GetCoordinate() + local coordtext = coord:ToStringBULLS(self.Coalition) + local text = string.format("All stations, FACA %s leaving station\nat %s, going home!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Unit#UNIT Target +-- @param Wrapper.Client#CLIENT Client +-- @param #string Playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterTargetDetected(From, Event, To, Target, Client, Playername) + self:I({From, Event, To}) + if self.debug then + local text = string.format("New target %s detected by %s!",Target:GetTypeName(),Playername) + MESSAGE:New(text,10,self.Name or "FACA"):ToCoalition(self.Coalition) + end + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param #string Playername +-- @param Core.Set#SET_UNIT TargetSet +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterTargetsSmoked(From, Event, To, Client, Playername, TargetSet) + self:I({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(true,true) + local coord = Client:GetCoordinate() + local coordtext = coord:ToStringBULLS(self.Coalition) + local text = string.format("All stations, FACA %s smoked targets\nat %s!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param #string Playername +-- @param Core.Set#SET_UNIT TargetSet +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterTargetsFlared(From, Event, To, Client, Playername, TargetSet) + self:I({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(true,true) + local coord = Client:GetCoordinate() + local coordtext = coord:ToStringBULLS(self.Coalition) + local text = string.format("All stations, FACA %s flared\ntargets at %s!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param Wrapper.Unit#UNIT Target +-- @param #number Lasercode +-- @param #number Lasingtime +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Lasercode, Lasingtime) + self:I({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(true,true) + local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS + local coord = Client:GetCoordinate() + local coordtext = coord:ToStringBULLS(self.Coalition,Settings) + local targettype = Target:GetTypeName() + local text = string.format("All stations, FACA %s lasing %s\nat %s!\nCode %d, Duration %d seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param Wrapper.Unit#UNIT Target +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterTargetLOSLost(From, Event, To, Client, Target) + self:I({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(true,true) + local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS + local coord = Client:GetCoordinate() + local coordtext = coord:ToStringBULLS(self.Coalition,Settings) + local targettype = Target:GetTypeName() + local text = string.format("All stations, FACA %s lost sight of %s\nat %s!",callsign, targettype, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param Core.Set#SET_UNIT TargetSet +-- @param Wrapper.Unit#UNIT Target +-- @param #string Text +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterTargetReport(From, Event, To, Client, TargetSet, Target, Text) + self:I({From, Event, To}) + MESSAGE:New(Text,45,self.Name or "FACA"):ToClient(Client) + if self.AttackSet then + -- send message to AttackSet + for _,_client in pairs(self.AttackSet.Set) do + local client = _client -- Wrapper.Client#CLIENT + if client and client:IsAlive() then + MESSAGE:New(Text,45,self.Name or "FACA"):ToClient(client) + end + end + end + self:__TargetReportSent(-2,Client, TargetSet, Target, Text) + return self +end + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param Core.Set#SET_UNIT TargetSet +-- @param Wrapper.Unit#UNIT Target +-- @param #string Text +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterTargetReportSent(From, Event, To, Client, TargetSet, Target, Text) + self:I({From, Event, To}) + return self +end + + +--- [Internal] +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterStop(From, Event, To) + self:I({From, Event, To}) + -- Player Events + self:UnHandleEvent(EVENTS.PlayerLeaveUnit) + self:UnHandleEvent(EVENTS.Ejection) + self:UnHandleEvent(EVENTS.Crash) + self:UnHandleEvent(EVENTS.PilotDead) + self:UnHandleEvent(EVENTS.PlayerEnterAircraft) + return self +end + +--[[ test script +local PlayerSet = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterCategories("helicopter"):FilterStart() +local Attackers = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterStart() +local myrecce = PLAYERRECCE:New("1st Forward FACA",coalition.side.BLUE,PlayerSet) +myrecce:SetAttackSet(Attackers) +--]] \ No newline at end of file diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 65c5fce6c..5af2a8577 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -103,6 +103,7 @@ Ops/Awacs.lua Ops/Operation.lua Ops/FlightControl.lua Ops/PlayerTask.lua +Ops/PlayerRecce.lua AI/AI_Balancer.lua AI/AI_Air.lua From 3322609f87be313bbab5470e009bdceb73b0b842 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 30 Sep 2022 19:02:41 +0200 Subject: [PATCH 041/603] #AWACS * Make markers and drawings strictly coalition specific --- Moose Development/Moose/Ops/Awacs.lua | 64 ++++++++++----------- Moose Development/Moose/Ops/PlayerRecce.lua | 4 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index e1e9bea0b..30a84aa53 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -27,7 +27,7 @@ do -- @field #string ClassName Name of this class. -- @field #string version Versioning. -- @field #string lid LID for log entries. --- @field #number coalition Colition side. +-- @field #number coalition Coalition side. -- @field #string coalitiontxt e.g."blue" -- @field Core.Zone#ZONE OpsZone, -- @field Core.Zone#ZONE StationZone, @@ -497,7 +497,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.43", -- #string + version = "0.2.44", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -1788,10 +1788,10 @@ function AWACS:SetAdditionalZone(Zone, Draw) self:T(self.lid.."SetAdditionalZone") self.BorderZone = Zone if self.debug then - Zone:DrawZone(-1,{1,0.64,0},1,{1,0.64,0},0.2,1,true) - MARKER:New(Zone:GetCoordinate(),"Defensive Zone"):ToAll() + Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true) + MARKER:New(Zone:GetCoordinate(),"Defensive Zone"):ToCoalition(self.coalition) elseif Draw then - Zone:DrawZone(-1,{1,0.64,0},1,{1,0.64,0},0.2,1,true) + Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true) end return self end @@ -1805,11 +1805,11 @@ function AWACS:SetRejectionZone(Zone,Draw) self:T(self.lid.."SetRejectionZone") self.RejectZone = Zone if Draw then - Zone:DrawZone(-1,{1,0.64,0},1,{1,0.64,0},0.2,1,true) + Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true) --MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToAll() elseif self.debug then - Zone:DrawZone(-1,{1,0.64,0},1,{1,0.64,0},0.2,1,true) - MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToAll() + Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true) + MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToCoalition(self.coalition) end return self end @@ -1818,7 +1818,7 @@ end -- @param #AWACS self -- @return #AWACS self function AWACS:DrawFEZ() - self.OpsZone:DrawZone(-1,{1,0,0},1,{1,0,0},0.2,5,true) + self.OpsZone:DrawZone(self.coalition,{1,0,0},1,{1,0,0},0.2,5,true) return self end @@ -3780,7 +3780,7 @@ function AWACS:_MoveAnchorStackFromMarker(Name,Coord) marker:UpdateText(stationtag) station.AnchorMarker = marker if self.debug then - station.StationZone:DrawZone(-1,{0,0,1},1,{0,0,1},0.2,5,true) + station.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true) end self.AnchorStacks:Push(station,Name) end @@ -3811,12 +3811,12 @@ function AWACS:_CreateAnchorStackFromMarker(Name,Coord) --push to AnchorStacks if self.debug then - AnchorStackOne.StationZone:DrawZone(-1,{0,0,1},1,{0,0,1},0.2,5,true) + AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true) local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) - AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToAll() + AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) else local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) - AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToAll() + AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) end self.AnchorStacks:Push(AnchorStackOne,newname) @@ -3857,12 +3857,12 @@ function AWACS:_CreateAnchorStack() --push to AnchorStacks if self.debug then --self.AnchorStacks:Flush() - AnchorStackOne.StationZone:DrawZone(-1,{0,0,1},1,{0,0,1},0.2,5,true) + AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true) local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) - AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToAll() + AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) else local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) - AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToAll() + AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) end self.AnchorStacks:Push(AnchorStackOne,newname) else @@ -3885,12 +3885,12 @@ function AWACS:_CreateAnchorStack() --push to AnchorStacks if self.debug then --self.AnchorStacks:Flush() - AnchorStackOne.StationZone:DrawZone(-1,{0,0,1},1,{0,0,1},0.2,5,true) + AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true) local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) - AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToAll() + AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) else local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) - AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToAll() + AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) end self.AnchorStacks:Push(AnchorStackOne,newname) end @@ -4819,12 +4819,12 @@ function AWACS:AddCAPAirWing(AirWing,Zone) --push to AnchorStacks if self.debug then --self.AnchorStacks:Flush() - AnchorStackOne.StationZone:DrawZone(-1,{0,0,1},1,{0,0,1},0.2,5,true) + AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true) local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) - AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToAll() + AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) else local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) - AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToAll() + AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) end self.AnchorStacks:Push(AnchorStackOne,newname) AirWing.HasOwnStation = true @@ -5601,28 +5601,28 @@ function AWACS:onafterStart(From, Event, To) local controlzonename = "FEZ-"..self.AOName self.ControlZone = ZONE_RADIUS:New(controlzonename,self.OpsZone:GetVec2(),UTILS.NMToMeters(self.ControlZoneRadius)) if self.debug then - self.ControlZone:DrawZone(-1,{0,1,0},1,{1,0,0},0.05,3,true) + self.ControlZone:DrawZone(self.coalition,{0,1,0},1,{1,0,0},0.05,3,true) --MARKER:New(self.ControlZone:GetCoordinate(),"Radar Zone"):ToAll() - self.OpsZone:DrawZone(-1,{1,0,0},1,{1,0,0},0.2,5,true) + self.OpsZone:DrawZone(self.coalition,{1,0,0},1,{1,0,0},0.2,5,true) local AOCoordString = self.AOCoordinate:ToStringLLDDM() local Rocktag = string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString) - MARKER:New(self.AOCoordinate,Rocktag):ToAll() - self.StationZone:DrawZone(-1,{0,0,1},1,{0,0,1},0.2,5,true) + MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition) + self.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true) local stationtag = string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM()) if not self.GCI then - MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToAll() - self.OrbitZone:DrawZone(-1,{0,1,0},1,{0,1,0},0.2,5,true) - MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToAll() + MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) + self.OrbitZone:DrawZone(self.coalition,{0,1,0},1,{0,1,0},0.2,5,true) + MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition) end else local AOCoordString = self.AOCoordinate:ToStringLLDDM() local Rocktag = string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString) - MARKER:New(self.AOCoordinate,Rocktag):ToAll() + MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition) if not self.GCI then - MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToAll() + MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition) end local stationtag = string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM()) - MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToAll() + MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) end if not self.GCI then diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 9d4e771f0..750663b85 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1033,9 +1033,9 @@ function PLAYERRECCE:onafterStop(From, Event, To) return self end ---[[ test script +--[[ local PlayerSet = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterCategories("helicopter"):FilterStart() -local Attackers = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterStart() +local Attackers = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterPrefixes({"CAS","BAI"}):FilterStart() local myrecce = PLAYERRECCE:New("1st Forward FACA",coalition.side.BLUE,PlayerSet) myrecce:SetAttackSet(Attackers) --]] \ No newline at end of file From ab42e506e825599754f13b3ea1eceb2ba870a589 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 30 Sep 2022 19:07:26 +0200 Subject: [PATCH 042/603] #PLAYERRECCE * Some nicefications --- Moose Development/Moose/Ops/PlayerRecce.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 750663b85..a9f32c0ee 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1,4 +1,4 @@ ---- **Ops** - Defines an extensive API to manage 3D points in the DCS World 3D simulation space. +--- **Ops** - Allow a player in the Gazelle to detect, smoke, flare, lase and report ground units to others. -- -- ## Features: -- @@ -62,8 +62,9 @@ -- -- # PLAYERRECCE -- --- * Simplifies defining, executing and controlling of Player tasks --- * TBD +-- * Allow a player in the Gazelle to detect, smoke, flare, lase and report ground units to others. +-- * Implements visual detection from the helo +-- * Implements optical detection via the Vivianne system and lasing -- -- If you have questions or suggestions, please visit the [MOOSE Discord](https://discord.gg/AeYAkHP) channel. -- From fb4f7acb16d8f5b32979e86110a1a1f75b4ea3c1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 1 Oct 2022 11:53:50 +0200 Subject: [PATCH 043/603] SRS additions --- Moose Development/Moose/Modules_local.lua | 1 + Moose Development/Moose/Ops/ATIS.lua | 15 +++++ Moose Development/Moose/Ops/PlayerRecce.lua | 61 ++++++++++++++++++--- Moose Development/Moose/Sound/SRS.lua | 27 ++++++++- 4 files changed, 95 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 949bcce75..848ba726f 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -107,6 +107,7 @@ __Moose.Include( 'Ops\\Awacs.lua' ) __Moose.Include( 'Ops\\PlayerTask.lua' ) __Moose.Include( 'Ops\\Operation.lua' ) __Moose.Include( 'Ops\\FlightControl.lua' ) +__Moose.Include( 'Ops\\PlayerRecce.lua' ) __Moose.Include( 'AI\\AI_Balancer.lua' ) __Moose.Include( 'AI\\AI_Air.lua' ) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 96c67805c..6c98bca81 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -92,6 +92,7 @@ -- @field Sound.SRS#MSRS msrs Moose SRS object. -- @field #number dTQueueCheck Time interval to check the radio queue. Default 5 sec or 90 sec if SRS is used. -- @field #boolean ReportmBar Report mBar/hpa even if not metric, i.e. for Mirage flights +-- @field #boolean TransmitOnlyWithPlayers For SRS - If true, only transmit if there are alive Players. -- @extends Core.Fsm#FSM --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -346,6 +347,7 @@ ATIS = { markerid = nil, relHumidity = nil, ReportmBar = false, + TransmitOnlyWithPlayers = false, } --- NATO alphabet. @@ -780,6 +782,18 @@ function ATIS:SetTowerFrequencies( freqs ) return self end +--- For SRS - Switch to only transmit if there are players on the server. +-- @param #ATIS self +-- @param #boolean Switch If true, only send SRS if there are alive Players. +-- @return #ATIS self +function ATIS:SetTransmitOnlyWithPlayers(Switch) + self.TransmitOnlyWithPlayers = Switch + if self.msrsQ then + self.msrsQ:SetTransmitOnlyWithPlayers(Switch) + end + return self +end + --- Set active runway for **landing** operations. This can be used if the automatic runway determination via the wind direction gives incorrect results. -- For example, use this if there are two runways with the same directions. -- @param #ATIS self @@ -1195,6 +1209,7 @@ function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) self.msrs:SetLabel("ATIS") self.msrs:SetGoogle(GoogleKey) self.msrsQ = MSRSQUEUE:New("ATIS") + self.msrsQ:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers) if self.dTQueueCheck<=10 then self:SetQueueUpdateTime(90) end diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index a9f32c0ee..dcf819fcf 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -724,7 +724,7 @@ function PLAYERRECCE:_BuildMenus() local group = client:GetGroup() if not self.ClientMenus[playername] then local canlase = self.CanLase[client:GetTypeName()] - self.ClientMenus[playername] = MENU_GROUP:New(group,self.Name or "RECCE") + self.ClientMenus[playername] = MENU_GROUP:New(group,self.MenuName or self.Name or "RECCE") local txtonstation = self.OnStation[playername] and "ON" or "OFF" local text = string.format("Switch On-Station (%s)",txtonstation) local onstationmenu = MENU_GROUP_COMMAND:New(group,text,self.ClientMenus[playername],self._SwitchOnStation,self,client,group,playername) @@ -785,6 +785,58 @@ function PLAYERRECCE:_CheckNewTargets(targetset,client,playername) return self end +--- [User] Set SRS TTS details - see @{Sound.SRS} for details +-- @param #PLAYERRECCE self +-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations! +-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies! +-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone" +-- @param #string Gender (Optional) Defaults to "male" +-- @param #string Culture (Optional) Defaults to "en-US" +-- @param #number Port (Optional) Defaults to 5002 +-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. +-- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. +-- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) +-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS +-- @return #PLAYERRECCE self +function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey) + self:I(self.lid.."SetSRS") + self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" -- + self.Gender = Gender or "male" -- + self.Culture = Culture or "en-US" -- + self.Port = Port or 5002 -- + self.Voice = Voice -- + self.PathToGoogleKey = PathToGoogleKey -- + self.Volume = Volume or 1.0 -- + self.UseSRS = true + self.Frequency = Frequency or {127,251} -- + self.BCFrequency = self.Frequency + self.Modulation = Modulation or {radio.modulation.FM,radio.modulation.AM} -- + self.BCModulation = self.Modulation + -- set up SRS + self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation,self.Volume) + self.SRS:SetCoalition(self.Coalition) + self.SRS:SetLabel(self.MenuName or self.Name) + self.SRS:SetGender(self.Gender) + self.SRS:SetCulture(self.Culture) + self.SRS:SetPort(self.Port) + self.SRS:SetVoice(self.Voice) + if self.PathToGoogleKey then + self.SRS:SetGoogle(self.PathToGoogleKey) + end + self.SRSQueue = MSRSQUEUE:New(self.MenuName or self.Name) + return self +end + +--- [User] Set the top menu name to a custom string. +-- @param #PLAYERRECCE self +-- @param #string Name The name to use as the top menu designation. +-- @return #PLAYERRECCE self +function PLAYERRECCE:SetMenuName(Name) + self:T(self.lid.."SetMenuName: "..Name) + self.MenuName = Name + return self +end + --- [Internal] -- @param #PLAYERRECCE self -- @param #string From @@ -1033,10 +1085,3 @@ function PLAYERRECCE:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.PlayerEnterAircraft) return self end - ---[[ -local PlayerSet = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterCategories("helicopter"):FilterStart() -local Attackers = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterPrefixes({"CAS","BAI"}):FilterStart() -local myrecce = PLAYERRECCE:New("1st Forward FACA",coalition.side.BLUE,PlayerSet) -myrecce:SetAttackSet(Attackers) ---]] \ No newline at end of file diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index bc1bb67dc..a5ce049f0 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -882,6 +882,8 @@ MSRSQUEUE = { -- @field #boolean isplaying If true, transmission is currently playing. -- @field #number Tplay Mission time (abs) in seconds when the transmission should be played. -- @field #number interval Interval in seconds before next transmission. +-- @field #boolean TransmitOnlyWithPlayers If true, only transmit if there are alive Players. +-- @field Core.Set#SET_CLIENT PlayerSet PlayerSet created when TransmitOnlyWithPlayers == true --- Create a new MSRSQUEUE object for a given radio frequency/modulation. -- @param #MSRSQUEUE self @@ -932,6 +934,23 @@ function MSRSQUEUE:AddTransmission(transmission) return self end +--- Switch to only transmit if there are players on the server. +-- @param #MSRSQUEUE self +-- @param #boolean Switch If true, only send SRS if there are alive Players. +-- @return #MSRSQUEUE self +function MSRSQUEUE:SetTransmitOnlyWithPlayers(Switch) + self.TransmitOnlyWithPlayers = Switch + if Switch == false or Switch==nil then + if self.PlayerSet then + self.PlayerSet:FilterStop() + end + self.PlayerSet = nil + else + self.PlayerSet = SET_CLIENT:New():FilterStart() + end + return self +end + --- Create a new transmission and add it to the radio queue. -- @param #MSRSQUEUE self -- @param #string text Text to play. @@ -946,7 +965,13 @@ end -- @param #number modulation Radio modulation if other then MSRS default. -- @return #MSRSQUEUE.Transmission Radio transmission table. function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation) - + + if self.TransmitOnlyWithPlayers then + if self.PlayerSet and self.PlayerSet:CountAlive() == 0 then + return self + end + end + -- Sanity checks. if not text then self:E(self.lid.."ERROR: No text specified.") From dd1cd3bc79d4201b3527ad02bf66c8c4d6eeacb9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 1 Oct 2022 11:56:39 +0200 Subject: [PATCH 044/603] ATIS --- Moose Development/Moose/Ops/ATIS.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 6c98bca81..ab66d8b5a 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -590,7 +590,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.9.9" +ATIS.version = "0.9.10" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list From ddde0f86291e0a1006b0bce5aa639372a5ac9dd5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 1 Oct 2022 16:01:41 +0200 Subject: [PATCH 045/603] #PLAYERRECCE * Integrated SRS * Integrated PLAYERTASKCONTROLLER (optional), can upload target data from Recce --- Moose Development/Moose/Ops/PlayerRecce.lua | 403 ++++++++++++++++---- 1 file changed, 323 insertions(+), 80 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index dcf819fcf..c87e74f65 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -5,6 +5,7 @@ -- * Allow a player in the Gazelle to detect, smoke, flare, lase and report ground units to others. -- * Implements visual detection from the helo -- * Implements optical detection via the Vivianne system and lasing +-- * Upload target info to a PLAYERTASKCONTROLLER Instance -- -- === -- @@ -52,6 +53,14 @@ -- @field #number lasingtime -- @field #table AutoLase -- @field Core.Set#SET_CLIENT AttackSet +-- @field #boolean TransmitOnlyWithPlayers +-- @field Sound.SRS#MSRS SRS +-- @field Sound.SRS#MSRSQUEUE SRSQueue +-- @field #boolean UseController +-- @field Ops.PlayerTask#PLAYERTASKCONTROLLER Controller +-- @field #boolean ShortCallsign +-- @field #boolean Keepnumber +-- @field #table CallsignTranslations -- @extends Core.Fsm#FSM --- @@ -74,7 +83,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.1", + version = "0.0.5", ViewZone = {}, ViewZoneVisual = {}, PlayerSet = nil, @@ -88,6 +97,12 @@ PLAYERRECCE = { lasingtime = 60, AutoLase = {}, AttackSet = nil, + TransmitOnlyWithPlayers = true, + UseController = false, + Controller = nil, + ShortCallsign = true, + Keepnumber = true, + CallsignTranslations = nil, } --- @@ -130,8 +145,11 @@ PLAYERRECCE.CanLase = { ["SA342L"] = true, } ---- +--- Create and rund a new PlayerRecce instance. -- @param #PLAYERRECCE self +-- @param #string Name The name of this instance +-- @param #number Coalition, e.g. coalition.side.BLUE +-- @param Core.Set#SET_CLIENT PlayerSet The set of pilots working as recce -- @return #PLAYERRECCE self function PLAYERRECCE:New(Name, Coalition, PlayerSet) @@ -177,9 +195,15 @@ function PLAYERRECCE:New(Name, Coalition, PlayerSet) local starttime = math.random(5,10) self:__Status(-starttime) + self:I(self.lid..self.version.." Started.") + return self end +------------------------------------------------------------------------------------------ +-- TODO: Functions +------------------------------------------------------------------------------------------ + --- [Internal] Event handling -- @param #PLAYERRECCE self -- @param Core.Event#EVENTDATA EventData @@ -188,7 +212,7 @@ function PLAYERRECCE:_EventHandler(EventData) self:T(self.lid.."_EventHandler: "..EventData.id) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then if EventData.IniPlayerName then - self:I(self.lid.."Event for player: "..EventData.IniPlayerName) + self:T(self.lid.."Event for player: "..EventData.IniPlayerName) if self.ClientMenus[EventData.IniPlayerName] then self.ClientMenus[EventData.IniPlayerName]:Remove() end @@ -198,7 +222,7 @@ function PLAYERRECCE:_EventHandler(EventData) end elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then - self:I(self.lid.."Event for player: "..EventData.IniPlayerName) + self:T(self.lid.."Event for player: "..EventData.IniPlayerName) self.UnitLaserCodes[EventData.IniPlayerName] = 1688 self.ClientMenus[EventData.IniPlayerName] = nil self.LaserSpots[EventData.IniPlayerName] = nil @@ -209,6 +233,37 @@ function PLAYERRECCE:_EventHandler(EventData) return self end +--- (Internal) Function to determine clockwise direction to target. +-- @param #PLAYERRECCE self +-- @param Wrapper.Unit#UNIT unit The Helicopter +-- @param Wrapper.Unit#UNIT target The downed Group +-- @return #number direction +function PLAYERRECCE:_GetClockDirection(unit, target) + self:T(self.lid .. " _GetClockDirection") + + local _playerPosition = unit:GetCoordinate() -- get position of helicopter + local _targetpostions = target:GetCoordinate() -- get position of downed pilot + local _heading = unit:GetHeading() -- heading + local DirectionVec3 = _playerPosition:GetDirectionVec3( _targetpostions ) + local Angle = _playerPosition:GetAngleDegrees( DirectionVec3 ) + local clock = 12 + local hours = 0 + if _heading and Angle then + clock = 12 + --if angle == 0 then angle = 360 end + clock = _heading-Angle + hours = (clock/30)*-1 + clock = 12+hours + clock = UTILS.Round(clock,0) + if clock > 12 then clock = clock-12 end + end + if self.debug then + local text = string.format("Heading = %d, Angle = %d, Hours= %d, Clock = %d",_heading,Angle,hours,clock) + self:I(self.lid .. text) + end + return clock +end + --- [User] Set a table of possible laser codes. -- Each new RECCE can select a code from this table, default is 1688. -- @param #PLAYERRECCE self @@ -219,6 +274,16 @@ function PLAYERRECCE:SetLaserCodes( LaserCodes ) return self end +--- [User] Set PlayerTaskController. Allows to upload target reports to the controller, in turn creating tasks for other players. +-- @param #PLAYERRECCE self +-- @param Ops.PlayerTask#PLAYERTASKCONTROLLER Controller +-- @return #PLAYERRECCE +function PLAYERRECCE:SetPlayerTaskController(Controller) + self.UseController = true + self.Controller = Controller + return self +end + --- [User] Set a set of clients which will receive target reports -- @param #PLAYERRECCE self -- @param Core.Set#SET_CLIENT AttackSet @@ -236,7 +301,7 @@ end -- @return #number maxview in meters. -- @return #boolean cameraison If true, camera is on, else off. function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle) - self:I(self.lid.."GetGazelleVivianneSight") + self:T(self.lid.."GetGazelleVivianneSight") local unit = Gazelle -- Wrapper.Unit#UNIT if unit and unit:IsAlive() then local dcsunit = Unit.getByName(Gazelle:GetName()) @@ -273,7 +338,7 @@ end -- @param #boolean vivoff Camera on or off -- @return #number maxview Max view distance in meters function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff) - self:I(self.lid.."_GetActualMaxLOSight") + self:T(self.lid.."_GetActualMaxLOSight") if vivoff then return 0 end local maxview = 0 if unit and unit:IsAlive() then @@ -295,6 +360,24 @@ function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff) return maxview end +--- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns. +-- @param #PLAYERRECCE self +-- @param #boolean ShortCallsign If true, only call out the major flight number +-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name for players as-is, no amendments or numbers. +-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized +-- callsigns from playername or group name. +-- @return #PLAYERRECCE self +function PLAYERRECCE:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations) + if not ShortCallsign or ShortCallsign == false then + self.ShortCallsign = false + else + self.ShortCallsign = true + end + self.Keepnumber = Keepnumber or false + self.CallsignTranslations = CallsignTranslations + return self +end + --- [Internal] Build a ZONE_POLYGON from a given viewport of a unit -- @param #PLAYERRECCE self -- @param Wrapper.Unit#UNIT unit The unit which is looking @@ -306,7 +389,7 @@ end -- @param #boolean draw Draw the zone on the F10 map -- @return Core.Zone#ZONE_POLYGON ViewZone or nil if camera is off function PLAYERRECCE:_GetViewZone(unit, vheading, vnod, maxview, angle, camon, draw) - self:I(self.lid.."_GetViewZone") + self:T(self.lid.."_GetViewZone") local viewzone = nil if not camon then return nil end if unit and unit:IsAlive() then @@ -340,7 +423,7 @@ end --@return Core.Set#SET_UNIT Set of targets, can be empty! --@return #number count Count of targets function PLAYERRECCE:_GetTargetSet(unit,camera) - self:I(self.lid.."_GetTargetSet") + self:T(self.lid.."_GetTargetSet") local finaltargets = SET_UNIT:New() local finalcount = 0 local heading,nod,maxview,angle = 0,30,5000,10 @@ -365,7 +448,7 @@ function PLAYERRECCE:_GetTargetSet(unit,camera) -- determine what we can see local startpos = unit:GetCoordinate() local targetset = SET_UNIT:New():FilterCategories("ground"):FilterActive(true):FilterZones({zone}):FilterCoalitions(redcoalition):FilterOnce() - self:I("Prefilter Target Count = "..targetset:CountAlive()) + self:T("Prefilter Target Count = "..targetset:CountAlive()) -- TODO - Threat level filter? -- TODO - Min distance from unit? targetset:ForEach( @@ -373,13 +456,13 @@ function PLAYERRECCE:_GetTargetSet(unit,camera) local _unit = _unit -- Wrapper.Unit#UNIT local _unitpos = _unit:GetCoordinate() if startpos:IsLOS(_unitpos) then - self:I("Adding to final targets: ".._unit:GetName()) + self:T("Adding to final targets: ".._unit:GetName()) finaltargets:Add(_unit:GetName(),_unit) end end ) finalcount = finaltargets:CountAlive() - self:I(string.format("%s Unit: %s | Targets in view %s",self.lid,name,finalcount)) + self:T(string.format("%s Unit: %s | Targets in view %s",self.lid,name,finalcount)) end return finaltargets, finalcount, zone end @@ -389,7 +472,7 @@ end --@param Core.Set#SET_UNIT targetset Set of targets, can be empty! --@return Wrapper.Unit#UNIT Target function PLAYERRECCE:_GetHVTTarget(targetset) - self:I(self.lid.."_GetHVTTarget") + self:T(self.lid.."_GetHVTTarget") -- get one target -- local target = targetset:GetRandom() -- Wrapper.Unit#UNIT @@ -426,7 +509,7 @@ end --@param Core.Set#SET_UNIT targetset Set of targets, can be empty! --@return #PLAYERRECCE self function PLAYERRECCE:_LaseTarget(client,targetset) - self:I(self.lid.."_LaseTarget") + self:T(self.lid.."_LaseTarget") -- get one target local target = self:_GetHVTTarget(targetset) -- Wrapper.Unit#UNIT local playername = client:GetPlayerName() @@ -475,7 +558,7 @@ end -- @param #string playername -- @return #PLAYERRECCE self function PLAYERRECCE:_SetClientLaserCode(client,group,playername,code) - self:I(self.lid.."_SetClientLaserCode") + self:T(self.lid.."_SetClientLaserCode") self.UnitLaserCodes[playername] = code or 1688 if self.ClientMenus[playername] then self.ClientMenus[playername]:Remove() @@ -491,7 +574,7 @@ end -- @param #string playername -- @return #PLAYERRECCE self function PLAYERRECCE:_SwitchOnStation(client,group,playername) - self:I(self.lid.."_SwitchOnStation") + self:T(self.lid.."_SwitchOnStation") if not self.OnStation[playername] then self.OnStation[playername] = true self:__RecceOnStation(-1,client,playername) @@ -513,7 +596,7 @@ end -- @param #string playername -- @return #PLAYERRECCE self function PLAYERRECCE:_SwitchLasing(client,group,playername) - self:I(self.lid.."_SwitchLasing") + self:T(self.lid.."_SwitchLasing") if not self.AutoLase[playername] then self.AutoLase[playername] = true MESSAGE:New("Lasing is now ON",10,self.Name or "FACA"):ToClient(client) @@ -546,7 +629,7 @@ end -- @param #string playername -- @return #PLAYERRECCE self function PLAYERRECCE:_SmokeTargets(client,group,playername) - self:I(self.lid.."_SmokeTargets") + self:T(self.lid.."_SmokeTargets") local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT cameraset:AddSet(visualset) @@ -590,7 +673,7 @@ end -- @param #string playername -- @return #PLAYERRECCE self function PLAYERRECCE:_FlareTargets(client,group,playername) - self:I(self.lid.."_SmokeTargets") + self:T(self.lid.."_FlareTargets") local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT cameraset:AddSet(visualset) @@ -627,6 +710,26 @@ function PLAYERRECCE:_FlareTargets(client,group,playername) return self end +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_UploadTargets(client,group,playername) + self:T(self.lid.."_UploadTargets") + local targetset, number = self:_GetTargetSet(client,true) + local vtargetset, vnumber = self:_GetTargetSet(client,false) + local totalset = SET_UNIT:New() + totalset:AddSet(targetset) + totalset:AddSet(vtargetset) + if totalset:CountAlive() > 0 then + self.Controller:AddTarget(totalset) + self:__TargetReportSent(1,client,playername,totalset) + end + return self +end + --- [Internal] -- @param #PLAYERRECCE self -- @param Wrapper.Client#CLIENT client @@ -634,7 +737,7 @@ end -- @param #string playername -- @return #PLAYERRECCE self function PLAYERRECCE:_ReportLaserTargets(client,group,playername) -self:I(self.lid.."_ReportLaserTargets") +self:T(self.lid.."_ReportLaserTargets") local targetset, number = self:_GetTargetSet(client,true) if number > 0 and self.AutoLase[playername] then local Settings = ( client and _DATABASE:GetPlayerSettings( playername ) ) or _SETTINGS @@ -675,7 +778,7 @@ end -- @param #string playername -- @return #PLAYERRECCE self function PLAYERRECCE:_ReportVisualTargets(client,group,playername) - self:I(self.lid.."_ReportVisualTargets") + self:T(self.lid.."_ReportVisualTargets") local targetset, number = self:_GetTargetSet(client,false) if number > 0 then local Settings = ( client and _DATABASE:GetPlayerSettings( playername ) ) or _SETTINGS @@ -711,7 +814,7 @@ end -- @param #PLAYERRECCE self -- @param #PLAYERRECCE self function PLAYERRECCE:_BuildMenus() - self:I(self.lid.."_BuildMenus") + self:T(self.lid.."_BuildMenus") local clients = self.PlayerSet -- Core.Set#SET_CLIENT local clientset = clients:GetSetObjects() for _,_client in pairs(clientset) do @@ -741,6 +844,10 @@ function PLAYERRECCE:_BuildMenus() local reportL = MENU_GROUP_COMMAND:New(group,"Laser Target",targetmenu,self._ReportLaserTargets,self,client,group,playername) end local reportV = MENU_GROUP_COMMAND:New(group,"Visual Targets",targetmenu,self._ReportVisualTargets,self,client,group,playername) + if self.UseController then + local text = string.format("Target Upload to %s",self.Controller.MenuName or self.Controller.Name) + local upload = MENU_GROUP_COMMAND:New(group,text,targetmenu,self._UploadTargets,self,client,group,playername) + end if canlase then local lasecodemenu = MENU_GROUP:New(group,"Set Laser Code",self.ClientMenus[playername]) local codemenu = {} @@ -766,18 +873,20 @@ end -- @param #string playername -- @return #PLAYERRECCE self function PLAYERRECCE:_CheckNewTargets(targetset,client,playername) - self:I(self.lid.."_CheckNewTargets") - targetset:ForEachUnit( + self:T(self.lid.."_CheckNewTargets") + targetset:ForEach( function(unit) if unit and unit:IsAlive() then + self:T("Report unit: "..unit:GetName()) if not unit.PlayerRecceDetected then + self:T("New unit: "..unit:GetName()) unit.PlayerRecceDetected = { detected = true, recce = client, playername = playername, timestamp = timer.getTime() } - self:__TargetDetected(-1,unit,client,playername) + self:TargetDetected(unit,client,playername) end end end @@ -799,7 +908,7 @@ end -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS -- @return #PLAYERRECCE self function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey) - self:I(self.lid.."SetSRS") + self:T(self.lid.."SetSRS") self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" -- self.Gender = Gender or "male" -- self.Culture = Culture or "en-US" -- @@ -824,6 +933,19 @@ function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,V self.SRS:SetGoogle(self.PathToGoogleKey) end self.SRSQueue = MSRSQUEUE:New(self.MenuName or self.Name) + self.SRSQueue:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers) + return self +end + +--- [User] For SRS - Switch to only transmit if there are players on the server. +-- @param #PLAYERRECCE self +-- @param #boolean Switch If true, only send SRS if there are alive Players. +-- @return #PLAYERRECCE self +function PLAYERRECCE:SetTransmitOnlyWithPlayers(Switch) + self.TransmitOnlyWithPlayers = Switch + if self.SRSQueue then + self.SRSQueue:SetTransmitOnlyWithPlayers(Switch) + end return self end @@ -837,7 +959,27 @@ function PLAYERRECCE:SetMenuName(Name) return self end ---- [Internal] +--- [Internal] Get text for text-to-speech. +-- Numbers are spaced out, e.g. "Heading 180" becomes "Heading 1 8 0 ". +-- @param #PLAYERRECCE self +-- @param #string text Original text. +-- @return #string Spoken text. +function PLAYERRECCE:_GetTextForSpeech(text) + + -- Space out numbers. + text=string.gsub(text,"%d","%1 ") + -- get rid of leading or trailing spaces + text=string.gsub(text,"^%s*","") + text=string.gsub(text,"%s*$","") + + return text +end + +------------------------------------------------------------------------------------------ +-- TODO: FSM Functions +------------------------------------------------------------------------------------------ + +--- [Internal] Status Loop -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -853,6 +995,7 @@ function PLAYERRECCE:onafterStatus(From, Event, To) local client = Client -- Wrapper.Client#CLIENT local playername = client:GetPlayerName() if client and client:IsAlive() and self.OnStation[playername] then + -- targets on camera local targetset, targetcount, tzone = self:_GetTargetSet(client,true) if targetset then @@ -863,7 +1006,17 @@ function PLAYERRECCE:onafterStatus(From, Event, To) self.ViewZone[playername]=tzone:DrawZone(self.Coalition,{0,0,1},nil,nil,nil,1) end end - self:I({targetcount=targetcount}) + self:T({targetcount=targetcount}) + -- lase targets on camera + if targetcount > 0 then + if self.CanLase[client:GetTypeName()] and self.AutoLase[playername] then + -- DONE move to lase at will + self:_LaseTarget(client,targetset) + end + end + -- Report new targets + self:_CheckNewTargets(targetset,client,playername) + -- visual targets local vistargetset, vistargetcount, viszone = self:_GetTargetSet(client,false) if vistargetset then @@ -874,17 +1027,8 @@ function PLAYERRECCE:onafterStatus(From, Event, To) self.ViewZoneVisual[playername]=viszone:DrawZone(self.Coalition,{1,0,0},nil,nil,nil,3) end end - self:I({targetcount=targetcount}) - -- lase targets on camera - if targetcount > 0 then - if self.CanLase[client:GetTypeName()] and self.AutoLase[playername] then - -- DONE move to lase at will - self:_LaseTarget(client,targetset) - end - end - -- Report new targets - targetset:AddSet(vistargetset) - self:_CheckNewTargets(targetset,client,playername) + self:T({visualtargetcount=vistargetcount}) + self:_CheckNewTargets(vistargetset,client,playername) end end ) @@ -893,7 +1037,7 @@ function PLAYERRECCE:onafterStatus(From, Event, To) return self end ---- [Internal] +--- [Internal] Recce on station -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -902,16 +1046,30 @@ end -- @param #string Playername -- @return #PLAYERRECCE self function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername) - self:I({From, Event, To}) - local callsign = Client:GetGroup():GetCustomCallSign(true,true) + self:T({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) - local text = string.format("All stations, FACA %s on station\nat %s!",callsign, coordtext) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + if self.debug then + local text = string.format("All stations, FACA %s on station\nat %s!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + end + local text1 = "Party time!" + local text2 = string.format("All stations, FACA %s on station\nat %s!",callsign, coordtext) + local text2tts = string.format("All stations, FACA %s on station at %s!",callsign, coordtext) + text2tts = self:_GetTextForSpeech(text2tts) + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2,{grp},text1,10) + self.SRSQueue:NewTransmission(text2tts,nil,self.SRS,nil,1,{grp},text2,10) + else + MESSAGE:New(text1,10,self.Name or "FACA"):ToClient(Client) + MESSAGE:New(text2,10,self.Name or "FACA"):ToClient(Client) + end return self end ---- [Internal] +--- [Internal] Recce off station -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -920,16 +1078,28 @@ end -- @param #string Playername -- @return #PLAYERRECCE self function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername) - self:I({From, Event, To}) - local callsign = Client:GetGroup():GetCustomCallSign(true,true) + self:T({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) - local text = string.format("All stations, FACA %s leaving station\nat %s, going home!",callsign, coordtext) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + local text = string.format("All stations, FACA %s leaving station\nat %s, good bye!",callsign, coordtext) + local texttts = string.format("All stations, FACA %s leaving station at %s, good bye!",callsign, coordtext) + texttts = self:_GetTextForSpeech(texttts) + if self.debug then + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + end + local text1 = "Going home!" + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2,{grp},text1,10) + self.SRSQueue:NewTransmission(texttts,nil,self.SRS,nil,2,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end return self end ---- [Internal] +--- [Internal] Target Detected -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -939,15 +1109,36 @@ end -- @param #string Playername -- @return #PLAYERRECCE self function PLAYERRECCE:onafterTargetDetected(From, Event, To, Target, Client, Playername) - self:I({From, Event, To}) - if self.debug then - local text = string.format("New target %s detected by %s!",Target:GetTypeName(),Playername) - MESSAGE:New(text,10,self.Name or "FACA"):ToCoalition(self.Coalition) + self:T({From, Event, To}) + local dunits = "meters" + local targetdirection = self:_GetClockDirection(Client,Target) + local targetdistance = Client:GetCoordinate():Get2DDistance(Target:GetCoordinate()) or 100 + local Settings = Client and _DATABASE:GetPlayerSettings(Playername) or _SETTINGS -- Core.Settings#SETTINGS + local Threatlvl = Target:GetThreatLevel() + local ThreatTxt = "Low" + if Threatlvl >=7 then + ThreatTxt = "Medium" + elseif Threatlvl >=3 then + ThreatTxt = "High" + end + if Settings:IsMetric() then + targetdistance = UTILS.Round(targetdistance,-2) + else + targetdistance = UTILS.Round(UTILS.MetersToFeet(targetdistance),-2) + dunits = "feet" + end + local text = string.format("Target! %s! %s o\'clock, %d %s!", ThreatTxt,targetdirection, targetdistance, dunits) + local ttstext = string.format("Target! %s! %s oh clock, %d %s!", ThreatTxt, targetdirection, targetdistance, dunits) + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) end return self end ---- [Internal] +--- [Internal] Targets Smoked -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -957,16 +1148,26 @@ end -- @param Core.Set#SET_UNIT TargetSet -- @return #PLAYERRECCE self function PLAYERRECCE:onafterTargetsSmoked(From, Event, To, Client, Playername, TargetSet) - self:I({From, Event, To}) - local callsign = Client:GetGroup():GetCustomCallSign(true,true) + self:T({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) - local text = string.format("All stations, FACA %s smoked targets\nat %s!",callsign, coordtext) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + if self.debug then + local text = string.format("All stations, FACA %s smoked targets\nat %s!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + end + local text = "Smoke on!" + local ttstext = "Smoke and Mirrors!" + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end return self end ---- [Internal] +--- [Internal] Targets Flared -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -976,16 +1177,26 @@ end -- @param Core.Set#SET_UNIT TargetSet -- @return #PLAYERRECCE self function PLAYERRECCE:onafterTargetsFlared(From, Event, To, Client, Playername, TargetSet) - self:I({From, Event, To}) - local callsign = Client:GetGroup():GetCustomCallSign(true,true) + self:T({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) - local text = string.format("All stations, FACA %s flared\ntargets at %s!",callsign, coordtext) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + if self.debug then + local text = string.format("All stations, FACA %s flared\ntargets at %s!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + end + local text = "Fireworks!" + local ttstext = "Fire works!" + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end return self end ---- [Internal] +--- [Internal] Target lasing -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -996,18 +1207,28 @@ end -- @param #number Lasingtime -- @return #PLAYERRECCE self function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Lasercode, Lasingtime) - self:I({From, Event, To}) - local callsign = Client:GetGroup():GetCustomCallSign(true,true) + self:T({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition,Settings) local targettype = Target:GetTypeName() - local text = string.format("All stations, FACA %s lasing %s\nat %s!\nCode %d, Duration %d seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + if self.debug then + local text = string.format("All stations, FACA %s lasing %s\nat %s!\nCode %d, Duration %d seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + end + local text = "Lasing!" + local ttstext = "Laser on!" + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end return self end ---- [Internal] +--- [Internal] Laser lost LOS -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -1016,18 +1237,28 @@ end -- @param Wrapper.Unit#UNIT Target -- @return #PLAYERRECCE self function PLAYERRECCE:onafterTargetLOSLost(From, Event, To, Client, Target) - self:I({From, Event, To}) - local callsign = Client:GetGroup():GetCustomCallSign(true,true) + self:T({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition,Settings) local targettype = Target:GetTypeName() - local text = string.format("All stations, FACA %s lost sight of %s\nat %s!",callsign, targettype, coordtext) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + if self.debug then + local text = string.format("All stations, FACA %s lost sight of %s\nat %s!",callsign, targettype, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + end + local text = "Lost LOS!" + local ttstext = "Lost L O S!" + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end return self end ---- [Internal] +--- [Internal] Target report -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -1038,7 +1269,7 @@ end -- @param #string Text -- @return #PLAYERRECCE self function PLAYERRECCE:onafterTargetReport(From, Event, To, Client, TargetSet, Target, Text) - self:I({From, Event, To}) + self:T({From, Event, To}) MESSAGE:New(Text,45,self.Name or "FACA"):ToClient(Client) if self.AttackSet then -- send message to AttackSet @@ -1049,11 +1280,11 @@ function PLAYERRECCE:onafterTargetReport(From, Event, To, Client, TargetSet, Tar end end end - self:__TargetReportSent(-2,Client, TargetSet, Target, Text) + --self:__TargetReportSent(-2,Client, TargetSet, Target, Text) return self end ---- [Internal] +--- [Internal] Target data upload -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -1063,13 +1294,20 @@ end -- @param Wrapper.Unit#UNIT Target -- @param #string Text -- @return #PLAYERRECCE self -function PLAYERRECCE:onafterTargetReportSent(From, Event, To, Client, TargetSet, Target, Text) - self:I({From, Event, To}) +function PLAYERRECCE:onafterTargetReportSent(From, Event, To, Client, TargetSet) + self:T({From, Event, To}) + local text = "Upload completed!" + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end return self end ---- [Internal] +--- [Internal] Stop -- @param #PLAYERRECCE self -- @param #string From -- @param #string Event @@ -1085,3 +1323,8 @@ function PLAYERRECCE:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.PlayerEnterAircraft) return self end + +------------------------------------------------------------------------------------------ +-- TODO: END PLAYERRECCE +------------------------------------------------------------------------------------------ + From c351c446a0b2ec8b377d6d8087104ad0f2d8f247 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 1 Oct 2022 16:35:51 +0200 Subject: [PATCH 046/603] * SRS silent when no player is on --- Moose Development/Moose/Ops/PlayerTask.lua | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 18ed99d5e..63edec3aa 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -761,6 +761,7 @@ do -- @field #table PlayerJoinMenu -- @field #table PlayerInfoMenu -- @field #boolean noflaresmokemenu +-- @field #boolean TransmitOnlyWithPlayers -- @extends Core.Fsm#FSM --- @@ -1063,6 +1064,7 @@ PLAYERTASKCONTROLLER = { PlayerJoinMenu = {}, PlayerInfoMenu = {}, noflaresmokemenu = false, + TransmitOnlyWithPlayers = true, } --- @@ -1221,7 +1223,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.37" +PLAYERTASKCONTROLLER.version="0.1.38" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1407,6 +1409,18 @@ function PLAYERTASKCONTROLLER:SetDisableSmokeFlareTask() return self end +--- [User] For SRS - Switch to only transmit if there are players on the server. +-- @param #PLAYERTASKCONTROLLER self +-- @param #boolean Switch If true, only send SRS if there are alive Players. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetTransmitOnlyWithPlayers(Switch) + self.TransmitOnlyWithPlayers = Switch + if self.SRSQueue then + self.SRSQueue:SetTransmitOnlyWithPlayers(Switch) + end + return self +end + --- [User] Show menu entries to smoke or flare targets (on by default!) -- @param #PLAYERTASKCONTROLLER self -- @return #PLAYERTASKCONTROLLER self @@ -3034,6 +3048,7 @@ function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Cultu self.SRS:SetGoogle(self.PathToGoogleKey) end self.SRSQueue = MSRSQUEUE:New(self.MenuName or self.Name) + self.SRSQueue:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers) return self end From d9acce04b6956ef7956caaea7d42416bbc8a8b18 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 2 Oct 2022 13:16:21 +0200 Subject: [PATCH 047/603] #POINT * Added COORDINATE:ToStringFromRPShort --- Moose Development/Moose/Core/Point.lua | 37 +++++- Moose Development/Moose/Ops/PlayerRecce.lua | 129 ++++++++++++++++---- 2 files changed, 139 insertions(+), 27 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 6d6ac18df..a0f4fc877 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2970,8 +2970,8 @@ do -- COORDINATE -- * Uses default settings in COORDINATE. -- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default. -- @param #COORDINATE self - -- @param #COORDINATE ReferenceCoord The refrence coordinate. - -- @param #string ReferenceName The refrence name. + -- @param #COORDINATE ReferenceCoord The reference coordinate. + -- @param #string ReferenceName The reference name. -- @param Wrapper.Controllable#CONTROLLABLE Controllable -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @return #string The coordinate Text in the configured coordinate system. @@ -2998,7 +2998,40 @@ do -- COORDINATE return nil end + + --- Provides a coordinate string of the point, based on a coordinate format system: + -- * Uses default settings in COORDINATE. + -- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default. + -- @param #COORDINATE self + -- @param #COORDINATE ReferenceCoord The reference coordinate. + -- @param #string ReferenceName The reference name. + -- @param Wrapper.Controllable#CONTROLLABLE Controllable + -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @return #string The coordinate Text in the configured coordinate system. + function COORDINATE:ToStringFromRPShort( ReferenceCoord, ReferenceName, Controllable, Settings ) + self:F2( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } ) + + local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS + + local IsAir = Controllable and Controllable:IsAirPlane() or false + + if IsAir then + local DirectionVec3 = ReferenceCoord:GetDirectionVec3( self ) + local AngleRadians = self:GetAngleRadians( DirectionVec3 ) + local Distance = self:Get2DDistance( ReferenceCoord ) + return self:GetBRText( AngleRadians, Distance, Settings ) .. " from " .. ReferenceName + else + local DirectionVec3 = ReferenceCoord:GetDirectionVec3( self ) + local AngleRadians = self:GetAngleRadians( DirectionVec3 ) + local Distance = self:Get2DDistance( ReferenceCoord ) + return self:GetBRText( AngleRadians, Distance, Settings ) .. " from " .. ReferenceName + end + + return nil + + end + --- Provides a coordinate string of the point, based on the A2G coordinate format system. -- @param #COORDINATE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index c87e74f65..c2fabce1a 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -28,7 +28,11 @@ ------------------------------------------------------------------------------------------------------------------- -- PLAYERRECCE -- TODO: PLAYERRECCE --- TODO: A lot... +-- DONE: No messages when no targets to flare or smoke +-- TODO: Flare smoke group, not all targets +-- DONE: Messages to Attack Group, use client settings +-- DONE: Lasing dist 8km +-- DONE: Reference Point RP ------------------------------------------------------------------------------------------------------------------- --- PLAYERRECCE class. @@ -61,6 +65,9 @@ -- @field #boolean ShortCallsign -- @field #boolean Keepnumber -- @field #table CallsignTranslations +-- @field Core.Point#COORDINATE ReferencePoint +-- @field #string RPName +-- @field Wrapper.Marker#MARKER RPMarker -- @extends Core.Fsm#FSM --- @@ -83,11 +90,11 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.5", + version = "0.0.6", ViewZone = {}, ViewZoneVisual = {}, PlayerSet = nil, - debug = true, + debug = false, LaserSpots = {}, UnitLaserCodes = {}, LaserCodes = {}, @@ -103,6 +110,7 @@ PLAYERRECCE = { ShortCallsign = true, Keepnumber = true, CallsignTranslations = nil, + ReferencePoint = nil, } --- @@ -119,10 +127,10 @@ PLAYERRECCE.LaserRelativePos = { -- @type MaxViewDistance -- @field #string typename Unit type name PLAYERRECCE.MaxViewDistance = { - ["SA342M"] = 5000, - ["SA342Mistral"] = 5000, - ["SA342Minigun"] = 5000, - ["SA342L"] = 5000, + ["SA342M"] = 8000, + ["SA342Mistral"] = 8000, + ["SA342Minigun"] = 8000, + ["SA342L"] = 8000, } --- @@ -141,7 +149,7 @@ PLAYERRECCE.Cameraheight = { PLAYERRECCE.CanLase = { ["SA342M"] = true, ["SA342Mistral"] = true, - ["SA342Minigun"] = false, + ["SA342Minigun"] = false, -- no optics ["SA342L"] = true, } @@ -274,6 +282,24 @@ function PLAYERRECCE:SetLaserCodes( LaserCodes ) return self end +--- [User] Set a reference point coordinate for A2G Operations. Will be used in coordinate references. +-- @param #PLAYERRECCE self +-- @param Core.Point#COORDINATE Coordinate Coordinate of the RP +-- @param #string Name Name of the RP +-- @return #PLAYERRECCE +function PLAYERRECCE:SetReferencePoint(Coordinate,Name) + self.ReferencePoint = Coordinate + self.RPName = Name + if self.RPMarker then + self.RPMarker:Remove() + end + local text = string.format("%s RP %s\n%s\n%s\n%s",self.Name,Name,Coordinate:ToStringLLDDM(),Coordinate:ToStringLLDMS(),Coordinate:ToStringMGRS()) + self.RPMarker = MARKER:New(Coordinate,text) + self.RPMarker:ReadOnly() + self.RPMarker:ToCoalition(self.Coalition) + return self +end + --- [User] Set PlayerTaskController. Allows to upload target reports to the controller, in turn creating tasks for other players. -- @param #PLAYERRECCE self -- @param Ops.PlayerTask#PLAYERTASKCONTROLLER Controller @@ -343,7 +369,7 @@ function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff) local maxview = 0 if unit and unit:IsAlive() then local typename = unit:GetTypeName() - maxview = self.MaxViewDistance[typename] or 5000 + maxview = self.MaxViewDistance[typename] or 8000 local CamHeight = self.Cameraheight[typename] or 0 if vnod > 0 then -- Looking down @@ -426,7 +452,7 @@ function PLAYERRECCE:_GetTargetSet(unit,camera) self:T(self.lid.."_GetTargetSet") local finaltargets = SET_UNIT:New() local finalcount = 0 - local heading,nod,maxview,angle = 0,30,5000,10 + local heading,nod,maxview,angle = 0,30,8000,10 local camon = true local typename = unit:GetTypeName() local name = unit:GetName() @@ -633,7 +659,9 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT cameraset:AddSet(visualset) - self:__TargetsSmoked(-1,client,playername,cameraset) + if cameraset:CountAlive() > 0 then + self:__TargetsSmoked(-1,client,playername,cameraset) + end local highsmoke = SMOKECOLOR.Orange local medsmoke = SMOKECOLOR.White local lowsmoke = SMOKECOLOR.Green @@ -677,7 +705,9 @@ function PLAYERRECCE:_FlareTargets(client,group,playername) local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT cameraset:AddSet(visualset) - self:__TargetsFlared(-1,client,playername,cameraset) + if cameraset:CountAlive() > 0 then + self:__TargetsFlared(-1,client,playername,cameraset) + end local highsmoke = FLARECOLOR.Yellow local medsmoke = FLARECOLOR.White local lowsmoke = FLARECOLOR.Green @@ -754,8 +784,11 @@ self:T(self.lid.."_ReportLaserTargets") report:Add(string.rep("-",15)) report:Add("Target type: "..target:GetTypeName()) report:Add("Threat Level: "..ThreatGraph.." ("..ThreatLevelText..")") - report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings)) - --report:Add("Location: "..target:GetCoordinate():ToStringA2G(client,Settings)) + if not self.ReferencePoint then + report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings)) + else + report:Add("Location: "..client:GetCoordinate():ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings)) + end report:Add("Laser Code: "..self.UnitLaserCodes[playername] or 1688) report:Add(string.rep("-",15)) local text = report:Text() @@ -794,8 +827,11 @@ function PLAYERRECCE:_ReportVisualTargets(client,group,playername) report:Add(string.rep("-",15)) report:Add("Target count: "..number) report:Add("Threat Level: "..ThreatGraph.." ("..ThreatLevelText..")") - report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings)) - --report:Add("Location: "..client:GetCoordinate():ToStringA2G(client,Settings)) + if not self.ReferencePoint then + report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings)) + else + report:Add("Location: "..client:GetCoordinate():ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings)) + end report:Add(string.rep("-",15)) local text = report:Text() self:__TargetReport(-1,client,targetset,nil,text) @@ -1050,6 +1086,10 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername) local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) + if self.ReferencePoint then + local Settings = Client and _DATABASE:GetPlayerSettings(Playername) or _SETTINGS -- Core.Settings#SETTINGS + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) + end if self.debug then local text = string.format("All stations, FACA %s on station\nat %s!",callsign, coordtext) MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) @@ -1060,8 +1100,9 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername) text2tts = self:_GetTextForSpeech(text2tts) if self.UseSRS then local grp = Client:GetGroup() - self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2,{grp},text1,10) - self.SRSQueue:NewTransmission(text2tts,nil,self.SRS,nil,1,{grp},text2,10) + self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2) + self.SRSQueue:NewTransmission(text2tts,nil,self.SRS,nil,2) + MESSAGE:New(text2,10,self.Name or "FACA"):ToCoalition(self.Coalition) else MESSAGE:New(text1,10,self.Name or "FACA"):ToClient(Client) MESSAGE:New(text2,10,self.Name or "FACA"):ToClient(Client) @@ -1082,6 +1123,10 @@ function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername) local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) + if self.ReferencePoint then + local Settings = Client and _DATABASE:GetPlayerSettings(Playername) or _SETTINGS -- Core.Settings#SETTINGS + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) + end local text = string.format("All stations, FACA %s leaving station\nat %s, good bye!",callsign, coordtext) local texttts = string.format("All stations, FACA %s leaving station at %s, good bye!",callsign, coordtext) texttts = self:_GetTextForSpeech(texttts) @@ -1091,8 +1136,9 @@ function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername) local text1 = "Going home!" if self.UseSRS then local grp = Client:GetGroup() - self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2,{grp},text1,10) - self.SRSQueue:NewTransmission(texttts,nil,self.SRS,nil,2,{grp},text,10) + self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2) + self.SRSQueue:NewTransmission(texttts,nil,self.SRS,nil,2) + MESSAGE:New(text,10,self.Name or "FACA"):ToCoalition(self.Coalition) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) end @@ -1153,9 +1199,23 @@ function PLAYERRECCE:onafterTargetsSmoked(From, Event, To, Client, Playername, T local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) if self.debug then - local text = string.format("All stations, FACA %s smoked targets\nat %s!",callsign, coordtext) + local text = string.format("All stations, %s smoked targets\nat %s!",callsign, coordtext) MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) end + if self.AttackSet then + for _,_client in pairs(self.AttackSet.Set) do + local client = _client --Wrapper.Client#CLIENT + if client and client:IsAlive() then + local Settings = client and _DATABASE:GetPlayerSettings(client:GetPlayerName()) or _SETTINGS + local coordtext = coord:ToStringA2G(client,Settings) + if self.ReferencePoint then + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) + end + local text = string.format("All stations, %s smoked targets\nat %s!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) + end + end + end local text = "Smoke on!" local ttstext = "Smoke and Mirrors!" if self.UseSRS then @@ -1182,9 +1242,23 @@ function PLAYERRECCE:onafterTargetsFlared(From, Event, To, Client, Playername, T local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) if self.debug then - local text = string.format("All stations, FACA %s flared\ntargets at %s!",callsign, coordtext) + local text = string.format("All stations, %s flared\ntargets at %s!",callsign, coordtext) MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) end + if self.AttackSet then + for _,_client in pairs(self.AttackSet.Set) do + local client = _client --Wrapper.Client#CLIENT + if client and client:IsAlive() then + local Settings = client and _DATABASE:GetPlayerSettings(client:GetPlayerName()) or _SETTINGS + if self.ReferencePoint then + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) + end + local coordtext = coord:ToStringA2G(client,Settings) + local text = string.format("All stations, %s flared targets\nat %s!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) + end + end + end local text = "Fireworks!" local ttstext = "Fire works!" if self.UseSRS then @@ -1212,9 +1286,12 @@ function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Laserc local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition,Settings) + if self.ReferencePoint then + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) + end local targettype = Target:GetTypeName() if self.debug then - local text = string.format("All stations, FACA %s lasing %s\nat %s!\nCode %d, Duration %d seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime) + local text = string.format("All stations, %s lasing %s\nat %s!\nCode %d, Duration %d seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime) MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) end local text = "Lasing!" @@ -1242,9 +1319,12 @@ function PLAYERRECCE:onafterTargetLOSLost(From, Event, To, Client, Target) local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition,Settings) + if self.ReferencePoint then + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) + end local targettype = Target:GetTypeName() if self.debug then - local text = string.format("All stations, FACA %s lost sight of %s\nat %s!",callsign, targettype, coordtext) + local text = string.format("All stations, %s lost sight of %s\nat %s!",callsign, targettype, coordtext) MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) end local text = "Lost LOS!" @@ -1327,4 +1407,3 @@ end ------------------------------------------------------------------------------------------ -- TODO: END PLAYERRECCE ------------------------------------------------------------------------------------------ - From 94fb59f8bfc684dad138c66ef2e1b67e56c2334a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 2 Oct 2022 19:33:46 +0200 Subject: [PATCH 048/603] #PLAYERRECCE -- DONE: Sort for multiple targets in one direction -- DONE: Targets with forget timeout, also report --- Moose Development/Moose/Ops/PlayerRecce.lua | 257 ++++++++++++++++---- 1 file changed, 212 insertions(+), 45 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index c2fabce1a..fcef0bcb9 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -33,6 +33,8 @@ -- DONE: Messages to Attack Group, use client settings -- DONE: Lasing dist 8km -- DONE: Reference Point RP +-- DONE: Sort for multiple targets in one direction +-- DONE: Targets with forget timeout, also report ------------------------------------------------------------------------------------------------------------------- --- PLAYERRECCE class. @@ -68,6 +70,8 @@ -- @field Core.Point#COORDINATE ReferencePoint -- @field #string RPName -- @field Wrapper.Marker#MARKER RPMarker +-- @field #number TForget +-- @field Utilities.FiFo#FIFO TargetCache -- @extends Core.Fsm#FSM --- @@ -90,11 +94,11 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.6", + version = "0.0.8", ViewZone = {}, ViewZoneVisual = {}, PlayerSet = nil, - debug = false, + debug = true, LaserSpots = {}, UnitLaserCodes = {}, LaserCodes = {}, @@ -111,8 +115,17 @@ PLAYERRECCE = { Keepnumber = true, CallsignTranslations = nil, ReferencePoint = nil, + TForget = 600, + TargetCache = nil, } +--- +-- @type PlayerRecceDetected +-- @field #boolean detected +-- @field Wrapper.Client#CLIENT recce +-- @field #string playername +-- @field #number timestamp + --- -- @type LaserRelativePos -- @field #string typename Unit type name @@ -153,6 +166,28 @@ PLAYERRECCE.CanLase = { ["SA342L"] = true, } +--- +-- @type SmokeColor +-- @field #string color +PLAYERRECCE.SmokeColor = { + ["highsmoke"] = SMOKECOLOR.Orange, + ["medsmoke"] = SMOKECOLOR.White, + ["lowsmoke"] = SMOKECOLOR.Green, + ["lasersmoke"] = SMOKECOLOR.Red, + ["ownsmoke"] = SMOKECOLOR.Blue, +} + +--- +-- @type FlareColor +-- @field #string color +PLAYERRECCE.FlareColor = { + ["highflare"] =FLARECOLOR.Yellow, + ["medflare"] = FLARECOLOR.White, + ["lowflare"] = FLARECOLOR.Green, + ["laserflare"] = FLARECOLOR.Red, + ["ownflare"] = FLARECOLOR.Green, +} + --- Create and rund a new PlayerRecce instance. -- @param #PLAYERRECCE self -- @param #string Name The name of this instance @@ -176,6 +211,9 @@ function PLAYERRECCE:New(Name, Coalition, PlayerSet) self.minthreatlevel = 0 + self.TForget = 600 + self.TargetCache = FIFO:New() + -- FSM start state is STOPPED. self:SetStartState("Stopped") @@ -442,6 +480,60 @@ function PLAYERRECCE:_GetViewZone(unit, vheading, vnod, maxview, angle, camon, d return viewzone end +--- [Internal] +--@param #PLAYERRECCE self +--@param Wrapper.Client#CLIENT client +--@return Core.Set#SET_UNIT Set of targets, can be empty! +--@return #number count Count of targets +function PLAYERRECCE:_GetKnownTargets(client) + self:T(self.lid.."_GetKnownTargets") + local finaltargets = SET_UNIT:New() + local targets = self.TargetCache:GetDataTable() + local playername = client:GetPlayerName() + for _,_target in pairs(targets) do + local targetdata = _target.PlayerRecceDetected -- Ops.PlayerRecce#PLAYERRECCE.PlayerRecceDetected + if targetdata.playername == playername then + finaltargets:Add(_target:GetName(),_target) + end + end + return finaltargets,finaltargets:CountAlive() +end + +--- [Internal] +--@param #PLAYERRECCE self +--@return #PLAYERRECCE self +function PLAYERRECCE:_CleanupTargetCache() + self:T(self.lid.."_CleanupTargetCache") + local cleancache = FIFO:New() + self.TargetCache:ForEach( + function(unit) + local pull = false + if unit and unit:IsAlive() then + if unit.PlayerRecceDetected and unit.PlayerRecceDetected.timestamp then + local TNow = timer.getTime() + if TNow-unit.PlayerRecceDetected.timestamp > self.TForget then + -- Forget this unit + pull = true + unit.PlayerRecceDetected=nil + end + else + -- no timestamp + pull = true + end + else + -- dead + pull = true + end + if not pull then + cleancache:Push(unit,unit:GetName()) + end + end + ) + self.TargetCache = nil + self.TargetCache = cleancache + return self +end + --- [Internal] --@param #PLAYERRECCE self --@param Wrapper.Unit#UNIT unit The FACA unit @@ -547,9 +639,6 @@ function PLAYERRECCE:_LaseTarget(client,targetset) self.UnitLaserCodes[playername] = 1688 end laser.LaserCode = self.UnitLaserCodes[playername] or 1688 - --function laser:OnAfterLaseOff(From,Event,To) - --MESSAGE:New("Finished lasing",15,"Info"):ToClient(client) - --end self.LaserSpots[playername] = laser else laser = self.LaserSpots[playername] @@ -561,7 +650,6 @@ function PLAYERRECCE:_LaseTarget(client,targetset) local lasingtime = self.lasingtime or 60 local targettype = target:GetTypeName() laser:LaseOn(target,lasercode,lasingtime) - --MESSAGE:New(string.format("Lasing Target %s with Code %d",targettype,lasercode),15,"Info"):ToClient(client) self:__TargetLasing(-1,client,target,lasercode,lasingtime) else -- still looking at target? @@ -571,7 +659,6 @@ function PLAYERRECCE:_LaseTarget(client,targetset) local targettype = oldtarget:GetTypeName() laser:LaseOff() self:__TargetLOSLost(-1,client,oldtarget) - --MESSAGE:New(string.format("Lost LOS on target %s!",targettype),15,"Info"):ToClient(client) end end return self @@ -662,10 +749,10 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) if cameraset:CountAlive() > 0 then self:__TargetsSmoked(-1,client,playername,cameraset) end - local highsmoke = SMOKECOLOR.Orange - local medsmoke = SMOKECOLOR.White - local lowsmoke = SMOKECOLOR.Green - local lasersmoke = SMOKECOLOR.Red + local highsmoke = self.SmokeColor.highsmoke + local medsmoke = self.SmokeColor.medsmoke + local lowsmoke = self.SmokeColor.lowsmoke + local lasersmoke = self.SmokeColor.lasersmoke local laser = self.LaserSpots[playername] -- Core.Spot#SPOT -- laser targer gets extra smoke if laser and laser.Target and laser.Target:IsAlive() then @@ -708,10 +795,10 @@ function PLAYERRECCE:_FlareTargets(client,group,playername) if cameraset:CountAlive() > 0 then self:__TargetsFlared(-1,client,playername,cameraset) end - local highsmoke = FLARECOLOR.Yellow - local medsmoke = FLARECOLOR.White - local lowsmoke = FLARECOLOR.Green - local lasersmoke = FLARECOLOR.Red + local highsmoke = self.FlareColor.highflare + local medsmoke = self.FlareColor.medflare + local lowsmoke = self.FlareColor.lowflare + local lasersmoke = self.FlareColor.laserflare local laser = self.LaserSpots[playername] -- Core.Spot#SPOT -- laser targer gets extra smoke if laser and laser.Target and laser.Target:IsAlive() then @@ -812,7 +899,7 @@ end -- @return #PLAYERRECCE self function PLAYERRECCE:_ReportVisualTargets(client,group,playername) self:T(self.lid.."_ReportVisualTargets") - local targetset, number = self:_GetTargetSet(client,false) + local targetset, number = self:_GetKnownTargets(client) if number > 0 then local Settings = ( client and _DATABASE:GetPlayerSettings( playername ) ) or _SETTINGS local ThreatLevel = targetset:CalculateThreatLevelA2G() @@ -910,6 +997,7 @@ end -- @return #PLAYERRECCE self function PLAYERRECCE:_CheckNewTargets(targetset,client,playername) self:T(self.lid.."_CheckNewTargets") + local tempset = SET_UNIT:New() targetset:ForEach( function(unit) if unit and unit:IsAlive() then @@ -922,11 +1010,45 @@ function PLAYERRECCE:_CheckNewTargets(targetset,client,playername) playername = playername, timestamp = timer.getTime() } - self:TargetDetected(unit,client,playername) + --self:TargetDetected(unit,client,playername) + tempset:Add(unit:GetName(),unit) + if not self.TargetCache:HasUniqueID(unit:GetName()) then + self.TargetCache:Push(unit,unit:GetName()) + end + end + if unit.PlayerRecceDetected and unit.PlayerRecceDetected.timestamp then + local TNow = timer.getTime() + if TNow-unit.PlayerRecceDetected.timestamp > self.TForget then + unit.PlayerRecceDetected = { + detected = true, + recce = client, + playername = playername, + timestamp = timer.getTime() + } + if not self.TargetCache:HasUniqueID(unit:GetName()) then + self.TargetCache:Push(unit,unit:GetName()) + end + tempset:Add(unit:GetName(),unit) + end end end end ) + local targetsbyclock = {} + for i=1,12 do + targetsbyclock[i] = {} + end + tempset:ForEach( + function (object) + local obj=object -- Wrapper.Unit#UNIT + local clock = self:_GetClockDirection(client,obj) + table.insert(targetsbyclock[clock],obj) + end + ) + self:I("Known target Count: "..self.TargetCache:Count()) + if tempset:CountAlive() > 0 then + self:TargetDetected(targetsbyclock,client,playername) + end return self end @@ -1024,6 +1146,16 @@ end function PLAYERRECCE:onafterStatus(From, Event, To) self:I({From, Event, To}) + if not self.timestamp then + self.timestamp = timer.getTime() + else + local tNow = timer.getTime() + if tNow - self.timestamp >= 60 then + self:_CleanupTargetCache() + self.timestamp = timer.getTime() + end + end + self:_BuildMenus() self.PlayerSet:ForEachClient( @@ -1050,9 +1182,7 @@ function PLAYERRECCE:onafterStatus(From, Event, To) self:_LaseTarget(client,targetset) end end - -- Report new targets - self:_CheckNewTargets(targetset,client,playername) - + -- visual targets local vistargetset, vistargetcount, viszone = self:_GetTargetSet(client,false) if vistargetset then @@ -1064,7 +1194,8 @@ function PLAYERRECCE:onafterStatus(From, Event, To) end end self:T({visualtargetcount=vistargetcount}) - self:_CheckNewTargets(vistargetset,client,playername) + targetset:AddSet(vistargetset) + self:_CheckNewTargets(targetset,client,playername) end end ) @@ -1150,36 +1281,72 @@ end -- @param #string From -- @param #string Event -- @param #string To --- @param Wrapper.Unit#UNIT Target +-- @param #table Targetsbyclock -- @param Wrapper.Client#CLIENT Client -- @param #string Playername -- @return #PLAYERRECCE self -function PLAYERRECCE:onafterTargetDetected(From, Event, To, Target, Client, Playername) +function PLAYERRECCE:onafterTargetDetected(From, Event, To, Targetsbyclock, Client, Playername) self:T({From, Event, To}) + local dunits = "meters" - local targetdirection = self:_GetClockDirection(Client,Target) - local targetdistance = Client:GetCoordinate():Get2DDistance(Target:GetCoordinate()) or 100 local Settings = Client and _DATABASE:GetPlayerSettings(Playername) or _SETTINGS -- Core.Settings#SETTINGS - local Threatlvl = Target:GetThreatLevel() - local ThreatTxt = "Low" - if Threatlvl >=7 then - ThreatTxt = "Medium" - elseif Threatlvl >=3 then - ThreatTxt = "High" - end - if Settings:IsMetric() then - targetdistance = UTILS.Round(targetdistance,-2) - else - targetdistance = UTILS.Round(UTILS.MetersToFeet(targetdistance),-2) - dunits = "feet" - end - local text = string.format("Target! %s! %s o\'clock, %d %s!", ThreatTxt,targetdirection, targetdistance, dunits) - local ttstext = string.format("Target! %s! %s oh clock, %d %s!", ThreatTxt, targetdirection, targetdistance, dunits) - if self.UseSRS then - local grp = Client:GetGroup() - self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) - else - MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + local clientcoord = Client:GetCoordinate() + + for i=1,12 do + local targets = Targetsbyclock[i] --#table + local targetno = #targets + if targetno == 1 then + -- only one + local targetdistance = clientcoord:Get2DDistance(targets[1]:GetCoordinate()) or 100 + local Threatlvl = targets[1]:GetThreatLevel() + local ThreatTxt = "Low" + if Threatlvl >=7 then + ThreatTxt = "Medium" + elseif Threatlvl >=3 then + ThreatTxt = "High" + end + if Settings:IsMetric() then + targetdistance = UTILS.Round(targetdistance,-2) + else + targetdistance = UTILS.Round(UTILS.MetersToFeet(targetdistance),-2) + dunits = "feet" + end + local text = string.format("Target! %s! %s o\'clock, %d %s!", ThreatTxt,i, targetdistance, dunits) + local ttstext = string.format("Target! %s! %s oh clock, %d %s!", ThreatTxt, i, targetdistance, dunits) + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end + elseif targetno > 1 then + -- multiple + local function GetNearest(TTable) + local distance = 10000000 + for _,_unit in pairs(TTable) do + local dist = clientcoord:Get2DDistance(_unit:GetCoordinate()) or 100 + if dist < distance then + distance = dist + end + end + return distance + end + local targetdistance = GetNearest(targets) + if Settings:IsMetric() then + targetdistance = UTILS.Round(targetdistance,-2) + else + targetdistance = UTILS.Round(UTILS.MetersToFeet(targetdistance),-2) + dunits = "feet" + end + local text = string.format(" %d targets! %s o\'clock, %d %s!", targetno, i, targetdistance, dunits) + local ttstext = string.format("%d targets! %s oh clock, %d %s!", targetno, i, targetdistance, dunits) + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end + end end return self end From dae2da4a5da8fcd73b6fde4025e4bf6eaed67246 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 5 Oct 2022 07:32:57 +0200 Subject: [PATCH 049/603] PlayerRecce --- Moose Development/Moose/Ops/PlayerRecce.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index fcef0bcb9..300de71fe 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -370,7 +370,7 @@ function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle) if unit and unit:IsAlive() then local dcsunit = Unit.getByName(Gazelle:GetName()) local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg - local vivivertical = dcsunit:getDrawArgumentValue(216) or 0 -- L/Mistral/Minigun model has no 216, ca 10deg up (=1) and down (=-1) + local vivivertical = dcsunit:getDrawArgumentValue(216) or 0 -- L/Mistral/Minigun model has no 216, ca 30deg up (=1) and down (=-1) local vivioff = false -- -1 = -180, 1 = 180 -- Actual view -0,66 to 0,66 From db8547838175ab0af37417116f06a68676612691 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 5 Oct 2022 14:19:39 +0200 Subject: [PATCH 050/603] ZONE_BASE/RADIUS not registering in the DB if not _DATABASE:FindZone(ZoneName) then _EVENTDISPATCHER:CreateEventNewZone(self) end -- problem when creating zones on the fly and trying to find them with :FindByName() laters --- Moose Development/Moose/Core/Zone.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 296dbb3ba..1b0f432ed 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -143,6 +143,9 @@ function ZONE_BASE:New( ZoneName ) self.ZoneName = ZoneName + if not _DATABASE:FindZone(ZoneName) then + _EVENTDISPATCHER:CreateEventNewZone(self) + end --_DATABASE:AddZone(ZoneName,self) return self From 929f3d2e165b7dbca1b3836c3cbe5058f785c8ca Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 5 Oct 2022 14:27:03 +0200 Subject: [PATCH 051/603] Update PlayerRecce.lua --- Moose Development/Moose/Ops/PlayerRecce.lua | 120 +++++++++++++++----- 1 file changed, 90 insertions(+), 30 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 300de71fe..d9f2a7e1a 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -228,6 +228,7 @@ function PLAYERRECCE:New(Name, Coalition, PlayerSet) self:AddTransition("*", "TargetLOSLost", "*") self:AddTransition("*", "TargetReport", "*") self:AddTransition("*", "TargetReportSent", "*") + self:AddTransition("*", "Shack", "*") self:AddTransition("Running", "Stop", "Stopped") -- Player Events @@ -370,7 +371,7 @@ function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle) if unit and unit:IsAlive() then local dcsunit = Unit.getByName(Gazelle:GetName()) local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg - local vivivertical = dcsunit:getDrawArgumentValue(216) or 0 -- L/Mistral/Minigun model has no 216, ca 30deg up (=1) and down (=-1) + local vivivertical = dcsunit:getDrawArgumentValue(216) or 0 -- L/Mistral/Minigun model has no 216, ca 10deg up (=1) and down (=-1) local vivioff = false -- -1 = -180, 1 = 180 -- Actual view -0,66 to 0,66 @@ -508,7 +509,7 @@ function PLAYERRECCE:_CleanupTargetCache() self.TargetCache:ForEach( function(unit) local pull = false - if unit and unit:IsAlive() then + if unit and unit:IsAlive() and unit:GetLife() > 1 then if unit.PlayerRecceDetected and unit.PlayerRecceDetected.timestamp then local TNow = timer.getTime() if TNow-unit.PlayerRecceDetected.timestamp > self.TForget then @@ -573,7 +574,7 @@ function PLAYERRECCE:_GetTargetSet(unit,camera) function(_unit) local _unit = _unit -- Wrapper.Unit#UNIT local _unitpos = _unit:GetCoordinate() - if startpos:IsLOS(_unitpos) then + if startpos:IsLOS(_unitpos) and _unit:IsAlive and _unit:GetLife()>1 then self:T("Adding to final targets: ".._unit:GetName()) finaltargets:Add(_unit:GetName(),_unit) end @@ -600,7 +601,7 @@ function PLAYERRECCE:_GetHVTTarget(targetset) local minthreat = self.minthreatlevel or 0 for _,_unit in pairs(targetset.Set) do local unit = _unit -- Wrapper.Unit#UNIT - if unit and unit:IsAlive() then + if unit and unit:IsAlive() and unit:GetLife() >1 then local threat = unit:GetThreatLevel() if threat >= minthreat then -- prefer radar units @@ -654,11 +655,15 @@ function PLAYERRECCE:_LaseTarget(client,targetset) else -- still looking at target? local oldtarget=laser.Target - if targetset:IsNotInSet(oldtarget) then - -- lost LOS - local targettype = oldtarget:GetTypeName() + if targetset:IsNotInSet(oldtarget) or (not oldtarget) or (not oldtarget:IsAlive()) or (oldtarget:GetLife() < 2) then + -- lost LOS or dead + --local targettype = oldtarget:GetTypeName() laser:LaseOff() - self:__TargetLOSLost(-1,client,oldtarget) + if (not oldtarget:IsAlive()) or (oldtarget:GetLife() < 2) then + self:__Shack(-1,client,oldtarget) + else + self:__TargetLOSLost(-1,client,oldtarget) + end end end return self @@ -1129,6 +1134,8 @@ function PLAYERRECCE:_GetTextForSpeech(text) -- get rid of leading or trailing spaces text=string.gsub(text,"^%s*","") text=string.gsub(text,"%s*$","") + text=string.gsub(text,"0","zero") + text=string.gsub(text,"9","niner") return text end @@ -1221,14 +1228,13 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername) local Settings = Client and _DATABASE:GetPlayerSettings(Playername) or _SETTINGS -- Core.Settings#SETTINGS coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) end - if self.debug then - local text = string.format("All stations, FACA %s on station\nat %s!",callsign, coordtext) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) - end local text1 = "Party time!" local text2 = string.format("All stations, FACA %s on station\nat %s!",callsign, coordtext) local text2tts = string.format("All stations, FACA %s on station at %s!",callsign, coordtext) text2tts = self:_GetTextForSpeech(text2tts) + if self.debug then + self:I(text2.."\n"..text2tts) + end if self.UseSRS then local grp = Client:GetGroup() self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2) @@ -1236,7 +1242,7 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername) MESSAGE:New(text2,10,self.Name or "FACA"):ToCoalition(self.Coalition) else MESSAGE:New(text1,10,self.Name or "FACA"):ToClient(Client) - MESSAGE:New(text2,10,self.Name or "FACA"):ToClient(Client) + MESSAGE:New(text2,10,self.Name or "FACA"):ToCoalition(self.Coalition) end return self end @@ -1262,7 +1268,7 @@ function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername) local texttts = string.format("All stations, FACA %s leaving station at %s, good bye!",callsign, coordtext) texttts = self:_GetTextForSpeech(texttts) if self.debug then - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + self:I(text.."\n"..texttts) end local text1 = "Going home!" if self.UseSRS then @@ -1271,7 +1277,7 @@ function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername) self.SRSQueue:NewTransmission(texttts,nil,self.SRS,nil,2) MESSAGE:New(text,10,self.Name or "FACA"):ToCoalition(self.Coalition) else - MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + MESSAGE:New(text,10,self.Name or "FACA"):ToCoalition(self.Coalition) end return self end @@ -1365,10 +1371,6 @@ function PLAYERRECCE:onafterTargetsSmoked(From, Event, To, Client, Playername, T local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) - if self.debug then - local text = string.format("All stations, %s smoked targets\nat %s!",callsign, coordtext) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) - end if self.AttackSet then for _,_client in pairs(self.AttackSet.Set) do local client = _client --Wrapper.Client#CLIENT @@ -1408,10 +1410,6 @@ function PLAYERRECCE:onafterTargetsFlared(From, Event, To, Client, Playername, T local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local coord = Client:GetCoordinate() local coordtext = coord:ToStringBULLS(self.Coalition) - if self.debug then - local text = string.format("All stations, %s flared\ntargets at %s!",callsign, coordtext) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) - end if self.AttackSet then for _,_client in pairs(self.AttackSet.Set) do local client = _client --Wrapper.Client#CLIENT @@ -1457,9 +1455,19 @@ function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Laserc coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) end local targettype = Target:GetTypeName() - if self.debug then - local text = string.format("All stations, %s lasing %s\nat %s!\nCode %d, Duration %d seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + if self.AttackSet then + for _,_client in pairs(self.AttackSet.Set) do + local client = _client --Wrapper.Client#CLIENT + if client and client:IsAlive() then + local Settings = client and _DATABASE:GetPlayerSettings(client:GetPlayerName()) or _SETTINGS + if self.ReferencePoint then + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) + end + local coordtext = coord:ToStringA2G(client,Settings) + local text = string.format("All stations, %s lasing %s\nat %s!\nCode %d, Duration %d seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime) + MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) + end + end end local text = "Lasing!" local ttstext = "Laser on!" @@ -1472,6 +1480,49 @@ function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Laserc return self end +--- [Internal] Lased target destroyed +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param Wrapper.Unit#UNIT Target +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterShack(From, Event, To, Client, Target) + self:T({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) + local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS + local coord = Client:GetCoordinate() + local coordtext = coord:ToStringBULLS(self.Coalition,Settings) + if self.ReferencePoint then + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) + end + local targettype = Target:GetTypeName() + if self.AttackSet then + for _,_client in pairs(self.AttackSet.Set) do + local client = _client --Wrapper.Client#CLIENT + if client and client:IsAlive() then + local Settings = client and _DATABASE:GetPlayerSettings(client:GetPlayerName()) or _SETTINGS + if self.ReferencePoint then + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) + end + local coordtext = coord:ToStringA2G(client,Settings) + local text = string.format("All stations, %s lost sight of %s\nat %s!",callsign, targettype, coordtext)) + MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) + end + end + end + local text = "Shack!" + local ttstext = "Kaboom!" + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end + return self +end + --- [Internal] Laser lost LOS -- @param #PLAYERRECCE self -- @param #string From @@ -1490,9 +1541,19 @@ function PLAYERRECCE:onafterTargetLOSLost(From, Event, To, Client, Target) coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) end local targettype = Target:GetTypeName() - if self.debug then - local text = string.format("All stations, %s lost sight of %s\nat %s!",callsign, targettype, coordtext) - MESSAGE:New(text,15,self.Name or "FACA"):ToCoalition(self.Coalition) + if self.AttackSet then + for _,_client in pairs(self.AttackSet.Set) do + local client = _client --Wrapper.Client#CLIENT + if client and client:IsAlive() then + local Settings = client and _DATABASE:GetPlayerSettings(client:GetPlayerName()) or _SETTINGS + if self.ReferencePoint then + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) + end + local coordtext = coord:ToStringA2G(client,Settings) + local text = string.format("All stations, %s lost sight of %s\nat %s!",callsign, targettype, coordtext)) + MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) + end + end end local text = "Lost LOS!" local ttstext = "Lost L O S!" @@ -1553,7 +1614,6 @@ function PLAYERRECCE:onafterTargetReportSent(From, Event, To, Client, TargetSet) return self end - --- [Internal] Stop -- @param #PLAYERRECCE self -- @param #string From From 22d98fe7530b8a520c63331fbaf710a2537f1f86 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 5 Oct 2022 16:01:56 +0200 Subject: [PATCH 052/603] Update Zone.lua fix for #1797 --- Moose Development/Moose/Core/Zone.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 1b0f432ed..1e367b10d 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -143,9 +143,6 @@ function ZONE_BASE:New( ZoneName ) self.ZoneName = ZoneName - if not _DATABASE:FindZone(ZoneName) then - _EVENTDISPATCHER:CreateEventNewZone(self) - end --_DATABASE:AddZone(ZoneName,self) return self @@ -635,7 +632,7 @@ ZONE_RADIUS = { -- @param DCS#Vec2 Vec2 The location of the zone. -- @param DCS#Distance Radius The radius of the zone. -- @return #ZONE_RADIUS self -function ZONE_RADIUS:New( ZoneName, Vec2, Radius ) +function ZONE_RADIUS:New( ZoneName, Vec2, Radius, noregister ) -- Inherit ZONE_BASE. local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS @@ -643,7 +640,10 @@ function ZONE_RADIUS:New( ZoneName, Vec2, Radius ) self.Radius = Radius self.Vec2 = Vec2 - + + if not noregister then + _EVENTDISPATCHER:CreateEventNewZone(self) + end --self.Coordinate=COORDINATE:NewFromVec2(Vec2) return self @@ -1608,7 +1608,7 @@ function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset) self.relative_to_unit = Offset.relative_to_unit or false end - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) + local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius, true ) ) self:F( { ZoneName, ZoneUNIT:GetVec2(), Radius } ) @@ -1724,7 +1724,7 @@ ZONE_GROUP = { -- @param DCS#Distance Radius The radius of the zone. -- @return #ZONE_GROUP self function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneGROUP:GetVec2(), Radius ) ) + local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneGROUP:GetVec2(), Radius, true ) ) self:F( { ZoneName, ZoneGROUP:GetVec2(), Radius } ) self._.ZoneGROUP = ZoneGROUP @@ -2951,7 +2951,7 @@ do -- ZONE_AIRBASE local Airbase = AIRBASE:FindByName( AirbaseName ) - local self = BASE:Inherit( self, ZONE_RADIUS:New( AirbaseName, Airbase:GetVec2(), Radius ) ) + local self = BASE:Inherit( self, ZONE_RADIUS:New( AirbaseName, Airbase:GetVec2(), Radius, true ) ) self._.ZoneAirbase = Airbase self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2() From 44f7df2ea526bd8a1d01b23e3f1082630e9940ae Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 6 Oct 2022 08:07:54 +0200 Subject: [PATCH 053/603] Fixes --- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- Moose Development/Moose/Ops/PlayerRecce.lua | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 1b63c2bbb..1626268d3 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -380,7 +380,7 @@ end --- Get name of airwing the flight group belongs to. -- @param #FLIGHTGROUP self -- @return #string Name of the airwing or "None" if the flightgroup does not belong to any airwing. -function FLIGHTGROUP:GetAirwing() +function FLIGHTGROUP:GetAirwingName() local name=self.legion and self.legion.alias or "None" return name end diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index d9f2a7e1a..f952aedd1 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -574,7 +574,7 @@ function PLAYERRECCE:_GetTargetSet(unit,camera) function(_unit) local _unit = _unit -- Wrapper.Unit#UNIT local _unitpos = _unit:GetCoordinate() - if startpos:IsLOS(_unitpos) and _unit:IsAlive and _unit:GetLife()>1 then + if startpos:IsLOS(_unitpos) and _unit:IsAlive() and _unit:GetLife()>1 then self:T("Adding to final targets: ".._unit:GetName()) finaltargets:Add(_unit:GetName(),_unit) end @@ -1507,7 +1507,7 @@ function PLAYERRECCE:onafterShack(From, Event, To, Client, Target) coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) end local coordtext = coord:ToStringA2G(client,Settings) - local text = string.format("All stations, %s lost sight of %s\nat %s!",callsign, targettype, coordtext)) + local text = string.format("All stations, %s lost sight of %s\nat %s!",callsign, targettype, coordtext) MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) end end @@ -1550,7 +1550,7 @@ function PLAYERRECCE:onafterTargetLOSLost(From, Event, To, Client, Target) coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) end local coordtext = coord:ToStringA2G(client,Settings) - local text = string.format("All stations, %s lost sight of %s\nat %s!",callsign, targettype, coordtext)) + local text = string.format("All stations, %s lost sight of %s\nat %s!",callsign, targettype, coordtext) MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) end end From e2dce132dff576f8ac82df28d716db5aa187e2b3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 6 Oct 2022 08:14:54 +0200 Subject: [PATCH 054/603] #PLAYERTASKCONTROLLER * switch names on opening comms --- Moose Development/Moose/Ops/PlayerTask.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 63edec3aa..58e03b156 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1223,7 +1223,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.38" +PLAYERTASKCONTROLLER.version="0.1.39" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1717,7 +1717,7 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) end playername = self:_GetTextForSpeech(playername) --local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext) - local text = string.format(switchtext,self.MenuName or self.Name,playername,freqtext) + local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext) self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation) end end From 120b77d853e64cd3d5c8b800b805e20a1ce30ed4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 6 Oct 2022 13:27:26 +0200 Subject: [PATCH 055/603] #PLAYERRECCE * Gazelle sight correcions * Added Shack event --- Moose Development/Moose/Ops/PlayerRecce.lua | 71 +++++++++++++++------ 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index f952aedd1..026c9217b 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -94,7 +94,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.8", + version = "0.0.9", ViewZone = {}, ViewZoneVisual = {}, PlayerSet = nil, @@ -242,7 +242,7 @@ function PLAYERRECCE:New(Name, Coalition, PlayerSet) local starttime = math.random(5,10) self:__Status(-starttime) - self:I(self.lid..self.version.." Started.") + self:I(self.lid.." Started.") return self end @@ -304,10 +304,10 @@ function PLAYERRECCE:_GetClockDirection(unit, target) clock = UTILS.Round(clock,0) if clock > 12 then clock = clock-12 end end - if self.debug then - local text = string.format("Heading = %d, Angle = %d, Hours= %d, Clock = %d",_heading,Angle,hours,clock) - self:I(self.lid .. text) - end + --if self.debug then + --local text = string.format("Heading = %d, Angle = %d, Hours= %d, Clock = %d",_heading,Angle,hours,clock) + --self:I(self.lid .. text) + --end return clock end @@ -390,7 +390,30 @@ function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle) local heading = unit:GetHeading() local viviheading = (heading+horizontalview)%360 local maxview = self:_GetActualMaxLOSight(unit,viviheading, verticalview,vivioff) - return viviheading, verticalview, maxview, not vivioff + -- visual skew + local factor = 3.15 + self.GazelleViewFactors = { + [1]=1.18, + [2]=1.32, + [3]=1.46, + [4]=1.62, + [5]=1.77, + [6]=1.85, + [7]=2.05, + [8]=2.05, + [9]=2.3, + [10]=2.3, + [11]=2.27, + [12]=2.27, + [13]=2.43, + } + local lfac = UTILS.Round(maxview,-2) + if lfac <= 1300 then + factor = self.GazelleViewFactors[lfac/100] + maxview = math.ceil((maxview*factor)/100)*100 + end + if maxview > 8000 then maxview = 8000 end + return viviheading, verticalview,maxview, not vivioff end return 0,0,0,false end @@ -651,6 +674,15 @@ function PLAYERRECCE:_LaseTarget(client,targetset) local lasingtime = self.lasingtime or 60 local targettype = target:GetTypeName() laser:LaseOn(target,lasercode,lasingtime) + + local function Shack(dT,client,target,targettype) + self:__Shack(-1,client,target,targettype) + end + + function laser:OnAfterDestroyed(From,Event,To) + Shack(-1,client,target,targettype) + end + self:__TargetLasing(-1,client,target,lasercode,lasingtime) else -- still looking at target? @@ -659,6 +691,7 @@ function PLAYERRECCE:_LaseTarget(client,targetset) -- lost LOS or dead --local targettype = oldtarget:GetTypeName() laser:LaseOff() + self:I(self.lid.."*** Laser off!") if (not oldtarget:IsAlive()) or (oldtarget:GetLife() < 2) then self:__Shack(-1,client,oldtarget) else @@ -840,12 +873,13 @@ end -- @return #PLAYERRECCE self function PLAYERRECCE:_UploadTargets(client,group,playername) self:T(self.lid.."_UploadTargets") - local targetset, number = self:_GetTargetSet(client,true) - local vtargetset, vnumber = self:_GetTargetSet(client,false) - local totalset = SET_UNIT:New() - totalset:AddSet(targetset) - totalset:AddSet(vtargetset) - if totalset:CountAlive() > 0 then + --local targetset, number = self:_GetTargetSet(client,true) + --local vtargetset, vnumber = self:_GetTargetSet(client,false) + local totalset, count = self:_GetKnownTargets(client) + --local totalset = SET_UNIT:New() + -- totalset:AddSet(targetset) + --totalset:AddSet(vtargetset) + if count > 0 then self.Controller:AddTarget(totalset) self:__TargetReportSent(1,client,playername,totalset) end @@ -1487,8 +1521,9 @@ end -- @param #string To -- @param Wrapper.Client#CLIENT Client -- @param Wrapper.Unit#UNIT Target +-- @param #string Targettype -- @return #PLAYERRECCE self -function PLAYERRECCE:onafterShack(From, Event, To, Client, Target) +function PLAYERRECCE:onafterShack(From, Event, To, Client, Target, Targettype) self:T({From, Event, To}) local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS @@ -1497,7 +1532,7 @@ function PLAYERRECCE:onafterShack(From, Event, To, Client, Target) if self.ReferencePoint then coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) end - local targettype = Target:GetTypeName() + local targettype = Targettype if self.AttackSet then for _,_client in pairs(self.AttackSet.Set) do local client = _client --Wrapper.Client#CLIENT @@ -1507,16 +1542,16 @@ function PLAYERRECCE:onafterShack(From, Event, To, Client, Target) coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) end local coordtext = coord:ToStringA2G(client,Settings) - local text = string.format("All stations, %s lost sight of %s\nat %s!",callsign, targettype, coordtext) + local text = string.format("All stations, %s good hit on %s\nat %s!",callsign, targettype, coordtext) MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) end end end local text = "Shack!" - local ttstext = "Kaboom!" + local ttstext = "Shack!" if self.UseSRS then local grp = Client:GetGroup() - self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) end From 6a22e0368946a33fc12885a13282966808c9527c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 6 Oct 2022 17:05:35 +0200 Subject: [PATCH 056/603] #PLAYERTASKCONTROLLER * Enable PLAYERTASKCONTROLLER:EnableBuddyLasing(Recce) - buddy lasing precision bombing tasks with a PLAYERRECCE connection --- Moose Development/Moose/Ops/PlayerRecce.lua | 2 +- Moose Development/Moose/Ops/PlayerTask.lua | 80 ++++++++++++++++++--- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 026c9217b..92859842a 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -94,7 +94,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.9", + version = "0.0.10", ViewZone = {}, ViewZoneVisual = {}, PlayerSet = nil, diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 58e03b156..63ce839e5 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -82,7 +82,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.3" +PLAYERTASK.version="0.1.4" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -762,6 +762,8 @@ do -- @field #table PlayerInfoMenu -- @field #boolean noflaresmokemenu -- @field #boolean TransmitOnlyWithPlayers +-- @field #boolean buddylasing +-- @field Ops.PlayerRecce#PLAYERRECCE PlayerRecce -- @extends Core.Fsm#FSM --- @@ -937,6 +939,7 @@ do -- NONE = "None", -- POINTEROVERTARGET = "%s, %s, pointer in reach for task %03d, lasing!", -- POINTERTARGETREPORT = "\nPointer in reach: %s\nLasing: %s", +-- RECCETARGETREPORT = "\nRecce %s in reach: %s\nLasing: %s", -- POINTERTARGETLASINGTTS = ". Pointer in reach and lasing.", -- TARGET = "Target", -- FLASHON = "%s - Flashing directions is now ON!", @@ -1065,6 +1068,8 @@ PLAYERTASKCONTROLLER = { PlayerInfoMenu = {}, noflaresmokemenu = false, TransmitOnlyWithPlayers = true, + buddylasing = false, + PlayerRecce = nil, } --- @@ -1152,6 +1157,7 @@ PLAYERTASKCONTROLLER.Messages = { NONE = "None", POINTEROVERTARGET = "%s, %s, pointer in reach for task %03d, lasing!", POINTERTARGETREPORT = "\nPointer in reach: %s\nLasing: %s", + RECCETARGETREPORT = "\nRecce %s in reach: %s\nLasing: %s", POINTERTARGETLASINGTTS = ". Pointer in reach and lasing.", TARGET = "Target", FLASHON = "%s - Flashing directions is now ON!", @@ -1213,6 +1219,7 @@ PLAYERTASKCONTROLLER.Messages = { NONE = "Keine", POINTEROVERTARGET = "%s, %s, Marker im Zielbereich für %03d, Laser an!", POINTERTARGETREPORT = "\nMarker im Zielbereich: %s\nLaser an: %s", + RECCETARGETREPORT = "\nSpäher % im Zielbereich: %s\nLasing: %s", POINTERTARGETLASINGTTS = ". Marker im Zielbereich, Laser is an.", TARGET = "Ziel", FLASHON = "%s - Richtungsangaben einblenden ist EIN!", @@ -1223,7 +1230,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.39" +PLAYERTASKCONTROLLER.version="0.1.40" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1303,9 +1310,9 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self:AddTransition("*", "TaskRepeatOnFailed", "*") self:AddTransition("*", "Stop", "Stopped") - self:__Start(-1) + self:__Start(2) local starttime = math.random(5,10) - self:__Status(-starttime) + self:__Status(starttime) -- Player leaves self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) @@ -1546,6 +1553,27 @@ function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode) return self end + +--- [User] Allow precision laser-guided bombing on statics and "high-value" ground units (MBT etc) with player units lasing. +-- @param #PLAYERTASKCONTROLLER self +-- @param Ops.PlayerRecce#PLAYERRECCE Recce (Optional) The PLAYERRECCE object governing the lasing players. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:EnableBuddyLasing(Recce) + self:T(self.lid.."EnableBuddyLasing") + self.buddylasing = true + self.PlayerRecce = Recce + return self +end + +--- [User] Allow precision laser-guided bombing on statics and "high-value" ground units (MBT etc) with player units lasing. +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:DisableBuddyLasing() + self:T(self.lid.."DisableBuddyLasing") + self.buddylasing = false + return self +end + --- [User] Allow addition of targets with user F10 map markers. -- @param #PLAYERTASKCONTROLLER self -- @param #string Tag (Optional) The tagname to use to identify commands, defaults to "TASK" @@ -1583,7 +1611,7 @@ end -- @return #string playername -- @return #string ttsplayername function PLAYERTASKCONTROLLER:_GetPlayerName(Client) - self:T(self.lid.."DisablePrecisionBombing") + self:T(self.lid.."_GetPlayerName") local playername = Client:GetPlayerName() local ttsplayername = nil if not self.customcallsigns[playername] then @@ -2225,7 +2253,7 @@ function PLAYERTASKCONTROLLER:_AddTask(Target) ttstype = self.gettext:GetEntry("BAITTS",self.locale) end -- see if we can do precision bombing - if (type == AUFTRAG.Type.BAI or type == AUFTRAG.Type.CAS) and self.precisionbombing then + if (type == AUFTRAG.Type.BAI or type == AUFTRAG.Type.CAS) and (self.precisionbombing or self.buddylasing) then -- threatlevel between 3 and 6 means, it's artillery, tank, modern tank or AAA if threat > 2 and threat < 7 then type = AUFTRAG.Type.PRECISIONBOMBING @@ -2464,6 +2492,36 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) text = text .. prectext end end + if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.buddylasing then + if self.PlayerRecce then + local yes = self.gettext:GetEntry("YES",self.locale) + local no = self.gettext:GetEntry("NO",self.locale) + -- TODO make dist dependent on PlayerRecce Object + local reachdist = 8000 + local inreach = false + -- someone close enough? + local pset = self.PlayerRecce.PlayerSet:GetAliveSet() + for _,_player in pairs(pset) do + local player = _player -- Wrapper.Client#CLIENT + local pcoord = player:GetCoordinate() + if pcoord:Get2DDistance(Coordinate) <= reachdist then + inreach = true + local callsign = player:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) + local playername = player:GetPlayerName() + local islasing = no + if self.PlayerRecce.CanLase[player:GetTypeName()] and self.PlayerRecce.AutoLase[playername] then + -- TODO - maybe compare Spot target + islasing = yes + end + local inrtext = inreach == true and yes or no + local prectext = self.gettext:GetEntry("RECCETARGETREPORT",self.locale) + -- RECCETARGETREPORT = "\nSpäher % im Zielbereich: %s\nLasing: %s", + prectext = string.format(prectext,callsign,inrtext,islasing) + text = text .. prectext + end + end + end + end local clienttxt = self.gettext:GetEntry("PILOTS",self.locale) if clientcount > 0 then for _,_name in pairs(clientlist) do @@ -2758,10 +2816,12 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) local active = MENU_GROUP_DELAYED:New(group,menuactive,topmenu) local info = MENU_GROUP_COMMAND_DELAYED:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client) local mark = MENU_GROUP_COMMAND_DELAYED:New(group,menumark,active,self._MarkTask,self,group,client) - if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A or self.noflaresmokemenu then - -- no smoking/flaring here if A2A or designer has set to false - local smoke = MENU_GROUP_COMMAND_DELAYED:New(group,menusmoke,active,self._SmokeTask,self,group,client) - local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client) + if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then + if self.noflaresmokemenu ~= true then + -- no smoking/flaring here if A2A or designer has set noflaresmokemenu to true + local smoke = MENU_GROUP_COMMAND_DELAYED:New(group,menusmoke,active,self._SmokeTask,self,group,client) + local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client) + end end local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client) if self.activehasinfomenu and self.taskinfomenu then From ff194b44255fb698ad65fea24909cc86086c4dd7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 7 Oct 2022 14:29:54 +0200 Subject: [PATCH 057/603] #PLAYERRECCE * Added setting of laser distance as camera sight calc is a no-starter * Added specific laser zone of 20mxlength to catch targets to lase * Reworked detection of hit on lased target --- Moose Development/Moose/Ops/PlayerRecce.lua | 230 ++++++++++++++------ Moose Development/Moose/Ops/Target.lua | 6 +- 2 files changed, 167 insertions(+), 69 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 92859842a..552fb8672 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -45,6 +45,9 @@ -- @field #string version -- @field #table ViewZone -- @field #table ViewZoneVisual +-- @field #table ViewZoneLaser +-- @field #table LaserFOV +-- @field #table LaserTarget -- @field Core.Set#SET_CLIENT PlayerSet -- @field #string Name -- @field #number Coalition @@ -94,9 +97,12 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.10", + version = "0.0.11", ViewZone = {}, ViewZoneVisual = {}, + ViewZoneLaser = {}, + LaserFOV = {}, + LaserTarget = {}, PlayerSet = nil, debug = true, LaserSpots = {}, @@ -470,36 +476,54 @@ end -- @param #PLAYERRECCE self -- @param Wrapper.Unit#UNIT unit The unit which is looking -- @param #number vheading Heading where the unit or camera is looking --- @param #number vnod Nod down in degrees --- @param #number maxview Max line of sight, depending on height +-- @param #number minview Min line of sight - for lasing +-- @param #number maxview Max line of sight -- @param #number angle Angle left/right to be added to heading to form a triangle -- @param #boolean camon Camera is switched on --- @param #boolean draw Draw the zone on the F10 map +-- @param #boolean laser Zone is for lasing -- @return Core.Zone#ZONE_POLYGON ViewZone or nil if camera is off -function PLAYERRECCE:_GetViewZone(unit, vheading, vnod, maxview, angle, camon, draw) +function PLAYERRECCE:_GetViewZone(unit, vheading, minview, maxview, angle, camon, laser) self:T(self.lid.."_GetViewZone") local viewzone = nil - if not camon then return nil end + --if not camon then return nil end if unit and unit:IsAlive() then local unitname = unit:GetName() - if self.ViewZone[unitname] then - self.ViewZone[unitname]:UndrawZone() - end - --local vheading, vnod, maxview, vivon = self:GetGazelleVivianneSight(unit) - local startpos = unit:GetCoordinate() - local heading1 = (vheading+angle)%360 - local heading2 = (vheading-angle)%360 - local pos1 = startpos:Translate(maxview,heading1) - local pos2 = startpos:Translate(maxview,heading2) - local array = {} - table.insert(array,startpos:GetVec2()) - table.insert(array,pos1:GetVec2()) - table.insert(array,pos2:GetVec2()) - viewzone = ZONE_POLYGON:NewFromPointsArray(unitname,array) - if draw then - viewzone:DrawZone(-1,{0,0,1},nil,nil,nil,1) - self.ViewZone[unitname] = viewzone - end + if not laser then + -- Triangle + local startpos = unit:GetCoordinate() + local heading1 = (vheading+angle)%360 + local heading2 = (vheading-angle)%360 + local pos1 = startpos:Translate(maxview,heading1) + local pos2 = startpos:Translate(maxview,heading2) + local array = {} + table.insert(array,startpos:GetVec2()) + table.insert(array,pos1:GetVec2()) + table.insert(array,pos2:GetVec2()) + viewzone = ZONE_POLYGON:NewFromPointsArray(unitname,array) + else + -- Square + local startp = unit:GetCoordinate() + local heading1 = (vheading+90)%360 + local heading2 = (vheading-90)%360 + self:I({heading1,heading2}) + local startpos = startp:Translate(minview,vheading) + --startpos:MarkToAll("Startpoint") + local pos1 = startpos:Translate(10,heading1) + local pos2 = startpos:Translate(10,heading2) + local pos3 = pos1:Translate(maxview,vheading) + local pos4 = pos2:Translate(maxview,vheading) + --pos1:MarkToAll("P1") + --pos2:MarkToAll("P2") + --pos3:MarkToAll("P3") + --pos4:MarkToAll("P4") + local array = {} + --table.insert(array,startpos:GetVec2()) + table.insert(array,pos1:GetVec2()) + table.insert(array,pos2:GetVec2()) + table.insert(array,pos4:GetVec2()) + table.insert(array,pos3:GetVec2()) + viewzone = ZONE_POLYGON:NewFromPointsArray(unitname,array) + end end return viewzone end @@ -564,24 +588,43 @@ end --@param #boolean camera If true, use the unit's camera for targets in sight --@return Core.Set#SET_UNIT Set of targets, can be empty! --@return #number count Count of targets -function PLAYERRECCE:_GetTargetSet(unit,camera) +function PLAYERRECCE:_GetTargetSet(unit,camera,laser) self:T(self.lid.."_GetTargetSet") local finaltargets = SET_UNIT:New() local finalcount = 0 + local minview = 0 + local typename = unit:GetTypeName() + local playername = unit:GetPlayerName() + local maxview = self.MaxViewDistance[typename] or 5000 local heading,nod,maxview,angle = 0,30,8000,10 local camon = true - local typename = unit:GetTypeName() local name = unit:GetName() if string.find(typename,"SA342") and camera then heading,nod,maxview,camon = self:_GetGazelleVivianneSight(unit) angle=10 + -- Model nod and actual TV view don't compute + maxview = self.MaxViewDistance[typename] or 5000 else -- visual heading = unit:GetHeading() nod,maxview,camon = 10,1000,true angle = 45 end - local zone = self:_GetViewZone(unit,heading,nod,maxview,angle,camon) + if laser then + -- get min/max values + if not self.LaserFOV[playername] then + minview = 100 + maxview = 2000 + self.LaserFOV[playername] = { + min=100, + max=2000, + } + else + minview = self.LaserFOV[playername].min + maxview = self.LaserFOV[playername].max + end + end + local zone = self:_GetViewZone(unit,heading,minview,maxview,angle,camon,laser) if zone then local redcoalition = "red" if self.Coalition == coalition.side.RED then @@ -612,13 +655,9 @@ end ---[Internal] --@param #PLAYERRECCE self --@param Core.Set#SET_UNIT targetset Set of targets, can be empty! ---@return Wrapper.Unit#UNIT Target +--@return Wrapper.Unit#UNIT Target or nil function PLAYERRECCE:_GetHVTTarget(targetset) self:T(self.lid.."_GetHVTTarget") - - -- get one target - -- local target = targetset:GetRandom() -- Wrapper.Unit#UNIT - -- sort units local unitsbythreat = {} local minthreat = self.minthreatlevel or 0 @@ -641,8 +680,12 @@ function PLAYERRECCE:_GetHVTTarget(targetset) local bNum = b[2] -- Coin value of b return aNum > bNum -- Return their comparisons, < for ascending, > for descending end) - - return unitsbythreat[1][1] + + if unitsbythreat[1] and unitsbythreat[1][1] then + return unitsbythreat[1][1] + else + return nil + end end --- [Internal] @@ -667,37 +710,32 @@ function PLAYERRECCE:_LaseTarget(client,targetset) else laser = self.LaserSpots[playername] end - if not laser:IsLasing() and target then + if self.LaserTarget[playername] then + -- still looking at target? + local target=self.LaserTarget[playername] -- Ops.Target#TARGET + local oldtarget = target:GetObject() --or laser.Target + self:I("Targetstate: "..target:GetState()) + if not oldtarget or targetset:IsNotInSet(oldtarget) or target:IsDead() or target:IsDestroyed() then + -- lost LOS or dead + laser:LaseOff() + if target:IsDead() or target:IsDestroyed() or target:GetLife() < 2 then + self:__Shack(-1,client,oldtarget) + self.LaserTarget[playername] = nil + else + self:__TargetLOSLost(-1,client,oldtarget) + self.LaserTarget[playername] = nil + end + end + elseif not laser:IsLasing() and target then local relativecam = self.LaserRelativePos[client:GetTypeName()] laser:SetRelativeStartPosition(relativecam) local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688 local lasingtime = self.lasingtime or 60 local targettype = target:GetTypeName() - laser:LaseOn(target,lasercode,lasingtime) - - local function Shack(dT,client,target,targettype) - self:__Shack(-1,client,target,targettype) - end - - function laser:OnAfterDestroyed(From,Event,To) - Shack(-1,client,target,targettype) - end - + laser:LaseOn(target,lasercode,lasingtime) + self.LaserTarget[playername] = TARGET:New(target) + self.LaserTarget[playername].TStatus = 9 self:__TargetLasing(-1,client,target,lasercode,lasingtime) - else - -- still looking at target? - local oldtarget=laser.Target - if targetset:IsNotInSet(oldtarget) or (not oldtarget) or (not oldtarget:IsAlive()) or (oldtarget:GetLife() < 2) then - -- lost LOS or dead - --local targettype = oldtarget:GetTypeName() - laser:LaseOff() - self:I(self.lid.."*** Laser off!") - if (not oldtarget:IsAlive()) or (oldtarget:GetLife() < 2) then - self:__Shack(-1,client,oldtarget) - else - self:__TargetLOSLost(-1,client,oldtarget) - end - end end return self end @@ -762,6 +800,35 @@ function PLAYERRECCE:_SwitchLasing(client,group,playername) return self end +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @param #number mindist +-- @param #number maxdist +-- @return #PLAYERRECCE self +function PLAYERRECCE:_SwitchLasingDist(client,group,playername,mindist,maxdist) + self:T(self.lid.."_SwitchLasingDist") + local mind = mindist or 100 + local maxd = maxdist or 2000 + if not self.LaserFOV[playername] then + self.LaserFOV[playername] = { + min=mind, + max=maxd, + } + else + self.LaserFOV[playername].min=mind + self.LaserFOV[playername].max=maxd + end + MESSAGE:New(string.format("Laser distance set to %d-%dm!",mindist,maxdist),10,"FACA"):ToClient(client) + if self.ClientMenus[playername] then + self.ClientMenus[playername]:Remove() + self.ClientMenus[playername]=nil + end + return self +end + --- [Internal] -- @param #PLAYERRECCE self -- @param Wrapper.Client#CLIENT client @@ -894,7 +961,7 @@ end -- @return #PLAYERRECCE self function PLAYERRECCE:_ReportLaserTargets(client,group,playername) self:T(self.lid.."_ReportLaserTargets") - local targetset, number = self:_GetTargetSet(client,true) + local targetset, number = self:_GetTargetSet(client,true,true) if number > 0 and self.AutoLase[playername] then local Settings = ( client and _DATABASE:GetPlayerSettings( playername ) ) or _SETTINGS local target = self:_GetHVTTarget(targetset) -- the one we're lasing @@ -1000,6 +1067,23 @@ function PLAYERRECCE:_BuildMenus() local txtonstation = self.AutoLase[playername] and "ON" or "OFF" local text = string.format("Switch Lasing (%s)",txtonstation) local lasemenu = MENU_GROUP_COMMAND:New(group,text,self.ClientMenus[playername],self._SwitchLasing,self,client,group,playername) + local lasedist = MENU_GROUP:New(group,"Set Laser Distance",self.ClientMenus[playername]) + local mindist = 100 + local maxdist = 2000 + if self.LaserFOV[playername] and self.LaserFOV[playername].max then + maxdist = self.LaserFOV[playername].max + end + local laselist={} + for i=2,8 do + local dist1 = (i*1000)-1000 + local dist2 = i*1000 + dist1 = dist1 == 1000 and 100 or dist1 + local text = string.format("%d-%dm",dist1,dist2) + if dist2 == maxdist then + text = text .. " (*)" + end + laselist[i] = MENU_GROUP_COMMAND:New(group,text,lasedist,self._SwitchLasingDist,self,client,group,playername,dist1,dist2) + end end local targetmenu = MENU_GROUP:New(group,"Target Report",self.ClientMenus[playername]) if canlase then @@ -1216,14 +1300,26 @@ function PLAYERRECCE:onafterStatus(From, Event, To) end end self:T({targetcount=targetcount}) + -- lase targets on camera - if targetcount > 0 then - if self.CanLase[client:GetTypeName()] and self.AutoLase[playername] then - -- DONE move to lase at will - self:_LaseTarget(client,targetset) + if self.AutoLase[playername] then + local laserset, targetcount, lzone = self:_GetTargetSet(client,true,true) + if targetcount > 0 or self.LaserTarget[playername] then + if self.CanLase[client:GetTypeName()] then + -- DONE move to lase at will + self:_LaseTarget(client,laserset) + end end + if lzone then + if self.ViewZoneLaser[playername] then + self.ViewZoneLaser[playername]:UndrawZone() + end + if self.debug and tzone then + self.ViewZoneLaser[playername]=lzone:DrawZone(self.Coalition,{0,1,0},nil,nil,nil,1) + end + end + self:I({lasercount=targetcount}) end - -- visual targets local vistargetset, vistargetcount, viszone = self:_GetTargetSet(client,false) if vistargetset then @@ -1532,7 +1628,7 @@ function PLAYERRECCE:onafterShack(From, Event, To, Client, Target, Targettype) if self.ReferencePoint then coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) end - local targettype = Targettype + local targettype = "target" if self.AttackSet then for _,_client in pairs(self.AttackSet.Set) do local client = _client --Wrapper.Client#CLIENT @@ -1575,7 +1671,7 @@ function PLAYERRECCE:onafterTargetLOSLost(From, Event, To, Client, Target) if self.ReferencePoint then coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings) end - local targettype = Target:GetTypeName() + local targettype = "target" --Target:GetTypeName() if self.AttackSet then for _,_client in pairs(self.AttackSet.Set) do local client = _client --Wrapper.Client#CLIENT diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 8129059c4..167c042f9 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -69,6 +69,7 @@ TARGET = { casualties = {}, threatlevel0 = 0, conditionStart = {}, + TStatus = 30, } @@ -146,7 +147,7 @@ _TARGETID=0 --- TARGET class version. -- @field #string version -TARGET.version="0.5.2" +TARGET.version="0.5.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -185,6 +186,7 @@ function TARGET:New(TargetObject) -- Defaults. self:SetPriority() self:SetImportance() + self.TStatus = 30 -- Log ID. self.lid=string.format("TARGET #%03d | ", _TARGETID) @@ -570,7 +572,7 @@ function TARGET:onafterStatus(From, Event, To) -- Update status again in 30 sec. if self:IsAlive() then - self:__Status(-30) + self:__Status(-self.TStatus) end end From c0845443d05beb4789ca06d0302a4e3391ead70e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 9 Oct 2022 12:51:53 +0200 Subject: [PATCH 058/603] #PLAYERRECCE * Minor enhancements --- Moose Development/Moose/Core/Set.lua | 4 +- Moose Development/Moose/Ops/PlayerRecce.lua | 95 +++++++++++++++------ 2 files changed, 73 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 4e279d56f..e827cfa27 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -276,7 +276,9 @@ do -- SET_BASE -- @param Core.Set#SET_BASE SetToAdd Set to add. -- @return #SET_BASE self function SET_BASE:AddSet(SetToAdd) - + + if not SetToAdd then return self end + for _,ObjectB in pairs(SetToAdd.Set) do self:AddObject(ObjectB) end diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 552fb8672..b11826f6a 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1,4 +1,4 @@ ---- **Ops** - Allow a player in the Gazelle to detect, smoke, flare, lase and report ground units to others. +--- **Ops** - Allow a player in a helo like the Gazelle to detect, smoke, flare, lase and report ground units to others. -- -- ## Features: -- @@ -97,7 +97,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.11", + version = "0.0.12", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, @@ -272,6 +272,13 @@ function PLAYERRECCE:_EventHandler(EventData) self.ClientMenus[EventData.IniPlayerName] = nil self.LaserSpots[EventData.IniPlayerName] = nil self.OnStation[EventData.IniPlayerName] = false + self.LaserFOV[EventData.IniPlayerName] = nil + self.UnitLaserCodes[EventData.IniPlayerName] = nil + self.LaserTarget[EventData.IniPlayerName] = nil + self.AutoLase[EventData.IniPlayerName] = false + if self.ViewZone[EventData.IniPlayerName] then self.ViewZone[EventData.IniPlayerName]:UndrawZone() end + if self.ViewZoneLaser[EventData.IniPlayerName] then self.ViewZoneLaser[EventData.IniPlayerName]:UndrawZone() end + if self.ViewZoneVisual[EventData.IniPlayerName] then self.ViewZoneVisual[EventData.IniPlayerName]:UndrawZone() end end elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then @@ -280,6 +287,13 @@ function PLAYERRECCE:_EventHandler(EventData) self.ClientMenus[EventData.IniPlayerName] = nil self.LaserSpots[EventData.IniPlayerName] = nil self.OnStation[EventData.IniPlayerName] = false + self.LaserFOV[EventData.IniPlayerName] = nil + self.UnitLaserCodes[EventData.IniPlayerName] = nil + self.LaserTarget[EventData.IniPlayerName] = nil + self.AutoLase[EventData.IniPlayerName] = false + if self.ViewZone[EventData.IniPlayerName] then self.ViewZone[EventData.IniPlayerName]:UndrawZone() end + if self.ViewZoneLaser[EventData.IniPlayerName] then self.ViewZoneLaser[EventData.IniPlayerName]:UndrawZone() end + if self.ViewZoneVisual[EventData.IniPlayerName] then self.ViewZoneVisual[EventData.IniPlayerName]:UndrawZone() end self:_BuildMenus() end end @@ -364,6 +378,24 @@ function PLAYERRECCE:SetAttackSet(AttackSet) return self end +---[Internal] Check Gazelle camera in on +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param #string playername +-- @return #boolen OnOff +function PLAYERRECCE:_CameraOn(client,playername) + local camera = true + local unit = client -- Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + local dcsunit = Unit.getByName(client:GetName()) + local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg + if vivihorizontal < -0.7 or vivihorizontal > 0.7 then + camera = false + end + end + return camera +end + --- [Internal] Get the view parameters from a Gazelle camera -- @param #PLAYERRECCE self -- @param Wrapper.Unit#UNIT Gazelle @@ -485,7 +517,7 @@ end function PLAYERRECCE:_GetViewZone(unit, vheading, minview, maxview, angle, camon, laser) self:T(self.lid.."_GetViewZone") local viewzone = nil - --if not camon then return nil end + if not camon then return nil end if unit and unit:IsAlive() then local unitname = unit:GetName() if not laser then @@ -507,17 +539,11 @@ function PLAYERRECCE:_GetViewZone(unit, vheading, minview, maxview, angle, camon local heading2 = (vheading-90)%360 self:I({heading1,heading2}) local startpos = startp:Translate(minview,vheading) - --startpos:MarkToAll("Startpoint") local pos1 = startpos:Translate(10,heading1) local pos2 = startpos:Translate(10,heading2) local pos3 = pos1:Translate(maxview,vheading) local pos4 = pos2:Translate(maxview,vheading) - --pos1:MarkToAll("P1") - --pos2:MarkToAll("P2") - --pos3:MarkToAll("P3") - --pos4:MarkToAll("P4") local array = {} - --table.insert(array,startpos:GetVec2()) table.insert(array,pos1:GetVec2()) table.insert(array,pos2:GetVec2()) table.insert(array,pos4:GetVec2()) @@ -586,6 +612,7 @@ end --@param #PLAYERRECCE self --@param Wrapper.Unit#UNIT unit The FACA unit --@param #boolean camera If true, use the unit's camera for targets in sight +--@param #laser Use laser zone --@return Core.Set#SET_UNIT Set of targets, can be empty! --@return #number count Count of targets function PLAYERRECCE:_GetTargetSet(unit,camera,laser) @@ -597,7 +624,7 @@ function PLAYERRECCE:_GetTargetSet(unit,camera,laser) local playername = unit:GetPlayerName() local maxview = self.MaxViewDistance[typename] or 5000 local heading,nod,maxview,angle = 0,30,8000,10 - local camon = true + local camon = false local name = unit:GetName() if string.find(typename,"SA342") and camera then heading,nod,maxview,camon = self:_GetGazelleVivianneSight(unit) @@ -1287,23 +1314,28 @@ function PLAYERRECCE:onafterStatus(From, Event, To) function(Client) local client = Client -- Wrapper.Client#CLIENT local playername = client:GetPlayerName() + local cameraison = self:_CameraOn(client,playername) if client and client:IsAlive() and self.OnStation[playername] then - + --- + local targetset, targetcount, tzone = nil,0,nil + local laserset, lzone = nil,nil + local vistargetset, vistargetcount, viszone = nil,0,nil -- targets on camera - local targetset, targetcount, tzone = self:_GetTargetSet(client,true) - if targetset then - if self.ViewZone[playername] then - self.ViewZone[playername]:UndrawZone() - end - if self.debug and tzone then - self.ViewZone[playername]=tzone:DrawZone(self.Coalition,{0,0,1},nil,nil,nil,1) + if cameraison then + targetset, targetcount, tzone = self:_GetTargetSet(client,true) + if targetset then + if self.ViewZone[playername] then + self.ViewZone[playername]:UndrawZone() + end + if self.debug and tzone then + self.ViewZone[playername]=tzone:DrawZone(self.Coalition,{0,0,1},nil,nil,nil,1) + end end + self:T({targetcount=targetcount}) end - self:T({targetcount=targetcount}) - -- lase targets on camera - if self.AutoLase[playername] then - local laserset, targetcount, lzone = self:_GetTargetSet(client,true,true) + if self.AutoLase[playername] and cameraison then + laserset, targetcount, lzone = self:_GetTargetSet(client,true,true) if targetcount > 0 or self.LaserTarget[playername] then if self.CanLase[client:GetTypeName()] then -- DONE move to lase at will @@ -1321,7 +1353,7 @@ function PLAYERRECCE:onafterStatus(From, Event, To) self:I({lasercount=targetcount}) end -- visual targets - local vistargetset, vistargetcount, viszone = self:_GetTargetSet(client,false) + vistargetset, vistargetcount, viszone = self:_GetTargetSet(client,false) if vistargetset then if self.ViewZoneVisual[playername] then self.ViewZoneVisual[playername]:UndrawZone() @@ -1331,8 +1363,21 @@ function PLAYERRECCE:onafterStatus(From, Event, To) end end self:T({visualtargetcount=vistargetcount}) - targetset:AddSet(vistargetset) - self:_CheckNewTargets(targetset,client,playername) + if targetset then + vistargetset:AddSet(targetset) + end + if laserset then + vistargetset:AddSet(laserset) + end + if not cameraison and self.debug then + if self.ViewZoneLaser[playername] then + self.ViewZoneLaser[playername]:UndrawZone() + end + if self.ViewZone[playername] then + self.ViewZone[playername]:UndrawZone() + end + end + self:_CheckNewTargets(vistargetset,client,playername) end end ) From 2b6eba0106a4af35a2f02821fdc2dbd3b3d3be7b Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 10 Oct 2022 14:02:37 +0200 Subject: [PATCH 059/603] Update PlayerTask.lua #PLAYERTASK * Added Coalition, Freetext and FreetextTTS addition options --- Moose Development/Moose/Ops/PlayerTask.lua | 67 +++++++++++++++++++++- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 63ce839e5..b31cdfeec 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update September 2022 +-- @date Last Update October 2022 do @@ -50,6 +50,9 @@ do -- @field Ops.PlayerTask#PLAYERTASKCONTROLLER TaskController -- @field #number timestamp -- @field #number lastsmoketime +-- @field #number coalition +-- @field #string Freetext +-- @field #string FreetextTTS -- @extends Core.Fsm#FSM @@ -78,11 +81,13 @@ PLAYERTASK = { TaskController = nil, timestamp = 0, lastsmoketime = 0, + Freetext = nil, + FreetextTTS = nil, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.4" +PLAYERTASK.version="0.1.5" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -252,6 +257,60 @@ function PLAYERTASK:_SetController(Controller) return self end +--- [User] Set a coalition side for this task +-- @param #PLAYERTASK self +-- @param #number Coalition Coaltion side to add, e.g. coalition.side.BLUE +-- @return #PLAYERTASK self +function PLAYERTASK:SetCoalition(Coalition) + self:T(self.lid.."SetCoalition") + self.coalition = Coalition or coalition.side.BLUE + return self +end + +--- [User] Get the coalition side for this task +-- @param #PLAYERTASK self +-- @return #number Coalition Coaltion side, e.g. coalition.side.BLUE, or nil if not set +function PLAYERTASK:GetCoalition() + self:T(self.lid.."GetCoalition") + return self.coalition +end + +--- [USER] Add a free text description to this task. +-- @param #PLAYERTASK self +-- @param #string Text +-- @return #PLAYERTASK self +function PLAYERTASK:AddFreetext(Text) + self:T(self.lid.."AddFreetext") + self.Freetext = Text + return self +end + +--- [USER] Get the free text description from this task. +-- @param #PLAYERTASK self +-- @return #string Text +function PLAYERTASK:GetFreetext() + self:T(self.lid.."GetFreetext") + return self.Freetext +end + +--- [USER] Add a free text description for TTS to this task. +-- @param #PLAYERTASK self +-- @param #string Text +-- @return #PLAYERTASK self +function PLAYERTASK:AddFreetextTTS(TextTTS) + self:T(self.lid.."AddFreetextTTS") + self.FreetextTTS = TextTTS + return self +end + +--- [USER] Get the free text TTS description from this task. +-- @param #PLAYERTASK self +-- @return #string Text +function PLAYERTASK:GetFreetextTTS() + self:T(self.lid.."GetFreetextTTS") + return self.FreetextTTS +end + --- [User] Check if task is done -- @param #PLAYERTASK self -- @return #boolean done @@ -1085,8 +1144,10 @@ PLAYERTASKCONTROLLER.Type = { A2GS = "Air-To-Ground-Sea", } ---- Define a new AUFTRAG Type +--- Define new AUFTRAG Types AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing" +AUFTRAG.Type.CTLD = "Combat Transport" +AUFTRAG.Type.CSAR "Combat Rescue" --- -- @type SeadAttributes From 19ca30527e48731322ab47b0fb3ac2926faaad40 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 10 Oct 2022 14:30:54 +0200 Subject: [PATCH 060/603] Update PlayerTask.lua Added PLAYERTASK:IlluminateTarget(Power,Height) --- Moose Development/Moose/Ops/PlayerTask.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index b31cdfeec..60eacc0dd 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -482,6 +482,25 @@ function PLAYERTASK:FlareTarget(Color) return self end +--- [User] Illuminate Target Area +-- @param #PLAYERTASK self +-- @param #number Power Power of illumination bomb in Candela. Default 1000 cd. +-- @param #number Height Height above target used to release the bomb, default 150m. +-- @return #PLAYERTASK self +function PLAYERTASK:IlluminateTarget(Power,Height) + self:T(self.lid.."IlluminateTarget") + local Power = Power or 1000 + local Height = Height or 150 + if self.Target then + local coordinate = self.Target:GetCoordinate() + if coordinate then + local bcoord = COORDINATE:NewFromVec2( coordinate:GetVec2(), Height ) + bcoord:IlluminationBomb(Power) + end + end + return self +end + -- success / failure function addion courtesy @FunkyFranky. --- [User] Add success condition. From 03231442489145493d153d74578ce239dbee1ea1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 10 Oct 2022 16:55:58 +0200 Subject: [PATCH 061/603] #Playertask --- Moose Development/Moose/Ops/PlayerTask.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 63ce839e5..e51289c6a 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -50,6 +50,7 @@ do -- @field Ops.PlayerTask#PLAYERTASKCONTROLLER TaskController -- @field #number timestamp -- @field #number lastsmoketime +-- @field #number coalition -- @extends Core.Fsm#FSM @@ -95,6 +96,7 @@ PLAYERTASK.version="0.1.4" -- @param Ops.Target#TARGET Target Target for this task -- @param #boolean Repeat Repeat this task if true (default = false) -- @param #number Times Repeat on failure this many times if Repeat is true (default = 1) +-- @param #string TTSType Task type name for TTS -- @return #PLAYERTASK self function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType) @@ -252,6 +254,16 @@ function PLAYERTASK:_SetController(Controller) return self end +--- [User] Add a coalition for this task +-- @param #PLAYERTASK self +-- @param# number Coalition Coalition, e.g. coalition.side.BLUE +-- @return #PLAYERTASK self +function PLAYERTASK:SetCoalition(Coalition) + self:T(self.lid.."SetCoalition") + self.coalition = Coalition or coalition.side.BLUE + return self +end + --- [User] Check if task is done -- @param #PLAYERTASK self -- @return #boolean done @@ -1087,6 +1099,8 @@ PLAYERTASKCONTROLLER.Type = { --- Define a new AUFTRAG Type AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing" +AUFTRAG.Type.CTLD = "Transport" +AUFTRAG.Type.CSAR = "Rescue" --- -- @type SeadAttributes From aaa956287f03c594519f313eefe0dba09356e011 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 10 Oct 2022 17:43:12 +0200 Subject: [PATCH 062/603] #Core.Point * Cleanup GetClosestAirbase() --- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index c6e239417..60eacc0dd 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update September 2022 +-- @date Last Update October 2022 do From d6363d0f8005c8a78e815a677875fc8bd3ff908a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 12 Oct 2022 09:33:57 +0200 Subject: [PATCH 063/603] Misc --- Moose Development/Moose/Ops/CTLD.lua | 52 +++++++++++++-- Moose Development/Moose/Ops/PlayerTask.lua | 73 +++++++++++++++++++--- Moose Development/Moose/Ops/Target.lua | 6 +- Moose Development/Moose/Wrapper/Static.lua | 24 +++++++ 4 files changed, 139 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 6ec93761c..a25f01a5e 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -23,7 +23,7 @@ -- @image OPS_CTLD.jpg -- Date: Feb 2022 --- Last Update Sep 2022 +-- Last Update October 2022 do @@ -343,7 +343,7 @@ CTLD_CARGO = { CRATE = "Crate", -- #string crate REPAIR = "Repair", -- #string repair ENGINEERS = "Engineers", -- #string engineers - STATIC = "Static", -- #string engineers + STATIC = "Static", -- #string statics } --- Function to create new CTLD_CARGO object. @@ -574,6 +574,10 @@ CTLD_CARGO = { end do +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +-- TODO CTLD +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + ------------------------------------------------------------------------- --- **CTLD** class, extends Core.Base#BASE, Core.Fsm#FSM -- @type CTLD @@ -1068,7 +1072,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.11" +CTLD.version="1.0.12" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1476,6 +1480,19 @@ function CTLD:SetTroopDropZoneRadius(Radius) return self end +--- (User) Add a PLAYERTASK - FSM events will check success +-- @param #CTLD self +-- @param Ops.PlayerTask#PLAYERTASK PlayerTask +-- @return #CTLD self +function CTLD:AddPlayerTask(PlayerTask) + self:T(self.lid .. " AddPlayerTask") + if not self.PlayerTaskQueue then + self.PlayerTaskQueue = FIFO:New() + end + self.PlayerTaskQueue:Push(PlayerTask,PlayerTask.PlayerTaskNr) + return self +end + --- (Internal) Event handler function -- @param #CTLD self -- @param Core.Event#EVENTDATA EventData @@ -4244,7 +4261,7 @@ end end ------------------------------------------------------------------- --- FSM functions +-- TODO FSM functions ------------------------------------------------------------------- --- (Internal) FSM Function onafterStart. @@ -4445,9 +4462,30 @@ end -- @return #CTLD self function CTLD:onbeforeCratesBuild(From, Event, To, Group, Unit, Vehicle) self:T({From, Event, To}) + if Unit and Unit:IsPlayer() and self.PlayerTaskQueue then + local playername = Unit:GetPlayerName() + local dropcoord = Vehicle:GetCoordinate() or COORDINATE:New(0,0,0) + local dropvec2 = dropcoord:GetVec2() + self.PlayerTaskQueue:ForEach( + function (Task) + local task = Task -- Ops.PlayerTask#PLAYERTASK + local subtype = task:GetSubType() + -- right subtype? + if Event == subtype and not task:IsDone() then + local targetzone = task.Target:GetObject() -- Core.Zone#ZONE should be a zone in this case .... + if targetzone and targetzone.ClassName and string.match(targetzone.ClassName,"ZONE") and targetzone:IsVec2InZone(dropvec2) then + if task.Clients:HasUniqueID(playername) then + -- success + task:__Success(-1) + end + end + end + end + ) + end return self end - + --- (Internal) FSM Function onbeforeTroopsRTB. -- @param #CTLD self -- @param #string From State. @@ -4802,7 +4840,9 @@ end -- end do do --- **Hercules Cargo AIR Drop Events** by Anubis Yinepu -- Moose CTLD OO refactoring by Applevangelist --- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +-- TODO CTLD_HERCULES +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -- This script will only work for the Herculus mod by Anubis, and only for **Air Dropping** cargo from the Hercules. -- Use the standard Moose CTLD if you want to unload on the ground. -- Payloads carried by pylons 11, 12 and 13 need to be declared in the Herculus_Loadout.lua file diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 60eacc0dd..af4a207d0 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1,11 +1,11 @@ ---- **Ops** - PlayerTask (mission) for Players. +---- **Ops** - PlayerTask (mission) for Players. -- -- ## Main Features: -- -- * Simplifies defining and executing Player tasks -- * FSM events when a mission is added, done, successful or failed, replanned -- * Ready to use SRS and localization --- * Mission locations can be smoked, flared and marked on the map +-- * Mission locations can be smoked, flared, illuminated and marked on the map -- -- === -- @@ -53,6 +53,7 @@ do -- @field #number coalition -- @field #string Freetext -- @field #string FreetextTTS +-- @field #string TaskSubType -- @extends Core.Fsm#FSM @@ -81,13 +82,14 @@ PLAYERTASK = { TaskController = nil, timestamp = 0, lastsmoketime = 0, - Freetext = nil, - FreetextTTS = nil, + Freetext = nil, + FreetextTTS = nil, + TaskSubType = nil, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.5" +PLAYERTASK.version="0.1.6" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -100,6 +102,7 @@ PLAYERTASK.version="0.1.5" -- @param Ops.Target#TARGET Target Target for this task -- @param #boolean Repeat Repeat this task if true (default = false) -- @param #number Times Repeat on failure this many times if Repeat is true (default = 1) +-- @param #string TTSType TTS friendly task type name -- @return #PLAYERTASK self function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType) @@ -285,6 +288,24 @@ function PLAYERTASK:AddFreetext(Text) return self end +--- [USER] Set a task sub type description to this task. +-- @param #PLAYERTASK self +-- @param #string Type +-- @return #PLAYERTASK self +function PLAYERTASK:SetSubType(Type) + self:T(self.lid.."AddSubType") + self.TaskSubType = Type + return self +end + +--- [USER] Get task sub type description from this task. +-- @param #PLAYERTASK self +-- @return #string Type or nil +function PLAYERTASK:GetSubType() + self:T(self.lid.."GetSubType") + return self.TaskSubType +end + --- [USER] Get the free text description from this task. -- @param #PLAYERTASK self -- @return #string Text @@ -842,6 +863,7 @@ do -- @field #boolean TransmitOnlyWithPlayers -- @field #boolean buddylasing -- @field Ops.PlayerRecce#PLAYERRECCE PlayerRecce +-- @field #number Coalition -- @extends Core.Fsm#FSM --- @@ -1147,7 +1169,8 @@ PLAYERTASKCONTROLLER = { noflaresmokemenu = false, TransmitOnlyWithPlayers = true, buddylasing = false, - PlayerRecce = nil, + PlayerRecce = nil, + Coalition = nil, } --- @@ -1166,7 +1189,7 @@ PLAYERTASKCONTROLLER.Type = { --- Define new AUFTRAG Types AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing" AUFTRAG.Type.CTLD = "Combat Transport" -AUFTRAG.Type.CSAR "Combat Rescue" +AUFTRAG.Type.CSAR = "Combat Rescue" --- -- @type SeadAttributes @@ -2425,6 +2448,42 @@ function PLAYERTASKCONTROLLER:_AddTask(Target) return self end +--- [User] Add a PLAYERTASK object to the list of (open) tasks +-- @param #PLAYERTASKCONTROLLER self +-- @param Ops.PlayerTask#PLAYERTASK PlayerTask +-- @return #PLAYERTASKCONTROLLER self +-- @usage +-- Example to create a PLAYERTASK of type CTLD and give Players 10 minutes to complete: +-- +-- local newtask = PLAYERTASK:New(AUFTRAG.Type.CTLD,ZONE:Find("Unloading"),false,0,"Combat Transport") +-- newtask.Time0 = timer.getAbsTime() -- inject a timestamp for T0 +-- newtask:AddFreetext("Transport crates to the drop zone and build a vehicle in the next 10 minutes!") +-- +-- -- add a condition for failure - fail after 10 minutes +-- newtask:AddConditionFailure( +-- function() +-- local Time = timer.getAbsTime() +-- if Time - newtask.Time0 > 600 then +-- return true +-- end +-- return false +-- end +-- ) +-- +-- taskmanager:AddPlayerTaskToQueue(PlayerTask) +function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask) + self:T(self.lid.."AddPlayerTaskToQueue") + if PlayerTask and PlayerTask.ClassName and PlayerTask.ClassName == "PLAYERTASK" then + PlayerTask:_SetController(self) + PlayerTask:SetCoalition(self.Coalition) + self.TaskQueue:Push(PlayerTask) + self:__TaskAdded(-1,PlayerTask) + else + self:E(self.lid.."***** NO valid PAYERTASK object sent!") + end + return self +end + --- [Internal] Join a player to a task -- @param #PLAYERTASKCONTROLLER self -- @param Wrapper.Group#GROUP Group diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 167c042f9..901dcb5e4 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -147,7 +147,7 @@ _TARGETID=0 --- TARGET class version. -- @field #string version -TARGET.version="0.5.3" +TARGET.version="0.5.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -830,8 +830,8 @@ function TARGET:_AddObject(Object) if static and static:IsAlive() then - target.Life0=1 - target.Life=1 + target.Life0=static:GetLife0() + target.Life=static:GetLife() target.N0=target.N0+1 table.insert(self.elements, target.Name) diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 08c9a0bce..fef0b098d 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -55,9 +55,33 @@ STATIC = { function STATIC:Register( StaticName ) local self = BASE:Inherit( self, POSITIONABLE:New( StaticName ) ) self.StaticName = StaticName + + local DCSStatic = StaticObject.getByName( self.StaticName ) + if DCSStatic then + local Life0 = DCSStatic:getLife() or 1 + self.Life0 = Life0 + end + return self end +--- Get initial life points +-- @param #STATIC self +-- @return #number lifepoints +function STATIC:GetLife0() + return self.Life0 or 1 +end + +--- Get current life points +-- @param #STATIC self +-- @return #number lifepoints or nil +function STATIC:GetLife() + local DCSStatic = StaticObject.getByName( self.StaticName ) + if DCSStatic then + return DCSStatic:getLife() or 1 + end + return nil +end --- Finds a STATIC from the _DATABASE using a DCSStatic object. -- @param #STATIC self From 9a2829730c94909d04ed33a7751970f1adb78234 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 12 Oct 2022 12:46:32 +0200 Subject: [PATCH 064/603] #CTLD PlayerTask --- Moose Development/Moose/Ops/CTLD.lua | 30 ++++++++++++++++++---- Moose Development/Moose/Ops/PlayerTask.lua | 10 ++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index a25f01a5e..3b5920842 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -288,8 +288,8 @@ CTLD_ENGINEERING = { end -do - + +do ------------------------------------------------------ --- **CTLD_CARGO** class, extends Core.Base#BASE -- @type CTLD_CARGO @@ -308,7 +308,6 @@ do -- @field #string Subcategory Sub-category name. -- @extends Core.Base#BASE - --- -- @field CTLD_CARGO CTLD_CARGO = { @@ -1072,7 +1071,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.12" +CTLD.version="1.0.14" --- Instantiate a new CTLD. -- @param #CTLD self @@ -4434,6 +4433,27 @@ end -- @return #CTLD self function CTLD:onbeforeTroopsDeployed(From, Event, To, Group, Unit, Troops) self:T({From, Event, To}) + if Unit and Unit:IsPlayer() and self.PlayerTaskQueue then + local playername = Unit:GetPlayerName() + local dropcoord = Troops:GetCoordinate() or COORDINATE:New(0,0,0) + local dropvec2 = dropcoord:GetVec2() + self.PlayerTaskQueue:ForEach( + function (Task) + local task = Task -- Ops.PlayerTask#PLAYERTASK + local subtype = task:GetSubType() + -- right subtype? + if Event == subtype and not task:IsDone() then + local targetzone = task.Target:GetObject() -- Core.Zone#ZONE should be a zone in this case .... + if targetzone and targetzone.ClassName and string.match(targetzone.ClassName,"ZONE") and targetzone:IsVec2InZone(dropvec2) then + if task.Clients:HasUniqueID(playername) then + -- success + task:__Success(-1) + end + end + end + end + ) + end return self end @@ -4461,7 +4481,7 @@ end -- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB build. -- @return #CTLD self function CTLD:onbeforeCratesBuild(From, Event, To, Group, Unit, Vehicle) - self:T({From, Event, To}) + self:I({From, Event, To}) if Unit and Unit:IsPlayer() and self.PlayerTaskQueue then local playername = Unit:GetPlayerName() local dropcoord = Vehicle:GetCoordinate() or COORDINATE:New(0,0,0) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index af4a207d0..2bbc4f1e7 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -332,6 +332,16 @@ function PLAYERTASK:GetFreetextTTS() return self.FreetextTTS end +--- [USER] Add a short free text description for the menu entry of this task. +-- @param #PLAYERTASK self +-- @param #string Text +-- @return #PLAYERTASK self +function PLAYERTASK:SetMenuName(Text) + self:T(self.lid.."SetMenuName") + self.Target.name = Text + return self +end + --- [User] Check if task is done -- @param #PLAYERTASK self -- @return #boolean done From 751f84befd823ffb259141ddf75c25d7e4c30610 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 12 Oct 2022 16:22:58 +0200 Subject: [PATCH 065/603] #SCENERY --- Moose Development/Moose/Wrapper/Scenery.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 445154037..377055e99 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -58,10 +58,10 @@ function SCENERY:GetThreatLevel() return 0, "Scenery" end ---- Find a SCENERY object by it's name/id. +--- Create a SCENERY object from it's name/id. --@param #SCENERY self --@param #string name The name/id of the scenery object as taken from the ME. Ex. '595785449' ---@return #SCENERY Scenery Object or nil if not found. +--@return #SCENERY Scenery Object - **Note** this might not point to anything useful. Check with `myscenery:IsAlive()` if it is valid. function SCENERY:FindByName(name) local findAirbase = function () local airbases = AIRBASE.GetAllAirbases() From d0b6791efd38a1e8a7f869baf292cab75d0feb04 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 13 Oct 2022 10:48:51 +0200 Subject: [PATCH 066/603] #SCENERY - Improvements --- Moose Development/Moose/Wrapper/Scenery.lua | 85 +++++++++++++-------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 0902bc269..cb00a6057 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -4,7 +4,7 @@ -- -- ### Author: **FlightControl** -- --- ### Contributions: +-- ### Contributions: **Applevangelist** -- -- === -- @@ -14,6 +14,9 @@ --- @type SCENERY +-- @field #string ClassName +-- @field #string SceneryName +-- @field #DCS.Object SceneryObject -- @extends Wrapper.Positionable#POSITIONABLE @@ -43,14 +46,14 @@ function SCENERY:Register( SceneryName, SceneryObject ) return self end ---- Register scenery object as POSITIONABLE. +--- Obtain DCS Object from the SCENERY Object. --@param #SCENERY self --@return #DCS.Object DCS scenery object. function SCENERY:GetDCSObject() return self.SceneryObject end ---- Register scenery object as POSITIONABLE. +--- Get the threat level of a SCENERY object. Always 0. --@param #SCENERY self --@return #number Threat level 0. --@return #string "Scenery". @@ -58,39 +61,59 @@ function SCENERY:GetThreatLevel() return 0, "Scenery" end ---- Create a SCENERY object from it's name/id. +--- Find a SCENERY object from it's name/id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first +-- to find the correct object. --@param #SCENERY self ---@param #string name The name/id of the scenery object as taken from the ME. Ex. '595785449' ---@return #SCENERY Scenery Object - **Note** this might not point to anything useful. Check with `myscenery:IsAlive()` if it is valid. -function SCENERY:FindByName(name) - local findAirbase = function () - local airbases = AIRBASE.GetAllAirbases() - for index,airbase in pairs(airbases) do - local surftype = airbase:GetCoordinate():GetSurfaceType() - if surftype ~= land.SurfaceType.SHALLOW_WATER and surftype ~= land.SurfaceType.WATER then - return airbase:GetCoordinate() - end - end - return nil - end - - local sceneryScan = function (scancoord) - if scancoord ~= nil then - local _,_,sceneryfound,_,_,scenerylist = scancoord:ScanObjects(200, false, false, true) - if sceneryfound == true then - scenerylist[1].id_ = name - SCENERY.SceneryObject = SCENERY:Register(scenerylist[1].id_, scenerylist[1]) - return SCENERY.SceneryObject +--@param #string Name The name/id of the scenery object as taken from the ME. Ex. '595785449' +--@param Core.Point#COORDINATE Coordinate Where to find the scenery object +--@param #number Radius (optional) Search radius around coordinate, defaults to 100 +--@return #SCENERY Scenery Object or `nil` if it cannot be found +function SCENERY:FindByName(Name, Coordinate, Radius) + + local radius = Radius or 100 + local name = Name or "unknown" + local scenery = nil + + --- + -- @param Core.Point#COORDINATE coordinate + -- @param #number radius + -- @param #string name + local function SceneryScan (coordinate, radius, name) + if coordinate ~= nil then + local scenerylist = coordinate:ScanScenery(radius) + local rscenery = nil + for _,_scenery in pairs(scenerylist) do + local scenery = _scenery -- Wrapper.Scenery#SCENERY + if tostring(scenery.SceneryName) == tostring(name) then + rscenery = scenery + break + end end + return rscenery end return nil end - if SCENERY.SceneryObject then - SCENERY.SceneryObject.SceneryObject.id_ = name - SCENERY.SceneryObject.SceneryName = name - return SCENERY:Register(SCENERY.SceneryObject.SceneryObject.id_, SCENERY.SceneryObject.SceneryObject) - else - return sceneryScan(findAirbase()) + if Coordinate then + scenery = SceneryScan(Coordinate, radius, name) end + + return scenery +end + +--- Find a SCENERY object from it's name/id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first +-- to find the correct object. +--@param #SCENERY self +--@param #string Name The name/id of the scenery object as taken from the ME. Ex. '595785449' +--@param Core.Zone#ZONE Zone Where to find the scenery object. Can be handed as zone name. +--@param #number Radius (optional) Search radius around coordinate, defaults to 100 +--@return #SCENERY Scenery Object or `nil` if it cannot be found +function SCENERY:FindByNameInZone(Name, Zone, Radius) + local radius = Radius or 100 + local name = Name or "unknown" + if type(Zone) == "string" then + Zone = ZONE:FindByName(Zone) + end + local coordinate = Zone:GetCoordinate() + return self:FindByName(Name,coordinate,Radius) end From 1bda9d40e1ea07522383c43b3a888f1a2edecd25 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 13 Oct 2022 12:53:13 +0200 Subject: [PATCH 067/603] #SCENERY * Improvements --- Moose Development/Moose/Wrapper/Scenery.lua | 38 +++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index cb00a6057..c10f8e0d8 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -17,6 +17,7 @@ -- @field #string ClassName -- @field #string SceneryName -- @field #DCS.Object SceneryObject +-- @field #number Life0 -- @extends Wrapper.Positionable#POSITIONABLE @@ -43,6 +44,11 @@ function SCENERY:Register( SceneryName, SceneryObject ) local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) self.SceneryName = SceneryName self.SceneryObject = SceneryObject + if self.SceneryObject then + self.Life0 = self.SceneryObject:getLife() + else + self.Life0 = 0 + end return self end @@ -53,6 +59,38 @@ function SCENERY:GetDCSObject() return self.SceneryObject end +--- Get current life points from the SCENERY Object. +--@param #SCENERY self +--@return #number life +function SCENERY:GetLife() + local life = 0 + if self.SceneryObject then + life = self.SceneryObject:getLife() + end + return life +end + +--- Get current initial life points from the SCENERY Object. +--@param #SCENERY self +--@return #number life +function SCENERY:GetLife0() + return self.Life0 or 0 +end + +--- Check if SCENERY Object is alive. +--@param #SCENERY self +--@return #number life +function SCENERY:IsAlive() + return self:GetLife() >= 1 and true or false +end + +--- Check if SCENERY Object is dead. +--@param #SCENERY self +--@return #number life +function SCENERY:IsDead() + return self:GetLife() < 1 and true or false +end + --- Get the threat level of a SCENERY object. Always 0. --@param #SCENERY self --@return #number Threat level 0. From 220e530bf3cce86ce4eb2cee5e7f60d0d7276b63 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 13 Oct 2022 17:42:02 +0200 Subject: [PATCH 068/603] #PLAYERTASK - CSAR Integration --- Moose Development/Moose/Ops/CSAR.lua | 59 ++++++++++++++- Moose Development/Moose/Ops/PlayerRecce.lua | 1 + Moose Development/Moose/Ops/PlayerTask.lua | 83 ++++++++++++++++++--- 3 files changed, 130 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 2638ea525..477f1fb75 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -270,7 +270,7 @@ CSAR.AircraftType["Bronco-OV-10A"] = 2 --- CSAR class version. -- @field #string version -CSAR.version="1.0.11" +CSAR.version="1.0.13" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -613,6 +613,19 @@ function CSAR:_DoubleEjection(_unitname) return false end +--- (User) Add a PLAYERTASK - FSM events will check success +-- @param #CSAR self +-- @param Ops.PlayerTask#PLAYERTASK PlayerTask +-- @return #CSAR self +function CSAR:AddPlayerTask(PlayerTask) + self:T(self.lid .. " AddPlayerTask") + if not self.PlayerTaskQueue then + self.PlayerTaskQueue = FIFO:New() + end + self.PlayerTaskQueue:Push(PlayerTask,PlayerTask.PlayerTaskNr) + return self +end + --- (Internal) Spawn a downed pilot -- @param #CSAR self -- @param #number country Country for template. @@ -1447,7 +1460,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG end if _heliUnit:InAir() and _unitsInHelicopter + 1 <= _maxUnits then - -- TODO - make variable + -- DONE - make variable if _distance < self.rescuehoverdistance then --check height! @@ -1455,7 +1468,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG if leaderheight < 0 then leaderheight = 0 end local _height = _heliUnit:GetHeight() - leaderheight - -- TODO - make variable + -- DONE - make variable if _height <= self.rescuehoverheight then local _time = self.hoverStatus[_lookupKeyHeli] @@ -2282,6 +2295,29 @@ end function CSAR:onbeforeBoarded(From, Event, To, Heliname, Woundedgroupname) self:T({From, Event, To, Heliname, Woundedgroupname}) self:_ScheduledSARFlight(Heliname,Woundedgroupname) + local Unit = UNIT:FindByName(Heliname) + if Unit and Unit:IsPlayer() and self.PlayerTaskQueue then + local playername = Unit:GetPlayerName() + local dropcoord = Unit:GetCoordinate() or COORDINATE:New(0,0,0) + local dropvec2 = dropcoord:GetVec2() + self.PlayerTaskQueue:ForEach( + function (Task) + local task = Task -- Ops.PlayerTask#PLAYERTASK + local subtype = task:GetSubType() + -- right subtype? + if Event == subtype and not task:IsDone() then + local targetzone = task.Target:GetObject() -- Core.Zone#ZONE should be a zone in this case .... + if (targetzone and targetzone.ClassName and string.match(targetzone.ClassName,"ZONE") and targetzone:IsVec2InZone(dropvec2)) + or (string.find(task.CSARPilotName,Woundedgroupname)) then + if task.Clients:HasUniqueID(playername) then + -- success + task:__Success(-1) + end + end + end + end + ) + end return self end @@ -2311,6 +2347,23 @@ function CSAR:onbeforeRescued(From, Event, To, HeliUnit, HeliName, PilotsSaved) self:T({From, Event, To, HeliName, HeliUnit}) self.rescues = self.rescues + 1 self.rescuedpilots = self.rescuedpilots + PilotsSaved + local Unit = HeliUnit or UNIT:FindByName(HeliName) + if Unit and Unit:IsPlayer() and self.PlayerTaskQueue then + local playername = Unit:GetPlayerName() + self.PlayerTaskQueue:ForEach( + function (Task) + local task = Task -- Ops.PlayerTask#PLAYERTASK + local subtype = task:GetSubType() + -- right subtype? + if Event == subtype and not task:IsDone() then + if task.Clients:HasUniqueID(playername) then + -- success + task:__Success(-1) + end + end + end + ) + end return self end diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index b11826f6a..caf79826e 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1281,6 +1281,7 @@ function PLAYERRECCE:_GetTextForSpeech(text) text=string.gsub(text,"%s*$","") text=string.gsub(text,"0","zero") text=string.gsub(text,"9","niner") + text=string.gsub(text," "," ") return text end diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 2bbc4f1e7..112305d1f 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -54,6 +54,9 @@ do -- @field #string Freetext -- @field #string FreetextTTS -- @field #string TaskSubType +-- @field #table NextTaskSuccess +-- @field #table NextTaskFailure +-- @field #string FinalState -- @extends Core.Fsm#FSM @@ -85,11 +88,14 @@ PLAYERTASK = { Freetext = nil, FreetextTTS = nil, TaskSubType = nil, + NextTaskSuccess = {}, + NextTaskFailure = {}, + FinalState = "none", } --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.6" +PLAYERTASK.version="0.1.8" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -311,12 +317,12 @@ end -- @return #string Text function PLAYERTASK:GetFreetext() self:T(self.lid.."GetFreetext") - return self.Freetext + return self.Freetext or self.FreetextTTS or "No Details" end --- [USER] Add a free text description for TTS to this task. -- @param #PLAYERTASK self --- @param #string Text +-- @param #string TextTTS -- @return #PLAYERTASK self function PLAYERTASK:AddFreetextTTS(TextTTS) self:T(self.lid.."AddFreetextTTS") @@ -329,7 +335,7 @@ end -- @return #string Text function PLAYERTASK:GetFreetextTTS() self:T(self.lid.."GetFreetextTTS") - return self.FreetextTTS + return self.FreetextTTS or self.Freetext or "No Details" end --- [USER] Add a short free text description for the menu entry of this task. @@ -342,6 +348,26 @@ function PLAYERTASK:SetMenuName(Text) return self end +--- [USER] Add a task to be assigned to same clients when task was a success. +-- @param #PLAYERTASK self +-- @param Ops.PlayerTask#PLAYERTASK Task +-- @return #PLAYERTASK self +function PLAYERTASK:AddNextTaskAfterSuccess(Task) + self:T(self.lid.."AddNextTaskAfterSuccess") + table.insert(self.NextTaskSuccess,Task) + return self +end + +--- [USER] Add a task to be assigned to same clients when task was a failure. +-- @param #PLAYERTASK self +-- @param Ops.PlayerTask#PLAYERTASK Task +-- @return #PLAYERTASK self +function PLAYERTASK:AddNextTaskAfterFailure(Task) + self:T(self.lid.."AddNextTaskAfterFailure") + table.insert(self.NextTaskFailure,Task) + return self +end + --- [User] Check if task is done -- @param #PLAYERTASK self -- @return #boolean done @@ -759,6 +785,7 @@ function PLAYERTASK:onafterCancel(From, Event, To) self.TaskController:__TaskCancelled(-1,self) end self.timestamp = timer.getAbsTime() + self.FinalState = "Cancel" self:__Done(-1) return self end @@ -778,6 +805,7 @@ function PLAYERTASK:onafterSuccess(From, Event, To) self.TargetMarker:Remove() end self.timestamp = timer.getAbsTime() + self.FinalState = "Success" self:__Done(-1) return self end @@ -802,9 +830,7 @@ function PLAYERTASK:onafterFailed(From, Event, To) if self.TargetMarker then self.TargetMarker:Remove() end - if self.TaskController then - self.TaskController:__TaskFailed(-1,self) - end + self.FinalState = "Failed" self:__Done(-1) end self.timestamp = timer.getAbsTime() @@ -824,6 +850,8 @@ do -- DONE Flash directions -- DONE less rebuilds menu, Task info menu available after join -- DONE Limit menu entries +-- DONE Integrated basic CTLD tasks +-- DONE Integrate basic CSAR tasks ------------------------------------------------------------------------------------------------------------------- --- PLAYERTASKCONTROLLER class. @@ -1343,7 +1371,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.40" +PLAYERTASKCONTROLLER.version="0.1.41" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1580,6 +1608,7 @@ function PLAYERTASKCONTROLLER:_GetTextForSpeech(text) -- get rid of leading or trailing spaces text=string.gsub(text,"^%s*","") text=string.gsub(text,"%s*$","") + text=string.gsub(text," "," ") return text end @@ -2013,6 +2042,23 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue() self:T("*****Removing player " .. _id) self.TasksPerPlayer:PullByID(_id) end + -- Follow-up tasks? + local nexttasks = {} + if task.FinalState == "Success" then + nexttasks = task.NextTaskSuccess + elseif task.FinalState == "Failed" then + nexttasks = task.NextTaskFailure + end + local clientlist, count = task:GetClientObjects() + if count > 0 then + for _,_client in pairs(clientlist) do + local client = _client --Wrapper.Client#CLIENT + local group = client:GetGroup() + for _,task in pairs(nexttasks) do + self:_JoinTask(group,client,task,true) + end + end + end local TNow = timer.getAbsTime() if TNow - task.timestamp > 10 then local task = self.TaskQueue:PullByID(_id) -- Ops.PlayerTask#PLAYERTASK @@ -2499,11 +2545,12 @@ end -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Client#CLIENT Client -- @param Ops.PlayerTask#PLAYERTASK Task +-- @param #boolean Force Assign task even if client already has one -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task) +function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force) self:T(self.lid.."_JoinTask") local playername, ttsplayername = self:_GetPlayerName(Client) - if self.TasksPerPlayer:HasUniqueID(playername) then + if self.TasksPerPlayer:HasUniqueID(playername) and not Force then -- Player already has a task if not self.NoScreenOutput then local text = self.gettext:GetEntry("HAVEACTIVETASK",self.locale) @@ -2601,6 +2648,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) self:T(self.lid.."_ActiveTaskInfo") local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" + local textTTS = "" if self.TasksPerPlayer:HasUniqueID(playername) or Task then -- TODO: Show multiple? local task = Task or self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK @@ -2670,6 +2718,15 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) end end end + elseif task.Type == AUFTRAG.Type.CTLD or task.Type == AUFTRAG.Type.CSAR then + -- THREATTEXT = "%s\nThreat: %s\nTargets left: %d\nCoord: %s", + -- THREATTEXTTTS = "%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.", + text = taskname + textTTS = taskname + local detail = task:GetFreetext() + local detailTTS = task:GetFreetextTTS() + text = text .. "\nDetail: "..detail.."\nTarget location "..CoordText + textTTS = textTTS .. "; Detail: "..detailTTS.."\nTarget location "..CoordText end local clienttxt = self.gettext:GetEntry("PILOTS",self.locale) if clientcount > 0 then @@ -2687,6 +2744,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) clienttxt = clienttxt .. keine end text = text .. clienttxt + textTTS = textTTS .. clienttxt if self.UseSRS then if string.find(CoordText," BR, ") then CoordText = string.gsub(CoordText," BR, "," Bee, Arr, ") @@ -2699,6 +2757,11 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) local lasingtext = self.gettext:GetEntry("POINTERTARGETLASINGTTS",self.locale) ttstext = ttstext .. lasingtext end + elseif task.Type == AUFTRAG.Type.CTLD or task.Type == AUFTRAG.Type.CSAR then + ttstext = textTTS + if string.find(ttstext," BR, ") then + CoordText = string.gsub(ttstext," BR, "," Bee, Arr, ") + end end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,2) end From 8279ac79c8bdfe2d5c30945efc6e271ac7e2332c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 14 Oct 2022 16:16:50 +0200 Subject: [PATCH 069/603] #PLAYERRECCE * Added switchable smoking of own position * Changed smoking - now laser target single, other targets as combined coordinate * Added battle field illumination * Added support for the KA-50. Note - camera is seen as "always on". For lasing, target needs to be head-on 12 o'clock --- Moose Development/Moose/Core/Set.lua | 6 +- Moose Development/Moose/Ops/PlayerRecce.lua | 215 +++++++++++++++++--- 2 files changed, 186 insertions(+), 35 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index e827cfa27..3c40434c0 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2639,8 +2639,10 @@ do -- SET_UNIT -- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units. function SET_UNIT:GetCoordinate() - local Coordinate = self:GetFirst():GetCoordinate() - + local Coordinate = self:GetRandom():GetCoordinate() + --self:F({Coordinate:GetVec3()}) + + local x1 = Coordinate.x local x2 = Coordinate.x local y1 = Coordinate.y diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index caf79826e..f872dd255 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -29,7 +29,7 @@ -- PLAYERRECCE -- TODO: PLAYERRECCE -- DONE: No messages when no targets to flare or smoke --- TODO: Flare smoke group, not all targets +-- DONE: Flare not all targets -- DONE: Messages to Attack Group, use client settings -- DONE: Lasing dist 8km -- DONE: Reference Point RP @@ -75,6 +75,8 @@ -- @field Wrapper.Marker#MARKER RPMarker -- @field #number TForget -- @field Utilities.FiFo#FIFO TargetCache +-- @field #boolean smokeownposition +-- @field #table SmokeOwn -- @extends Core.Fsm#FSM --- @@ -97,14 +99,14 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.12", + version = "0.0.15", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, LaserFOV = {}, LaserTarget = {}, PlayerSet = nil, - debug = true, + debug = false, LaserSpots = {}, UnitLaserCodes = {}, LaserCodes = {}, @@ -123,6 +125,8 @@ PLAYERRECCE = { ReferencePoint = nil, TForget = 600, TargetCache = nil, + smokeownposition = true, + SmokeOwn = {}, } --- @@ -140,6 +144,7 @@ PLAYERRECCE.LaserRelativePos = { ["SA342Mistral"] = { x = 1.7, y = 1.2, z = 0 }, ["SA342Minigun"] = { x = 1.7, y = 1.2, z = 0 }, ["SA342L"] = { x = 1.7, y = 1.2, z = 0 }, + ["Ka-50"] = { x = 6.1, y = -0.85 , z = 0 } } --- @@ -150,6 +155,7 @@ PLAYERRECCE.MaxViewDistance = { ["SA342Mistral"] = 8000, ["SA342Minigun"] = 8000, ["SA342L"] = 8000, + ["Ka-50"] = 8000, } --- @@ -160,6 +166,7 @@ PLAYERRECCE.Cameraheight = { ["SA342Mistral"] = 2.85, ["SA342Minigun"] = 2.85, ["SA342L"] = 2.85, + ["Ka-50"] = 0.5, } --- @@ -170,6 +177,7 @@ PLAYERRECCE.CanLase = { ["SA342Mistral"] = true, ["SA342Minigun"] = false, -- no optics ["SA342L"] = true, + ["Ka-50"] = true, } --- @@ -230,6 +238,7 @@ function PLAYERRECCE:New(Name, Coalition, PlayerSet) self:AddTransition("*", "TargetDetected", "*") self:AddTransition("*", "TargetsSmoked", "*") self:AddTransition("*", "TargetsFlared", "*") + self:AddTransition("*", "Illumination", "*") self:AddTransition("*", "TargetLasing", "*") self:AddTransition("*", "TargetLOSLost", "*") self:AddTransition("*", "TargetReport", "*") @@ -387,10 +396,15 @@ function PLAYERRECCE:_CameraOn(client,playername) local camera = true local unit = client -- Wrapper.Unit#UNIT if unit and unit:IsAlive() then - local dcsunit = Unit.getByName(client:GetName()) - local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg - if vivihorizontal < -0.7 or vivihorizontal > 0.7 then - camera = false + local typename = unit:GetTypeName() + if string.find(typename,"SA342") then + local dcsunit = Unit.getByName(client:GetName()) + local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg + if vivihorizontal < -0.7 or vivihorizontal > 0.7 then + camera = false + end + elseif typename == "Ka-50" then + camera = true end end return camera @@ -537,7 +551,7 @@ function PLAYERRECCE:_GetViewZone(unit, vheading, minview, maxview, angle, camon local startp = unit:GetCoordinate() local heading1 = (vheading+90)%360 local heading2 = (vheading-90)%360 - self:I({heading1,heading2}) + self:T({heading1,heading2}) local startpos = startp:Translate(minview,vheading) local pos1 = startpos:Translate(10,heading1) local pos2 = startpos:Translate(10,heading2) @@ -612,7 +626,7 @@ end --@param #PLAYERRECCE self --@param Wrapper.Unit#UNIT unit The FACA unit --@param #boolean camera If true, use the unit's camera for targets in sight ---@param #laser Use laser zone +--@param #laser laser Use laser zone --@return Core.Set#SET_UNIT Set of targets, can be empty! --@return #number count Count of targets function PLAYERRECCE:_GetTargetSet(unit,camera,laser) @@ -631,6 +645,11 @@ function PLAYERRECCE:_GetTargetSet(unit,camera,laser) angle=10 -- Model nod and actual TV view don't compute maxview = self.MaxViewDistance[typename] or 5000 + elseif typename == "Ka-50" and camera then + heading = unit:GetHeading() + nod,maxview,camon = 10,1000,true + angle = 10 + maxview = self.MaxViewDistance[typename] or 5000 else -- visual heading = unit:GetHeading() @@ -741,7 +760,7 @@ function PLAYERRECCE:_LaseTarget(client,targetset) -- still looking at target? local target=self.LaserTarget[playername] -- Ops.Target#TARGET local oldtarget = target:GetObject() --or laser.Target - self:I("Targetstate: "..target:GetState()) + self:T("Targetstate: "..target:GetState()) if not oldtarget or targetset:IsNotInSet(oldtarget) or target:IsDead() or target:IsDestroyed() then -- lost LOS or dead laser:LaseOff() @@ -805,6 +824,28 @@ function PLAYERRECCE:_SwitchOnStation(client,group,playername) return self end +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_SwitchSmoke(client,group,playername) + self:T(self.lid.."_SwitchLasing") + if not self.SmokeOwn[playername] then + self.SmokeOwn[playername] = true + MESSAGE:New("Smoke self is now ON",10,self.Name or "FACA"):ToClient(client) + else + self.SmokeOwn[playername] = false + MESSAGE:New("Smoke self is now OFF",10,self.Name or "FACA"):ToClient(client) + end + if self.ClientMenus[playername] then + self.ClientMenus[playername]:Remove() + self.ClientMenus[playername]=nil + end + return self +end + --- [Internal] -- @param #PLAYERRECCE self -- @param Wrapper.Client#CLIENT client @@ -863,7 +904,7 @@ end -- @param #string playername -- @return #PLAYERRECCE self function PLAYERRECCE:_WIP(client,group,playername) - self:I(self.lid.."_WIP") + self:T(self.lid.."_WIP") return self end @@ -878,14 +919,17 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT cameraset:AddSet(visualset) + if cameraset:CountAlive() > 0 then self:__TargetsSmoked(-1,client,playername,cameraset) end + local highsmoke = self.SmokeColor.highsmoke local medsmoke = self.SmokeColor.medsmoke local lowsmoke = self.SmokeColor.lowsmoke local lasersmoke = self.SmokeColor.lasersmoke local laser = self.LaserSpots[playername] -- Core.Spot#SPOT + -- laser targer gets extra smoke if laser and laser.Target and laser.Target:IsAlive() then laser.Target:GetCoordinate():Smoke(lasersmoke) @@ -893,23 +937,27 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) cameraset:Remove(laser.Target:GetName(),true) end end + -- smoke everything else - for _,_unit in pairs(cameraset.Set) do - local unit = _unit --Wrapper.Unit#UNIT - if unit then - local coord = unit:GetCoordinate() - local threat = unit:GetThreatLevel() - if coord then - local color = lowsmoke - if threat > 7 then - color = medsmoke - elseif threat > 2 then - color = lowsmoke - end - coord:Smoke(color) - end + local coordinate = cameraset:GetCoordinate() + local setthreat = cameraset:CalculateThreatLevelA2G() + + if coordinate then + local color = lowsmoke + if setthreat > 7 then + color = medsmoke + elseif setthreat > 2 then + color = lowsmoke end + coordinate:Smoke(color) end + + if self.SmokeOwn[playername] then + local cc = client:GetCoordinate() + local color = self.SmokeColor.ownsmoke + cc:Smoke(color) + end + return self end @@ -959,6 +1007,24 @@ function PLAYERRECCE:_FlareTargets(client,group,playername) return self end +--- [Internal] +-- @param #PLAYERRECCE self +-- @param Wrapper.Client#CLIENT client +-- @param Wrapper.Group#GROUP group +-- @param #string playername +-- @return #PLAYERRECCE self +function PLAYERRECCE:_IlluTargets(client,group,playername) + self:T(self.lid.."_IlluTargets") + local totalset, count = self:_GetKnownTargets(client) -- Core.Set#SET_UNIT + if count > 0 then + local coord = totalset:GetCoordinate() -- Core.Point#COORDINATE + coord.y = coord.y + 200 + coord:IlluminationBomb(nil,1) + self:__Illumination(1,client,playername,totalset) + end + return self +end + --- [Internal] -- @param #PLAYERRECCE self -- @param Wrapper.Client#CLIENT client @@ -1080,6 +1146,9 @@ function PLAYERRECCE:_BuildMenus() if not self.UnitLaserCodes[playername] then self:_SetClientLaserCode(nil,nil,playername,1688) end + if not self.SmokeOwn[playername] then + self.SmokeOwn[playername] = self.smokeownposition + end local group = client:GetGroup() if not self.ClientMenus[playername] then local canlase = self.CanLase[client:GetTypeName()] @@ -1088,8 +1157,13 @@ function PLAYERRECCE:_BuildMenus() local text = string.format("Switch On-Station (%s)",txtonstation) local onstationmenu = MENU_GROUP_COMMAND:New(group,text,self.ClientMenus[playername],self._SwitchOnStation,self,client,group,playername) if self.OnStation[playername] then - local smokemenu = MENU_GROUP_COMMAND:New(group,"Smoke Targets",self.ClientMenus[playername],self._SmokeTargets,self,client,group,playername) - local smokemenu = MENU_GROUP_COMMAND:New(group,"Flare Targets",self.ClientMenus[playername],self._FlareTargets,self,client,group,playername) + local smoketopmenu = MENU_GROUP:New(group,"Visual Markers",self.ClientMenus[playername]) + local smokemenu = MENU_GROUP_COMMAND:New(group,"Smoke Targets",smoketopmenu,self._SmokeTargets,self,client,group,playername) + local flaremenu = MENU_GROUP_COMMAND:New(group,"Flare Targets",smoketopmenu,self._FlareTargets,self,client,group,playername) + local illumenu = MENU_GROUP_COMMAND:New(group,"Illuminate Area",smoketopmenu,self._IlluTargets,self,client,group,playername) + local ownsm = self.SmokeOwn[playername] and "ON" or "OFF" + local owntxt = string.format("Switch smoke self (%s)",ownsm) + local ownsmoke = MENU_GROUP_COMMAND:New(group,owntxt,smoketopmenu,self._SwitchSmoke,self,client,group,playername) if canlase then local txtonstation = self.AutoLase[playername] and "ON" or "OFF" local text = string.format("Switch Lasing (%s)",txtonstation) @@ -1195,7 +1269,7 @@ function PLAYERRECCE:_CheckNewTargets(targetset,client,playername) table.insert(targetsbyclock[clock],obj) end ) - self:I("Known target Count: "..self.TargetCache:Count()) + self:T("Known target Count: "..self.TargetCache:Count()) if tempset:CountAlive() > 0 then self:TargetDetected(targetsbyclock,client,playername) end @@ -1267,6 +1341,24 @@ function PLAYERRECCE:SetMenuName(Name) return self end +--- [User] Enable smoking of own position +-- @param #PLAYERRECCE self +-- @return #PLAYERRECCE self +function PLAYERRECCE:EnableSmokeOwnPosition() + self:T(self.lid.."ENableSmokeOwnPosition") + self.smokeownposition = true + return self +end + +--- [User] Disable smoking of own position +-- @param #PLAYERRECCE self +-- @return #PLAYERRECCE +function PLAYERRECCE:DisableSmokeOwnPosition() + self:T(self.lid.."DisableSmokeOwnPosition") + self.smokeownposition = false + return self +end + --- [Internal] Get text for text-to-speech. -- Numbers are spaced out, e.g. "Heading 180" becomes "Heading 1 8 0 ". -- @param #PLAYERRECCE self @@ -1351,7 +1443,7 @@ function PLAYERRECCE:onafterStatus(From, Event, To) self.ViewZoneLaser[playername]=lzone:DrawZone(self.Coalition,{0,1,0},nil,nil,nil,1) end end - self:I({lasercount=targetcount}) + self:T({lasercount=targetcount}) end -- visual targets vistargetset, vistargetcount, viszone = self:_GetTargetSet(client,false) @@ -1489,9 +1581,18 @@ function PLAYERRECCE:onafterTargetDetected(From, Event, To, Targetsbyclock, Clie end if Settings:IsMetric() then targetdistance = UTILS.Round(targetdistance,-2) + if targetdistance >= 1000 then + targetdistance = UTILS.Round(targetdistance/1000,0) + dunits = "kilometer" + end else - targetdistance = UTILS.Round(UTILS.MetersToFeet(targetdistance),-2) - dunits = "feet" + if UTILS.MetersToNM(targetdistance) >=1 then + targetdistance = UTILS.Round(UTILS.MetersToNM(targetdistance),0) + dunits = "miles" + else + targetdistance = UTILS.Round(UTILS.MetersToFeet(targetdistance),-2) + dunits = "feet" + end end local text = string.format("Target! %s! %s o\'clock, %d %s!", ThreatTxt,i, targetdistance, dunits) local ttstext = string.format("Target! %s! %s oh clock, %d %s!", ThreatTxt, i, targetdistance, dunits) @@ -1516,9 +1617,18 @@ function PLAYERRECCE:onafterTargetDetected(From, Event, To, Targetsbyclock, Clie local targetdistance = GetNearest(targets) if Settings:IsMetric() then targetdistance = UTILS.Round(targetdistance,-2) + if targetdistance >= 1000 then + targetdistance = UTILS.Round(targetdistance/1000,0) + dunits = "kilometer" + end else - targetdistance = UTILS.Round(UTILS.MetersToFeet(targetdistance),-2) - dunits = "feet" + if UTILS.MetersToNM(targetdistance) >=1 then + targetdistance = UTILS.Round(UTILS.MetersToNM(targetdistance),0) + dunits = "miles" + else + targetdistance = UTILS.Round(UTILS.MetersToFeet(targetdistance),-2) + dunits = "feet" + end end local text = string.format(" %d targets! %s o\'clock, %d %s!", targetno, i, targetdistance, dunits) local ttstext = string.format("%d targets! %s oh clock, %d %s!", targetno, i, targetdistance, dunits) @@ -1533,6 +1643,45 @@ function PLAYERRECCE:onafterTargetDetected(From, Event, To, Targetsbyclock, Clie return self end +--- [Internal] Targets Illuminated +-- @param #PLAYERRECCE self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Client#CLIENT Client +-- @param #string Playername +-- @param Core.Set#SET_UNIT TargetSet +-- @return #PLAYERRECCE self +function PLAYERRECCE:onafterIllumination(From, Event, To, Client, Playername, TargetSet) + self:T({From, Event, To}) + local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) + local coord = Client:GetCoordinate() + local coordtext = coord:ToStringBULLS(self.Coalition) + if self.AttackSet then + for _,_client in pairs(self.AttackSet.Set) do + local client = _client --Wrapper.Client#CLIENT + if client and client:IsAlive() then + local Settings = client and _DATABASE:GetPlayerSettings(client:GetPlayerName()) or _SETTINGS + local coordtext = coord:ToStringA2G(client,Settings) + if self.ReferencePoint then + coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) + end + local text = string.format("All stations, %s fired illumination\nat %s!",callsign, coordtext) + MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) + end + end + end + local text = "Sunshine!" + local ttstext = "Sunshine!" + if self.UseSRS then + local grp = Client:GetGroup() + self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) + else + MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) + end + return self +end + --- [Internal] Targets Smoked -- @param #PLAYERRECCE self -- @param #string From From 60a1ceb7a0c2a7e6363b4ba26a37e08bee7422cf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 14 Oct 2022 16:50:54 +0200 Subject: [PATCH 070/603] #Docu changes --- Moose Development/Moose/Ops/CTLD.lua | 16 ++++++++++++++-- Moose Development/Moose/Ops/PlayerRecce.lua | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 3b5920842..b8a26447a 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -827,6 +827,8 @@ do -- -- To award player with points, using the SCORING Class (SCORING: my_Scoring, CTLD: CTLD_Cargotransport) -- +-- my_scoring = SCORING:New("Combat Transport") +-- -- function CTLD_Cargotransport:OnAfterCratesDropped(From, Event, To, Group, Unit, Cargotable) -- local points = 10 -- if Unit then @@ -904,7 +906,7 @@ do -- -- my_ctld.useprefix = true -- this is true by default and MUST BE ON. -- --- ### 5.2 Integrate Hercules ground crew (F8 Menu) loadable objects (alternative method) +-- ### 5.2 Integrate Hercules ground crew (F8 Menu) loadable objects (alternative method, use either the above OR this method, NOT both!) -- -- Integrate to your CTLD instance like so, where `my_ctld` is a previously created CTLD instance: -- @@ -931,6 +933,8 @@ do -- The script works on the EVENTS.Shot trigger, which is used by the mod when you **drop cargo from the Hercules while flying**. Unloading on the ground does -- not achieve anything here. If you just want to unload on the ground, use the normal Moose CTLD (see 5.1). -- +-- DO NOT use the "splash damage" script together with this method! Your cargo will explode on the ground! +-- -- There are two ways of airdropping: -- -- 1) Very low and very slow (>5m and <10m AGL) - here you can drop stuff which has "Skid" at the end of the cargo name (loaded via F8 Ground Crew menu) @@ -4992,13 +4996,21 @@ CTLD_HERCULES.Types = { -- -- Expected template names are the ones in the rounded brackets. -- --- HINTS +-- ### HINTS -- -- The script works on the EVENTS.Shot trigger, which is used by the mod when you **drop cargo from the Hercules while flying**. Unloading on the ground does -- not achieve anything here. If you just want to unload on the ground, use the normal Moose CTLD. +-- **Do not use** the **splash damage** script together with this, your cargo will just explode when reaching the ground! +-- +-- ### Airdrops +-- -- There are two ways of airdropping: -- 1) Very low and very slow (>5m and <10m AGL) - here you can drop stuff which has "Skid" at the end of the cargo name (loaded via F8 Ground Crew menu) -- 2) Higher up and slow (>100m AGL) - here you can drop paratroopers and cargo which has "Air" at the end of the cargo name (loaded via F8 Ground Crew menu) +-- +-- ### General +-- +-- Use either this method to integrate the Hercules **or** the one from the "normal" CTLD. Never both! function CTLD_HERCULES:New(Coalition, Alias, CtldObject) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #CTLD_HERCULES diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index f872dd255..e3be80a18 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -18,7 +18,7 @@ -- -- ### Authors: -- --- * Applevengelist (Design & Programming) +-- * Applevangelist (Design & Programming) -- -- === -- @@ -29,7 +29,7 @@ -- PLAYERRECCE -- TODO: PLAYERRECCE -- DONE: No messages when no targets to flare or smoke --- DONE: Flare not all targets +-- DONE: Smoke not all targets -- DONE: Messages to Attack Group, use client settings -- DONE: Lasing dist 8km -- DONE: Reference Point RP From 57f34f934c8d216b9d92017f431927b4f23f3acd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 16 Oct 2022 12:05:38 +0200 Subject: [PATCH 071/603] short if --- Moose Development/Moose/Ops/PlayerRecce.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index e3be80a18..1278a0a06 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -290,7 +290,7 @@ function PLAYERRECCE:_EventHandler(EventData) if self.ViewZoneVisual[EventData.IniPlayerName] then self.ViewZoneVisual[EventData.IniPlayerName]:UndrawZone() end end elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then - if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then + if EventData.IniPlayerName then self:T(self.lid.."Event for player: "..EventData.IniPlayerName) self.UnitLaserCodes[EventData.IniPlayerName] = 1688 self.ClientMenus[EventData.IniPlayerName] = nil From b40b4ec500f49101b82ef9778a8a76e49305da54 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 16 Oct 2022 13:48:23 +0200 Subject: [PATCH 072/603] #PLAYERTASKCONTROLLER * Show Freetext as Briefing in all tasks when available * Use average coordinates in TARGET --- Moose Development/Moose/Ops/PlayerTask.lua | 48 ++++++++++++++++----- Moose Development/Moose/Ops/Target.lua | 31 ++++++++++++-- Moose Development/Moose/Wrapper/Group.lua | 50 +++++++++++++++++++++- 3 files changed, 114 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 112305d1f..84c48165f 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -95,7 +95,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.8" +PLAYERTASK.version="0.1.9" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -294,6 +294,22 @@ function PLAYERTASK:AddFreetext(Text) return self end +--- [USER] Query if a task has free text description. +-- @param #PLAYERTASK self +-- @return #PLAYERTASK self +function PLAYERTASK:HasFreetext() + self:T(self.lid.."HasFreetext") + return self.Freetext ~= nil and true or false +end + +--- [USER] Query if a task has free text TTS description. +-- @param #PLAYERTASK self +-- @return #PLAYERTASK self +function PLAYERTASK:HasFreetextTTS() + self:T(self.lid.."HasFreetextTTS") + return self.FreetextTTS ~= nil and true or false +end + --- [USER] Set a task sub type description to this task. -- @param #PLAYERTASK self -- @param #string Type @@ -514,7 +530,7 @@ function PLAYERTASK:SmokeTarget(Color) if not self.lastsmoketime then self.lastsmoketime = 0 end local TDiff = timer.getAbsTime() - self.lastsmoketime if self.Target and TDiff > 299 then - local coordinate = self.Target:GetCoordinate() + local coordinate = self.Target:GetAverageCoordinate() if coordinate then coordinate:Smoke(color) self.lastsmoketime = timer.getAbsTime() @@ -531,7 +547,7 @@ function PLAYERTASK:FlareTarget(Color) self:T(self.lid.."SmokeTarget") local color = Color or FLARECOLOR.Red if self.Target then - local coordinate = self.Target:GetCoordinate() + local coordinate = self.Target:GetAverageCoordinate() if coordinate then coordinate:Flare(color,0) end @@ -549,7 +565,7 @@ function PLAYERTASK:IlluminateTarget(Power,Height) local Power = Power or 1000 local Height = Height or 150 if self.Target then - local coordinate = self.Target:GetCoordinate() + local coordinate = self.Target:GetAverageCoordinate() if coordinate then local bcoord = COORDINATE:NewFromVec2( coordinate:GetVec2(), Height ) bcoord:IlluminationBomb(Power) @@ -1371,7 +1387,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.41" +PLAYERTASKCONTROLLER.version="0.1.42" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -2650,7 +2666,8 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) local text = "" local textTTS = "" if self.TasksPerPlayer:HasUniqueID(playername) or Task then - -- TODO: Show multiple? + -- NODO: Show multiple? + -- Details local task = Task or self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK local tname = self.gettext:GetEntry("TASKNAME",self.locale) local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale) @@ -2663,6 +2680,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) else CoordText = Coordinate:ToStringA2A(Client) end + -- Threat Level local ThreatLevel = task.Target:GetThreatLevelMax() --local ThreatLevelText = "high" local ThreatLevelText = self.gettext:GetEntry("THREATHIGH",self.locale) @@ -2673,11 +2691,13 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) --ThreatLevelText = "low" ThreatLevelText = self.gettext:GetEntry("THREATLOW",self.locale) end + -- Targetno and Threat local targets = task.Target:CountTargets() or 0 local clientlist, clientcount = task:GetClients() local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel local ThreatLocaleText = self.gettext:GetEntry("THREATTEXT",self.locale) text = string.format(ThreatLocaleText, taskname, ThreatGraph, targets, CoordText) + -- Prec bombing if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then if self.LasingDrone and self.LasingDrone.playertask then local yes = self.gettext:GetEntry("YES",self.locale) @@ -2689,6 +2709,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) text = text .. prectext end end + -- Buddylasing if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.buddylasing then if self.PlayerRecce then local yes = self.gettext:GetEntry("YES",self.locale) @@ -2718,6 +2739,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) end end end + -- Transport elseif task.Type == AUFTRAG.Type.CTLD or task.Type == AUFTRAG.Type.CSAR then -- THREATTEXT = "%s\nThreat: %s\nTargets left: %d\nCoord: %s", -- THREATTEXTTTS = "%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.", @@ -2725,15 +2747,15 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) textTTS = taskname local detail = task:GetFreetext() local detailTTS = task:GetFreetextTTS() - text = text .. "\nDetail: "..detail.."\nTarget location "..CoordText - textTTS = textTTS .. "; Detail: "..detailTTS.."\nTarget location "..CoordText + text = text .. "\nBriefing: "..detail.."\nTarget location "..CoordText + textTTS = textTTS .. "; Briefing: "..detailTTS.."\nTarget location "..CoordText end + + -- Pilots local clienttxt = self.gettext:GetEntry("PILOTS",self.locale) if clientcount > 0 then for _,_name in pairs(clientlist) do if self.customcallsigns[_name] then - -- personalized flight name in player naming - --_name = string.match(_name,"| ([%a]+)") _name = self.customcallsigns[_name] end clienttxt = clienttxt .. _name .. ", " @@ -2743,6 +2765,8 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) local keine = self.gettext:GetEntry("NONE",self.locale) clienttxt = clienttxt .. keine end + + -- Task Report text = text .. clienttxt textTTS = textTTS .. clienttxt if self.UseSRS then @@ -2762,6 +2786,10 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) if string.find(ttstext," BR, ") then CoordText = string.gsub(ttstext," BR, "," Bee, Arr, ") end + elseif task:HasFreetext() then + -- add freetext + text = text .. "\nBriefing: "..task:GetFreetext() + ttstext = ttstext .. "; Briefing: "..task:GetFreetextTTS() end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,2) end diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 901dcb5e4..ad1fdc575 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -1133,8 +1133,9 @@ end --- Get target 3D position vector. -- @param #TARGET self -- @param #TARGET.Object Target Target object. +-- @param #boolean Average -- @return DCS#Vec3 Vector with x,y,z components. -function TARGET:GetTargetVec3(Target) +function TARGET:GetTargetVec3(Target, Average) if Target.Type==TARGET.ObjectType.GROUP then @@ -1142,6 +1143,9 @@ function TARGET:GetTargetVec3(Target) if object and object:IsAlive() then local vec3=object:GetVec3() + if Average then + vec3=object:GetAverageVec3() + end if vec3 then return vec3 @@ -1220,8 +1224,9 @@ end --- Get target coordinate. -- @param #TARGET self -- @param #TARGET.Object Target Target object. +-- @param #boolean Average -- @return Core.Point#COORDINATE Coordinate of the target. -function TARGET:GetTargetCoordinate(Target) +function TARGET:GetTargetCoordinate(Target, Average) if Target.Type==TARGET.ObjectType.COORDINATE then @@ -1231,7 +1236,7 @@ function TARGET:GetTargetCoordinate(Target) else -- Get updated position vector. - local vec3=self:GetTargetVec3(Target) + local vec3=self:GetTargetVec3(Target, Average) -- Update position. This saves us to create a new COORDINATE object each time. if vec3 then @@ -1364,6 +1369,26 @@ function TARGET:GetCoordinate() return nil end +--- Get average coordinate. +-- @param #TARGET self +-- @return Core.Point#COORDINATE Coordinate of the target. +function TARGET:GetAverageCoordinate() + + for _,_target in pairs(self.targets) do + local Target=_target --#TARGET.Object + + local coordinate=self:GetTargetCoordinate(Target,true) + + if coordinate then + return coordinate + end + + end + + self:E(self.lid..string.format("ERROR: Cannot get average coordinate of target %s", tostring(self.name))) + return nil +end + --- Get category. -- @param #TARGET self -- @return #string Target category. See `TARGET.Category.X`, where `X=AIRCRAFT, GROUND`. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index e8e2e6fb9..906331fa4 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1019,9 +1019,9 @@ function GROUP:GetVec2() end ---- Returns the current Vec3 vector of the first DCS Unit in the GROUP. +--- Returns the current Vec3 vector of the first Unit in the GROUP. -- @param #GROUP self --- @return DCS#Vec3 Current Vec3 of the first DCS Unit of the GROUP. +-- @return DCS#Vec3 Current Vec3 of the first Unit of the GROUP or nil if cannot be found. function GROUP:GetVec3() -- Get first unit. @@ -1036,6 +1036,37 @@ function GROUP:GetVec3() return nil end +--- Returns the average Vec3 vector of the Units in the GROUP. +-- @param #GROUP self +-- @return DCS#Vec3 Current Vec3 of the GROUP or nil if cannot be found. +function GROUP:GetAverageVec3() + local units = self:GetUnits() or {} + -- Init. + local x=0 ; local y=0 ; local z=0 ; local n=0 + -- Loop over all units. + for _,unit in pairs(units) do + local vec3=nil --DCS#Vec3 + if unit and unit:IsAlive() then + vec3 = unit:GetVec3() + end + if vec3 then + -- Sum up posits. + x=x+vec3.x + y=y+vec3.y + z=z+vec3.z + -- Increase counter. + n=n+1 + end + end + + if n>0 then + -- Average. + local Vec3={x=x/n, y=y/n, z=z/n} --DCS#Vec3 + return Vec3 + end + return nil +end + --- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission. -- @param #GROUP self -- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP. @@ -1056,6 +1087,21 @@ function GROUP:GetPointVec2() return nil end +--- Returns a COORDINATE object indicating the average position of the GROUP within the mission. +-- @param Wrapper.Group#GROUP self +-- @return Core.Point#COORDINATE The COORDINATE of the GROUP. +function GROUP:GetAverageCoordinate() + local vec3 = self:GetAverageVec3() + if vec3 then + local coord = COORDINATE:NewFromVec3(vec3) + local Heading = self:GetHeading() + coord.Heading = Heading + else + BASE:E( { "Cannot GetAverageCoordinate", Group = self, Alive = self:IsAlive() } ) + return nil + end +end + --- Returns a COORDINATE object indicating the point of the first UNIT of the GROUP within the mission. -- @param Wrapper.Group#GROUP self -- @return Core.Point#COORDINATE The COORDINATE of the GROUP. From 53023c97c5ff8b5002e403cd0b2a832539a1bcdf Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 17 Oct 2022 12:45:37 +0200 Subject: [PATCH 073/603] CTLD - No radius check (#1800) (#1802) Get rid of radius check in IsUnitInZone() --- Moose Development/Moose/Ops/CTLD.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index b8a26447a..5c1166450 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -309,7 +309,7 @@ do -- @extends Core.Base#BASE --- --- @field CTLD_CARGO +-- @field #CTLD_CARGO CTLD_CARGO CTLD_CARGO = { ClassName = "CTLD_CARGO", ID = 0, @@ -1075,7 +1075,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.14" +CTLD.version="1.0.15" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3652,9 +3652,10 @@ function CTLD:IsUnitInZone(Unit,Zonetype) local zoneret = nil local zonewret = nil local zonenameret = nil + local unitcoord = Unit:GetCoordinate() + local unitVec2 = unitcoord:GetVec2() for _,_cargozone in pairs(zonetable) do local czone = _cargozone -- #CTLD.CargoZone - local unitcoord = Unit:GetCoordinate() local zonename = czone.name local active = czone.active local color = czone.color @@ -3671,17 +3672,17 @@ function CTLD:IsUnitInZone(Unit,Zonetype) zone = ZONE:FindByName(zonename) self:T("Checking Zone: "..zonename) zonecoord = zone:GetCoordinate() - zoneradius = zone:GetRadius() + zoneradius = 1500 zonewidth = zoneradius elseif AIRBASE:FindByName(zonename) then zone = AIRBASE:FindByName(zonename):GetZone() self:T("Checking Zone: "..zonename) zonecoord = zone:GetCoordinate() - zoneradius = zone:GetRadius() + zoneradius = 2500 zonewidth = zoneradius end local distance = self:_GetDistance(zonecoord,unitcoord) - if distance <= zoneradius and active then + if zone:IsVec2InZone(unitVec2) and active then outcome = true end if maxdist > distance then From fb52fb0517c099620cf095096d7d7663b8ab88ce Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 17 Oct 2022 15:49:45 +0200 Subject: [PATCH 074/603] Update Scenery.lua --- Moose Development/Moose/Wrapper/Scenery.lua | 23 ++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index c10f8e0d8..ff532b494 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -34,7 +34,6 @@ SCENERY = { ClassName = "SCENERY", } - --- Register scenery object as POSITIONABLE. --@param #SCENERY self --@param #string SceneryName Scenery name. @@ -99,7 +98,7 @@ function SCENERY:GetThreatLevel() return 0, "Scenery" end ---- Find a SCENERY object from it's name/id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first +--- Find a SCENERY object from its name or id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first -- to find the correct object. --@param #SCENERY self --@param #string Name The name/id of the scenery object as taken from the ME. Ex. '595785449' @@ -116,7 +115,7 @@ function SCENERY:FindByName(Name, Coordinate, Radius) -- @param Core.Point#COORDINATE coordinate -- @param #number radius -- @param #string name - local function SceneryScan (coordinate, radius, name) + local function SceneryScan(coordinate, radius, name) if coordinate ~= nil then local scenerylist = coordinate:ScanScenery(radius) local rscenery = nil @@ -139,10 +138,10 @@ function SCENERY:FindByName(Name, Coordinate, Radius) return scenery end ---- Find a SCENERY object from it's name/id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first +--- Find a SCENERY object from its name or id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first -- to find the correct object. --@param #SCENERY self ---@param #string Name The name/id of the scenery object as taken from the ME. Ex. '595785449' +--@param #string Name The name or id of the scenery object as taken from the ME. Ex. '595785449' --@param Core.Zone#ZONE Zone Where to find the scenery object. Can be handed as zone name. --@param #number Radius (optional) Search radius around coordinate, defaults to 100 --@return #SCENERY Scenery Object or `nil` if it cannot be found @@ -155,3 +154,17 @@ function SCENERY:FindByNameInZone(Name, Zone, Radius) local coordinate = Zone:GetCoordinate() return self:FindByName(Name,coordinate,Radius) end + +--- Find a SCENERY object from its zone name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first +-- to find the correct object. +--@param #SCENERY self +--@param #string Name The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object. +--@return #SCENERY Scenery Object or `nil` if it cannot be found +function SCENERY:FindByZoneName( ZoneName ) + local zone = ZoneName + if type(ZoneName) == "string" then + zone = ZONE:FindByName(ZoneName) + end + local _id = zone:GetProperty('OBJECT ID') + return self:FindByName(_id, zone:GetCoordinate()) +end From 7428ad21ee08bc9db0aaa881bdce7d3975a4870e Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 17 Oct 2022 15:53:15 +0200 Subject: [PATCH 075/603] Update Set.lua --- Moose Development/Moose/Core/Set.lua | 242 +++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 3c40434c0..70409a554 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -6732,3 +6732,245 @@ do -- SET_OPSGROUP end end + +do -- SET_SCENERY + + --- + -- @type SET_SCENERY + -- @extends Core.Set#SET_BASE + + --- Mission designers can use the SET_SCENERY class to build sets of scenery belonging to certain: + -- + -- * Zone Sets + -- + -- ## SET_SCENERY constructor + -- + -- Create a new SET_SCENERY object with the @{#SET_SCENERY.New} method: + -- + -- * @{#SET_SCENERY.New}: Creates a new SET_SCENERY object. + -- + -- ## Add or Remove SCENERY(s) from SET_SCENERY + -- + -- SCENERYs can be added and removed using the @{Core.Set#SET_SCENERY.AddSceneryByName} and @{Core.Set#SET_SCENERY.RemoveSceneryByName} respectively. + -- These methods take a single SCENERY name or an array of SCENERY names to be added or removed from SET_SCENERY. + -- + -- ## SET_SCENERY filter criteria + -- + -- N/A at the moment + -- + -- ## SET_SCENERY iterators + -- + -- Once the filters have been defined and the SET_SCENERY has been built, you can iterate the SET_SCENERY with the available iterator methods. + -- The iterator methods will walk the SET_SCENERY set, and call for each element within the set a function that you provide. + -- The following iterator methods are currently available within the SET_SCENERY: + -- + -- * @{#SET_SCENERY.ForEachScenery}: Calls a function for each alive unit it finds within the SET_SCENERY. + -- + -- ## SET_SCENERY atomic methods + -- + -- N/A at the moment + -- + -- === + -- @field #SET_SCENERY SET_SCENERY + SET_SCENERY = { + ClassName = "SET_SCENERY", + Scenerys = {}, + Filter = { + SceneryPrefixes = nil, + Zones = nil, + }, + } + + --- Creates a new SET_SCENERY object. Scenery is **not** auto-registered in the Moose database, there are too many objects on each map. Hence we need to find them first. For this we are using a SET_ZONE. + -- @param #SET_SCENERY self + -- @param #SET_ZONE ZoneSet SET_ZONE of ZONE objects as created by right-clicks on the map in the mission editor, choosing "assign as...". Rename the zones for grouping purposes, e.g. all sections of a bridge as "Bridge-1" to "Bridge-3". + -- @return #SET_SCENERY + -- @usage + -- -- Define a new SET_SCENERY Object. This Object will contain a reference to all added Scenery Objects. + -- ZoneSet = SET_ZONE:New():FilterPrefixes("Bridge"):FilterOnce() + -- mysceneryset = SET_SCENERY:New(ZoneSet) + function SET_SCENERY:New(ZoneSet) + + local zoneset = {} + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( zoneset ) ) -- Core.Set#SET_SCENERY + + local zonenames = {} + for _,_zone in pairs(ZoneSet.Set) do + table.insert(zonenames,_zone:GetName()) + end + + self:AddSceneryByName(zonenames) + + return self + end + + --- Add SCENERY(s) to SET_SCENERY. + -- @param #SET_SCENERY self + -- @param #string AddScenery A single SCENERY. + -- @return #SET_SCENERY self + function SET_SCENERY:AddScenery( AddScenery ) + self:F2( AddScenery:GetName() ) + + self:Add( AddScenery:GetName(), AddScenery ) + + return self + end + + + --- Add SCENERY(s) to SET_SCENERY. + -- @param #SET_SCENERY self + -- @param #string AddSceneryNames A single name or an array of SCENERY zone names. + -- @return #SET_SCENERY self + function SET_SCENERY:AddSceneryByName( AddSceneryNames ) + + local AddSceneryNamesArray = ( type( AddSceneryNames ) == "table" ) and AddSceneryNames or { AddSceneryNames } + + self:T( AddSceneryNamesArray ) + for AddSceneryID, AddSceneryName in pairs( AddSceneryNamesArray ) do + self:Add( AddSceneryName, SCENERY:FindByZoneName( AddSceneryName ) ) + end + + return self + end + + --- Remove SCENERY(s) from SET_SCENERY. + -- @param Core.Set#SET_SCENERY self + -- @param Wrapper.Scenery#SCENERY RemoveSceneryNames A single name or an array of SCENERY zone names. + -- @return self + function SET_SCENERY:RemoveSceneryByName( RemoveSceneryNames ) + + local RemoveSceneryNamesArray = ( type( RemoveSceneryNames ) == "table" ) and RemoveSceneryNames or { RemoveSceneryNames } + + for RemoveSceneryID, RemoveSceneryName in pairs( RemoveSceneryNamesArray ) do + self:Remove( RemoveSceneryName ) + end + + return self + end + + --- Finds a Scenery in the SET, based on the Scenery Name. + -- @param #SET_SCENERY self + -- @param #string SceneryName + -- @return Wrapper.Scenery#SCENERY The found Scenery. + function SET_SCENERY:FindScenery( SceneryName ) + local SceneryFound = self.Set[SceneryName] + return SceneryFound + end + + --- Builds a set of scenery objects in zones. + -- @param #SET_SCENERY self + -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE + -- @return #SET_SCENERY self + function SET_SCENERY:FilterZones( Zones ) + if not self.Filter.Zones then + self.Filter.Zones = {} + end + local zones = {} + if Zones.ClassName and Zones.ClassName == "SET_ZONE" then + zones = Zones.Set + elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") + return self + else + zones = Zones + end + for _,Zone in pairs( zones ) do + local zonename = Zone:GetName() + self.Filter.Zones[zonename] = Zone + end + return self + end + + --- Builds a set of SCENERYs that contain the given string in their name. + -- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all scenery that **contain** the string. + -- @param #SET_SCENERY self + -- @param #string Prefixes The string pattern(s) that need to be contained in the scenery name. Can also be passed as a `#table` of strings. + -- @return #SET_SCENERY self + function SET_SCENERY:FilterPrefixes( Prefixes ) + if not self.Filter.SceneryPrefixes then + self.Filter.SceneryPrefixes = {} + end + if type( Prefixes ) ~= "table" then + Prefixes = { Prefixes } + end + for PrefixID, Prefix in pairs( Prefixes ) do + self.Filter.SceneryPrefixes[Prefix] = Prefix + end + return self + end + + --- Iterate the SET_SCENERY and count how many SCENERYSs are alive. + -- @param #SET_SCENERY self + -- @return #number The number of UNITs alive. + function SET_SCENERY:CountAlive() + + local Set = self:GetSet() + + local CountU = 0 + for UnitID, UnitData in pairs(Set) do + if UnitData and UnitData:IsAlive() then + CountU = CountU + 1 + end + + end + + return CountU + end + + --- Iterate the SET_SCENERY and call an iterator function for each **alive** SCENERY, providing the SCENERY and optional parameters. + -- @param #SET_SCENERY self + -- @param #function IteratorFunction The function that will be called when there is an alive SCENERY in the SET_SCENERY. The function needs to accept a SCENERY parameter. + -- @return #SET_SCENERY self + function SET_SCENERY:ForEachScenery( IteratorFunction, ... ) + self:F2( arg ) + self:ForEach( IteratorFunction, arg, self:GetSet() ) + return self + end + + --- Get the center coordinate of the SET_SCENERY. + -- @param #SET_SCENERY self + -- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units. + function SET_SCENERY:GetCoordinate() + + local Coordinate = self:GetRandom():GetCoordinate() + + local x1 = Coordinate.x + local x2 = Coordinate.x + local y1 = Coordinate.y + local y2 = Coordinate.y + local z1 = Coordinate.z + local z2 = Coordinate.z + + for SceneryName, SceneryData in pairs( self:GetSet() ) do + + local Scenery = SceneryData -- Wrapper.Scenery#SCENERY + local Coordinate = Scenery:GetCoordinate() + + x1 = ( Coordinate.x < x1 ) and Coordinate.x or x1 + x2 = ( Coordinate.x > x2 ) and Coordinate.x or x2 + y1 = ( Coordinate.y < y1 ) and Coordinate.y or y1 + y2 = ( Coordinate.y > y2 ) and Coordinate.y or y2 + z1 = ( Coordinate.y < z1 ) and Coordinate.z or z1 + z2 = ( Coordinate.y > z2 ) and Coordinate.z or z2 + + end + + Coordinate.x = ( x2 - x1 ) / 2 + x1 + Coordinate.y = ( y2 - y1 ) / 2 + y1 + Coordinate.z = ( z2 - z1 ) / 2 + z1 + + self:F( { Coordinate = Coordinate } ) + return Coordinate + + end + + --- + -- @param #SET_SCENERY self + -- @param Wrapper.Scenery#SCENERY MScenery + -- @return #SET_SCENERY self + function SET_SCENERY:IsIncludeObject( MScenery ) + self:F2( MScenery ) + return true + end +end From dc239c81373143471f6f37320412f6455ca44fe6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 17 Oct 2022 17:57:31 +0200 Subject: [PATCH 076/603] #SET_SCENERY - Scenery and Target additions --- Moose Development/Moose/Ops/Target.lua | 2 +- Moose Development/Moose/Wrapper/Scenery.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index ad1fdc575..5a5c5c842 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -266,7 +266,7 @@ end -- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, AIRBASE or COORDINATE. function TARGET:AddObject(Object) - if Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") or Object:IsInstanceOf("SET_STATIC") or Object:IsInstanceOf("SET_OPSGROUP") then + if Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") or Object:IsInstanceOf("SET_STATIC") or Object:IsInstanceOf("SET_SCENERY") or Object:IsInstanceOf("SET_OPSGROUP") then --- -- Sets diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index ff532b494..22b8b627a 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -158,7 +158,7 @@ end --- Find a SCENERY object from its zone name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first -- to find the correct object. --@param #SCENERY self ---@param #string Name The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object. +--@param #string ZoneName The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object. --@return #SCENERY Scenery Object or `nil` if it cannot be found function SCENERY:FindByZoneName( ZoneName ) local zone = ZoneName From 59578a886876f6277b92ec8dc8392aa4561c5f15 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 17 Oct 2022 18:08:31 +0200 Subject: [PATCH 077/603] #SET_SCENERY --- Moose Development/Moose/Core/Set.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 70409a554..227207667 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -26,6 +26,7 @@ -- * @{#SET_AIRBASE}: Defines a collection of @{Wrapper.Airbase}s filtered by filter criteria. -- * @{#SET_CARGO}: Defines a collection of @{Cargo.Cargo}s filtered by filter criteria. -- * @{#SET_ZONE}: Defines a collection of @{Core.Zone}s filtered by filter criteria. +-- * @{#SET_SCENERY}: Defines a collection of @{Warpper.Scenery}s added via a filtered @{#SET_ZONE}. -- -- These classes are derived from @{#SET_BASE}, which contains the main methods to manage the collections. -- @@ -37,7 +38,7 @@ -- === -- -- ### Author: **FlightControl** --- ### Contributions: **funkyfranky** +-- ### Contributions: **funkyfranky**, **applevangelist** -- -- === -- @@ -6764,7 +6765,7 @@ do -- SET_SCENERY -- The iterator methods will walk the SET_SCENERY set, and call for each element within the set a function that you provide. -- The following iterator methods are currently available within the SET_SCENERY: -- - -- * @{#SET_SCENERY.ForEachScenery}: Calls a function for each alive unit it finds within the SET_SCENERY. + -- * @{#SET_SCENERY.ForEachScenery}: Calls a function for each alive object it finds within the SET_SCENERY. -- -- ## SET_SCENERY atomic methods -- @@ -6793,7 +6794,7 @@ do -- SET_SCENERY local zoneset = {} -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( zoneset ) ) -- Core.Set#SET_SCENERY + local self = BASE:Inherit( self, SET_BASE:New( zoneset ) ) -- Core.Set#SET_SCENERY local zonenames = {} for _,_zone in pairs(ZoneSet.Set) do @@ -6802,7 +6803,7 @@ do -- SET_SCENERY self:AddSceneryByName(zonenames) - return self + return self end --- Add SCENERY(s) to SET_SCENERY. @@ -6902,7 +6903,7 @@ do -- SET_SCENERY --- Iterate the SET_SCENERY and count how many SCENERYSs are alive. -- @param #SET_SCENERY self - -- @return #number The number of UNITs alive. + -- @return #number The number of SCENERYSs alive. function SET_SCENERY:CountAlive() local Set = self:GetSet() @@ -6930,7 +6931,7 @@ do -- SET_SCENERY --- Get the center coordinate of the SET_SCENERY. -- @param #SET_SCENERY self - -- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units. + -- @return Core.Point#COORDINATE The center coordinate of all the objects in the set. function SET_SCENERY:GetCoordinate() local Coordinate = self:GetRandom():GetCoordinate() From f495a583eb8ac324bdfe2deb790f8a13cb96dbb4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 18 Oct 2022 10:29:34 +0200 Subject: [PATCH 078/603] #PLAYERRECCE - add picture --- Moose Development/Moose/Ops/PlayerRecce.lua | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 1278a0a06..26669e6b2 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1,10 +1,12 @@ ---- **Ops** - Allow a player in a helo like the Gazelle to detect, smoke, flare, lase and report ground units to others. +--- **Ops** - Allow a player in a helo like the Gazelle, KA-50 to recon and lase groud targets. -- -- ## Features: -- --- * Allow a player in the Gazelle to detect, smoke, flare, lase and report ground units to others. +-- * Allow a player in a helicopter to detect, smoke, flare, lase and report ground units to others. -- * Implements visual detection from the helo --- * Implements optical detection via the Vivianne system and lasing +-- * Implements optical detection via the Gazelle Vivianne system and lasing +-- * KA-50 BlackShark basic support +-- * Everyone else gets visual detection only -- * Upload target info to a PLAYERTASKCONTROLLER Instance -- -- === @@ -23,7 +25,7 @@ -- === -- -- @module Ops.PlayerRecce --- @image @image Detection.JPG +-- @image Ops_PlayerRecce.png ------------------------------------------------------------------------------------------------------------------- -- PLAYERRECCE @@ -87,9 +89,12 @@ -- -- # PLAYERRECCE -- --- * Allow a player in the Gazelle to detect, smoke, flare, lase and report ground units to others. +-- * Allow a player in a helicopter to detect, smoke, flare, lase and report ground units to others. -- * Implements visual detection from the helo --- * Implements optical detection via the Vivianne system and lasing +-- * Implements optical detection via the Gazelle Vivianne system and lasing +-- * KA-50 BlackShark basic support +-- * Everyone else gets visual detection only +-- * Upload target info to a PLAYERTASKCONTROLLER Instance -- -- If you have questions or suggestions, please visit the [MOOSE Discord](https://discord.gg/AeYAkHP) channel. -- @@ -202,7 +207,7 @@ PLAYERRECCE.FlareColor = { ["ownflare"] = FLARECOLOR.Green, } ---- Create and rund a new PlayerRecce instance. +--- Create and run a new PlayerRecce instance. -- @param #PLAYERRECCE self -- @param #string Name The name of this instance -- @param #number Coalition, e.g. coalition.side.BLUE @@ -1501,7 +1506,7 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername) local text2tts = string.format("All stations, FACA %s on station at %s!",callsign, coordtext) text2tts = self:_GetTextForSpeech(text2tts) if self.debug then - self:I(text2.."\n"..text2tts) + self:I(text2.."\n"..text2tts) end if self.UseSRS then local grp = Client:GetGroup() From 4c073a30d7a241e0b3e3b271d2a551db04ed9066 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 18 Oct 2022 12:54:09 +0200 Subject: [PATCH 079/603] RECCE --- Moose Development/Moose/Ops/PlayerRecce.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 26669e6b2..ef80816c8 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1,4 +1,4 @@ ---- **Ops** - Allow a player in a helo like the Gazelle, KA-50 to recon and lase groud targets. +--- **Ops** - Allow a player in a helo like the Gazelle, KA-50 to recon and lase ground targets. -- -- ## Features: -- From 30ce1aa907bafd20f16d5ae577655da7d4baea89 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 18 Oct 2022 13:26:48 +0200 Subject: [PATCH 080/603] PlayerTask --- Moose Development/Moose/Ops/PlayerTask.lua | 34 ++++++++++++++++------ 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 84c48165f..5f614fe9d 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -944,7 +944,7 @@ do -- -- ## 2 Task Types -- --- Targets can be of types GROUP, SET\_GROUP, UNIT, SET\_UNIT, STATIC, SET\_STATIC, AIRBASE, ZONE or COORDINATE. The system will auto-create tasks for players from these targets. +-- Targets can be of types GROUP, SET\_GROUP, UNIT, SET\_UNIT, STATIC, SET\_STATIC, SET\_SCENERY, AIRBASE, ZONE or COORDINATE. The system will auto-create tasks for players from these targets. -- Tasks are created as @{Ops.PlayerTask#PLAYERTASK} objects, which leverage @{Ops.Target#TARGET} for the management of the actual target. The system creates these task types -- from the target objects: -- @@ -964,6 +964,8 @@ do -- * ZONE and COORDINATE - Targets will be scanned for GROUND or STATIC enemy units and tasks created from these -- * Intercept - Any airborne targets, if the controller is of type "A2A" -- * Anti-Ship - Any ship targets, if the controller is of type "A2S" +-- * CTLD - Combat transport and logistics deployment +-- * CSAR - Combat search and rescue -- -- ## 3 Task repetition -- @@ -1099,6 +1101,8 @@ do -- FLASHON = "%s - Flashing directions is now ON!", -- FLASHOFF = "%s - Flashing directions is now OFF!", -- FLASHMENU = "Flash Directions Switch", +-- BRIEFING = "Briefing", +-- TARGETLOCATION ="Target location", -- }, -- -- e.g. @@ -1320,6 +1324,8 @@ PLAYERTASKCONTROLLER.Messages = { FLASHON = "%s - Flashing directions is now ON!", FLASHOFF = "%s - Flashing directions is now OFF!", FLASHMENU = "Flash Directions Switch", + BRIEFING = "Briefing", + TARGETLOCATION ="Target location", }, DE = { TASKABORT = "Auftrag abgebrochen!", @@ -1382,14 +1388,16 @@ PLAYERTASKCONTROLLER.Messages = { FLASHON = "%s - Richtungsangaben einblenden ist EIN!", FLASHOFF = "%s - Richtungsangaben einblenden ist AUS!", FLASHMENU = "Richtungsangaben Schalter", + BRIEFING = "Briefing", + TARGETLOCATION ="Zielkoordinate", }, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.42" +PLAYERTASKCONTROLLER.version="0.1.43" ---- Constructor +--- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self -- @param #string Name Name of this controller -- @param #number Coalition of this controller, e.g. coalition.side.BLUE @@ -2590,7 +2598,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force) --local m=MESSAGE:New(text,"10","Tasking"):ToAll() end if self.UseSRS then - self:I(self.lid..text) + self:T(self.lid..text) self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) end self.TasksPerPlayer:Push(Task,playername) @@ -2747,8 +2755,12 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) textTTS = taskname local detail = task:GetFreetext() local detailTTS = task:GetFreetextTTS() - text = text .. "\nBriefing: "..detail.."\nTarget location "..CoordText - textTTS = textTTS .. "; Briefing: "..detailTTS.."\nTarget location "..CoordText + local brieftxt = self.gettext:GetEntry("BRIEFING",self.locale) + local locatxt = self.gettext:GetEntry("TARGETLOCATION",self.locale) + text = text .. string.format("\n%s: %s\n%s %s",brieftxt,detail,locatxt,CoordText) + --text = text .. "\nBriefing: "..detail.."\nTarget location "..CoordText + --textTTS = textTTS .. "; Briefing: "..detailTTS.."\nTarget location "..CoordText + textTTS = textTTS .. string.format("; %s: %s; %s %s",brieftxt,detailTTS,locatxt,CoordText) end -- Pilots @@ -2768,6 +2780,10 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) -- Task Report text = text .. clienttxt + if task:HasFreetext() then + local brieftxt = self.gettext:GetEntry("BRIEFING",self.locale) + text = text .. string.format("\n%s: ",brieftxt)..task:GetFreetext() + end textTTS = textTTS .. clienttxt if self.UseSRS then if string.find(CoordText," BR, ") then @@ -2787,9 +2803,9 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) CoordText = string.gsub(ttstext," BR, "," Bee, Arr, ") end elseif task:HasFreetext() then - -- add freetext - text = text .. "\nBriefing: "..task:GetFreetext() - ttstext = ttstext .. "; Briefing: "..task:GetFreetextTTS() + -- add tts freetext + local brieftxt = self.gettext:GetEntry("BRIEFING",self.locale) + ttstext = ttstext .. string.format("; %s: ",brieftxt)..task:GetFreetextTTS() end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,2) end From 2bf5cc2369c7aacc50d96809205849b394ea6f44 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 18 Oct 2022 16:13:55 +0200 Subject: [PATCH 081/603] #CSAR - use custom callsigns, add weight to helo per saved pilot --- Moose Development/Moose/Ops/CSAR.lua | 54 ++++++++++++++++++++++------ Moose Development/Moose/Ops/CTLD.lua | 1 - 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 477f1fb75..d5abb33e8 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -30,7 +30,7 @@ -- @module Ops.CSAR -- @image OPS_CSAR.jpg --- Date: June 2022 +-- Date: October 2022 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM @@ -270,7 +270,7 @@ CSAR.AircraftType["Bronco-OV-10A"] = 2 --- CSAR class version. -- @field #string version -CSAR.version="1.0.13" +CSAR.version="1.0.15" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -1210,6 +1210,20 @@ function CSAR:_RemoveNameFromDownedPilots(name,force) return found end +--- (Internal) Check if a name is in downed pilot table and remove it. +-- @param #CSAR self +-- @param #string UnitName +-- @return #string CallSign +function CSAR:_GetCustomCallSign(UnitName) + local callsign = Unitname + local unit = UNIT:FindByName(UnitName) + if unit and unit:IsAlive() then + local group = unit:GetGroup() + callsign = group:GetCustomCallSign(true,true) + end + return callsign +end + --- (Internal) Check state of wounded group. -- @param #CSAR self -- @param #string heliname heliname @@ -1266,9 +1280,9 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname) local dist = UTILS.MetersToNM(self.autosmokedistance) disttext = string.format("%.0fnm",dist) end - self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Finally, that is music in my ears!\nI'll pop a smoke when you are %s away.\nLand or hover by the smoke.", _heliName, _pilotName, disttext), self.messageTime,false,true) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Finally, that is music in my ears!\nI'll pop a smoke when you are %s away.\nLand or hover by the smoke.", self:_GetCustomCallSign(_heliName), _pilotName, disttext), self.messageTime,false,true) else - self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Finally, that is music in my ears!\nRequest a flare or smoke if you need.", _heliName, _pilotName), self.messageTime,false,true) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Finally, that is music in my ears!\nRequest a flare or smoke if you need.", self:_GetCustomCallSign(_heliName), _pilotName), self.messageTime,false,true) end --mark as shown for THIS heli and THIS group self.heliVisibleMessage[_lookupKeyHeli] = true @@ -1332,7 +1346,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam _maxUnits = self.max_units end if _unitsInHelicopter + 1 > _maxUnits then - self:_DisplayMessageToSAR(_heliUnit, string.format("%s, %s. We\'re already crammed with %d guys! Sorry!", _pilotName, _heliName, _unitsInHelicopter, _unitsInHelicopter), self.messageTime,false,false,true) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s, %s. We\'re already crammed with %d guys! Sorry!", _pilotName, self:_GetCustomCallSign(_heliName), _unitsInHelicopter, _unitsInHelicopter), self.messageTime,false,false,true) return self end @@ -1350,13 +1364,29 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam _woundedGroup:Destroy(false) self:_RemoveNameFromDownedPilots(_woundedGroupName,true) - self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", self:_GetCustomCallSign(_heliName), _pilotName), self.messageTime,true,true) + + self:_UpdateUnitCargoMass(_heliName) self:__Boarded(5,_heliName,_woundedGroupName,grouptable.desc) return self end +--- (Internal) Function to calculate and set Unit internal cargo mass +-- @param #CSAR self +-- @param #string _heliName Unit name +-- @return #CSAR self +function CSAR:_UpdateUnitCargoMass(_heliName) + self:T(self.lid .. " _UpdateUnitCargoMass") + local calculatedMass = self:_PilotsOnboard(_heliName)*80 + local Unit = UNIT:FindByName(_heliName) + if Unit then + Unit:SetUnitInternalCargo(calculatedMass) + end + return self +end + --- (Internal) Move group to destination. -- @param #CSAR self -- @param Wrapper.Group#GROUP _leader @@ -1371,7 +1401,6 @@ function CSAR:_OrderGroupToMoveToPoint(_leader, _destination) return self end - --- (internal) Function to check if the heli door(s) are open. Thanks to Shadowze. -- @param #CSAR self -- @param #string unit_name Name of unit. @@ -1405,9 +1434,9 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG if self.heliCloseMessage[_lookupKeyHeli] == nil then if self.autosmoke == true then - self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land or hover at the smoke.", _heliName, _pilotName), self.messageTime,false,true) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land or hover at the smoke.", self:_GetCustomCallSign(_heliName), _pilotName), self.messageTime,false,true) else - self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ", _heliName, _pilotName), self.messageTime,false,true) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ", self:_GetCustomCallSign(_heliName), _pilotName), self.messageTime,false,true) end self.heliCloseMessage[_lookupKeyHeli] = true end @@ -1574,9 +1603,12 @@ function CSAR:_RescuePilots(_heliUnit) self.inTransitGroups[_heliName] = nil - local _txt = string.format("%s: The %d pilot(s) have been taken to the\nmedical clinic. Good job!", _heliName, PilotsSaved) + local _txt = string.format("%s: The %d pilot(s) have been taken to the\nmedical clinic. Good job!", self:_GetCustomCallSign(_heliName), PilotsSaved) self:_DisplayMessageToSAR(_heliUnit, _txt, self.messageTime) + + self:_UpdateUnitCargoMass(_heliName) + -- trigger event self:__Rescued(-1,_heliUnit,_heliName, PilotsSaved) return self @@ -1610,7 +1642,7 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak, _overrid local _clear = _clear or nil local _time = _time or self.messageTime if _override or not self.suppressmessages then - local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group) + local m = MESSAGE:New(_text,_time,"CSAR",_clear):ToGroup(group) end -- integrate SRS if _speak and self.useSRS then diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 5c1166450..7b5dd5929 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,6 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Date: Feb 2022 -- Last Update October 2022 do From 7e12a48a8ad1ce3adebceadfdfb2a5e0d2512b77 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 18 Oct 2022 16:55:54 +0200 Subject: [PATCH 082/603] #Ops - various fixes --- Moose Development/Moose/Ops/CSAR.lua | 42 ++++++++++++++++------ Moose Development/Moose/Ops/PlayerTask.lua | 4 +-- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index d5abb33e8..511fc7929 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -1210,6 +1210,24 @@ function CSAR:_RemoveNameFromDownedPilots(name,force) return found end +--- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns. +-- @param #CSAR self +-- @param #boolean ShortCallsign If true, only call out the major flight number +-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name for players as-is, no amendments or numbers. +-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized +-- callsigns from playername or group name. +-- @return #CSAR self +function CSAR:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations) + if not ShortCallsign or ShortCallsign == false then + self.ShortCallsign = false + else + self.ShortCallsign = true + end + self.Keepnumber = Keepnumber or false + self.CallsignTranslations = CallsignTranslations + return self +end + --- (Internal) Check if a name is in downed pilot table and remove it. -- @param #CSAR self -- @param #string UnitName @@ -1219,7 +1237,7 @@ function CSAR:_GetCustomCallSign(UnitName) local unit = UNIT:FindByName(UnitName) if unit and unit:IsAlive() then local group = unit:GetGroup() - callsign = group:GetCustomCallSign(true,true) + callsign = group:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) end return callsign end @@ -1791,7 +1809,7 @@ function CSAR:_SignalFlare(_unitName) else _distance = string.format("%.1fkm",_closest.distance) end - local _msg = string.format("%s - Popping signal flare at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance) + local _msg = string.format("%s - Popping signal flare at your %s o\'clock. Distance %s", self:_GetCustomCallSign(_unitName), _clockDir, _distance) self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true, true) local _coord = _closest.pilot:GetCoordinate() @@ -1845,7 +1863,7 @@ function CSAR:_Reqsmoke( _unitName ) else _distance = string.format("%.1fkm",_closest.distance/1000) end - local _msg = string.format("%s - Popping smoke at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance) + local _msg = string.format("%s - Popping smoke at your %s o\'clock. Distance %s", self:_GetCustomCallSign(_unitName), _clockDir, _distance) self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true, true) local _coord = _closest.pilot:GetCoordinate() local color = self.smokecolor @@ -2049,13 +2067,17 @@ function CSAR:_GetClockDirection(_heli, _group) local DirectionVec3 = _playerPosition:GetDirectionVec3( _targetpostions ) local Angle = _playerPosition:GetAngleDegrees( DirectionVec3 ) self:T(self.lid .. " _GetClockDirection"..tostring(Angle).." "..tostring(_heading)) - local clock = 12 - if _heading then - local Aspect = Angle - _heading - if Aspect == 0 then Aspect = 360 end - clock = math.abs(UTILS.Round((Aspect / 30),0)) - if clock == 0 then clock = 12 end - end + local hours = 0 + local clock = 12 + if _heading and Angle then + clock = 12 + --if angle == 0 then angle = 360 end + clock = _heading-Angle + hours = (clock/30)*-1 + clock = 12+hours + clock = UTILS.Round(clock,0) + if clock > 12 then clock = clock-12 end + end return clock end diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 5f614fe9d..d5a63324e 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1626,7 +1626,7 @@ end -- @param #string text Original text. -- @return #string Spoken text. function PLAYERTASKCONTROLLER:_GetTextForSpeech(text) - + self:T(self.lid.."_GetTextForSpeech") -- Space out numbers. text=string.gsub(text,"%d","%1 ") -- get rid of leading or trailing spaces @@ -2780,7 +2780,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) -- Task Report text = text .. clienttxt - if task:HasFreetext() then + if task:HasFreetext() and not ( task.Type == AUFTRAG.Type.CTLD or task.Type == AUFTRAG.Type.CSAR) then local brieftxt = self.gettext:GetEntry("BRIEFING",self.locale) text = text .. string.format("\n%s: ",brieftxt)..task:GetFreetext() end From 6d92bff3e979ce8324d6b1397fd857259eb7b0a3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 19 Oct 2022 12:26:26 +0200 Subject: [PATCH 083/603] #SCENERY; SET_SCENERY * enhancements --- Moose Development/Moose/Core/Database.lua | 2 +- Moose Development/Moose/Core/Set.lua | 42 +++++++---- Moose Development/Moose/Core/Zone.lua | 84 ++++++++++++++++++--- Moose Development/Moose/Ops/PlayerTask.lua | 16 +++- Moose Development/Moose/Wrapper/Scenery.lua | 65 +++++++++++++++- 5 files changed, 180 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index e80f36558..5cf5bf666 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -309,7 +309,7 @@ do -- Zones self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName)) - Zone=ZONE_POLYGON_BASE:New(ZoneName, ZoneData.verticies) + Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, ZoneData.verticies) --for i,vec2 in pairs(ZoneData.verticies) do -- local coord=COORDINATE:NewFromVec2(vec2) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 227207667..7e82a3edb 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -6792,20 +6792,36 @@ do -- SET_SCENERY -- mysceneryset = SET_SCENERY:New(ZoneSet) function SET_SCENERY:New(ZoneSet) - local zoneset = {} - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( zoneset ) ) -- Core.Set#SET_SCENERY - - local zonenames = {} - for _,_zone in pairs(ZoneSet.Set) do - table.insert(zonenames,_zone:GetName()) - end - - self:AddSceneryByName(zonenames) - - return self + local zoneset = {} + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( zoneset ) ) -- Core.Set#SET_SCENERY + + local zonenames = {} + + if ZoneSet then + for _,_zone in pairs(ZoneSet.Set) do + self:I("Zone type handed: "..tostring(_zone.ClassName)) + table.insert(zonenames,_zone:GetName()) + end + self:AddSceneryByName(zonenames) + end + + return self end - + + --- Creates a new SET_SCENERY object. Scenery is **not** auto-registered in the Moose database, there are too many objects on each map. Hence we need to find them first. For this we scan the zone. + -- @param #SET_SCENERY self + -- @param Core.Zone#ZONE Zone The zone to be scanned. Can be a ZONE_RADIUS (round) or a ZONE_POLYGON (e.g. Quad-Point) + -- @return #SET_SCENERY + function SET_SCENERY:NewFromZone(Zone) + local zone = Zone -- Core.Zone#ZONE_POLYGON + if type(Zone) == "string" then + zone = ZONE:FindByName(Zone) + end + zone:Scan({Object.Category.SCENERY}) + return zone:GetScannedSetScenery() + end + --- Add SCENERY(s) to SET_SCENERY. -- @param #SET_SCENERY self -- @param #string AddScenery A single SCENERY. diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 3c14da69c..d00c64f46 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -934,6 +934,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) self.ScanData = {} self.ScanData.Coalitions = {} self.ScanData.Scenery = {} + self.ScanData.SceneryTable = {} self.ScanData.Units = {} local ZoneCoord = self:GetCoordinate() @@ -996,8 +997,10 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) if ObjectCategory == Object.Category.SCENERY then local SceneryType = ZoneObject:getTypeName() local SceneryName = ZoneObject:getName() + --BASE:I("SceneryType "..SceneryType.."SceneryName"..SceneryName) self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {} self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject ) + table.insert(self.ScanData.SceneryTable,self.ScanData.Scenery[SceneryType][SceneryName] ) self:T( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } ) end @@ -1137,11 +1140,29 @@ end --- Get scanned scenery table -- @param #ZONE_RADIUS self --- @return #table Table of DCS scenery objects. +-- @return #table Structured object table: [type].[name].SCENERY function ZONE_RADIUS:GetScannedScenery() return self.ScanData.Scenery end +--- Get table of scanned scenery objects +-- @param #ZONE_RADIUS self +-- @return #table Table of SCENERY objects. +function ZONE_RADIUS:GetScannedSceneryObjects() + return self.ScanData.SceneryTable +end + +--- Get set of scanned scenery objects +-- @param #ZONE_RADIUS self +-- @return #table Table of Wrapper.Scenery#SCENERY scenery objects. +function ZONE_RADIUS:GetScannedSetScenery() + local scenery = SET_SCENERY:New() + local objects = self:GetScannedSceneryObjects() + for _,_obj in pairs (objects) do + scenery:AddScenery(_obj) + end + return scenery +end --- Is All in Zone of Coalition? -- Check if only the specifed coalition is inside the zone and noone else. @@ -2452,14 +2473,28 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories ) self.ScanData = {} self.ScanData.Coalitions = {} self.ScanData.Scenery = {} + self.ScanData.SceneryTable = {} self.ScanData.Units = {} - + + local vectors = self:GetBoundingSquare() + + local minVec3 = {x=vectors.x1, y=0, z=vectors.y1} + local maxVec3 = {x=vectors.x2, y=0, z=vectors.y2} + + local VolumeBox = { + id = world.VolumeType.BOX, + params = { + min = minVec3, + max = maxVec3 + } + } + local function EvaluateZone( ZoneObject ) if ZoneObject then local ObjectCategory = ZoneObject:getCategory() - + if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then local CoalitionDCSUnit = ZoneObject:getCoalition() @@ -2494,16 +2529,16 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories ) end end - --[[ - -- no scenery possible at the moment + -- trying with box search if ObjectCategory == Object.Category.SCENERY then local SceneryType = ZoneObject:getTypeName() local SceneryName = ZoneObject:getName() self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {} self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject ) + table.insert(self.ScanData.SceneryTable,self.ScanData.Scenery[SceneryType][SceneryName]) self:T( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } ) end - --]] + end return true @@ -2529,6 +2564,18 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories ) end ) + local searchscenery = false + for _,_type in pairs(ObjectCategories) do + if _type == Object.Category.SCENERY then + searchscenery = true + end + end + + if searchscenery then + -- Search objects. + world.searchObjects({Object.Category.SCENERY}, VolumeBox, EvaluateZone ) + end + end --- Count the number of different coalitions inside the zone. @@ -2643,20 +2690,39 @@ function ZONE_POLYGON:GetScannedCoalition( Coalition ) end end ---- Get scanned scenery type (currently not implemented in ZONE_POLYGON) +--- Get scanned scenery types -- @param #ZONE_POLYGON self -- @return #table Table of DCS scenery type objects. function ZONE_POLYGON:GetScannedSceneryType( SceneryType ) return self.ScanData.Scenery[SceneryType] end ---- Get scanned scenery table (currently not implemented in ZONE_POLYGON) +--- Get scanned scenery table -- @param #ZONE_POLYGON self --- @return #table Table of DCS scenery objects. +-- @return #table Table of Wrapper.Scenery#SCENERY scenery objects. +function ZONE_POLYGON:GetScannedSceneryObjects() + return self.ScanData.SceneryTable +end + +--- Get scanned scenery table +-- @param #ZONE_POLYGON self +-- @return #table Structured table of [type].[name].Wrapper.Scenery#SCENERY scenery objects. function ZONE_POLYGON:GetScannedScenery() return self.ScanData.Scenery end +--- Get scanned set of scenery objects +-- @param #ZONE_POLYGON self +-- @return #table Table of Wrapper.Scenery#SCENERY scenery objects. +function ZONE_POLYGON:GetScannedSetScenery() + local scenery = SET_SCENERY:New() + local objects = self:GetScannedSceneryObjects() + for _,_obj in pairs (objects) do + scenery:AddScenery(_obj) + end + return scenery +end + --- Is All in Zone of Coalition? -- Check if only the specifed coalition is inside the zone and noone else. -- @param #ZONE_POLYGON self diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index d5a63324e..95288c1df 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -918,6 +918,7 @@ do -- @field #boolean buddylasing -- @field Ops.PlayerRecce#PLAYERRECCE PlayerRecce -- @field #number Coalition +-- @field Core.Menu#MENU_MISSION MenuParent -- @extends Core.Fsm#FSM --- @@ -1229,6 +1230,7 @@ PLAYERTASKCONTROLLER = { buddylasing = false, PlayerRecce = nil, Coalition = nil, + MenuParent = nil, } --- @@ -1395,7 +1397,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.43" +PLAYERTASKCONTROLLER.version="0.1.44" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -3052,7 +3054,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) end else -- 1) new player# - topmenu = MENU_GROUP_DELAYED:New(group,menuname,nil) + topmenu = MENU_GROUP_DELAYED:New(group,menuname,self.MenuParent) self.PlayerMenu[playername] = topmenu self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) end @@ -3237,6 +3239,16 @@ function PLAYERTASKCONTROLLER:SetMenuName(Name) return self end +--- [User] Set the top menu to be a sub-menu of another MENU entry. +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Menu#MENU_MISSION Menu +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetParentName(Menu) + self:T(self.lid.."SetParentName") + self.MenuParent = Menu + return self +end + --- [User] Set up INTEL detection -- @param #PLAYERTASKCONTROLLER self -- @param #string RecceName This name will be used to build a detection group set. All groups with this string somewhere in their group name will be added as Recce. diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 22b8b627a..b8c8dc22d 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -159,12 +159,69 @@ end -- to find the correct object. --@param #SCENERY self --@param #string ZoneName The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object. ---@return #SCENERY Scenery Object or `nil` if it cannot be found +--@return #SCENERY First found Scenery Object or `nil` if it cannot be found function SCENERY:FindByZoneName( ZoneName ) - local zone = ZoneName + local zone = ZoneName -- Core.Zone#ZONE if type(ZoneName) == "string" then - zone = ZONE:FindByName(ZoneName) + zone = ZONE:FindByName(ZoneName) end local _id = zone:GetProperty('OBJECT ID') - return self:FindByName(_id, zone:GetCoordinate()) + if not _id then + -- this zone has no object ID + BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) + if string.find(zone.ClassName,"POLYGON") then + zone:Scan({Object.Category.SCENERY}) + local scanned = zone:GetScannedScenery() + for _,_scenery in (scanned) do + local scenery = _scenery -- Wrapper.Scenery#SCENERY + if scenery:IsAlive() then + return scenery + end + end + return nil + else + local coordinate = zone:GetCoordinate() + local scanned = coordinate:ScanScenery() + for _,_scenery in (scanned) do + local scenery = _scenery -- Wrapper.Scenery#SCENERY + if scenery:IsAlive() then + return scenery + end + end + return nil + end + else + return self:FindByName(_id, zone:GetCoordinate()) + end +end + +--- Scan and find all SCENERY objects from a zone by zone-name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first +-- to find the correct object. +--@param #SCENERY self +--@param #string ZoneName The name of the zone, can be handed as ZONE_RADIUS or ZONE_POLYGON object +--@return #table of SCENERY Objects, or `nil` if nothing found +function SCENERY:FindAllByZoneName( ZoneName ) + local zone = ZoneName -- Core.Zone#ZONE_RADIUS + if type(ZoneName) == "string" then + zone = ZONE:FindByName(ZoneName) + end + local _id = zone:GetProperty('OBJECT ID') + if not _id then + -- this zone has no object ID + --BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) + zone:Scan({Object.Category.SCENERY}) + local scanned = zone:GetScannedSceneryObjects() + if #scanned > 0 then + return scanned + else + return nil + end + else + local obj = self:FindByName(_id, zone:GetCoordinate()) + if obj then + return {obj} + else + return nil + end + end end From 4e79123c1106ee9baa4ae358a836f7d2bf4defff Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 19 Oct 2022 13:03:25 +0200 Subject: [PATCH 084/603] #PLAYERTASKCONTROLLER * No spaces in callsign-Nos in task info pilot list --- Moose Development/Moose/Ops/PlayerTask.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 95288c1df..c62c0015a 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -2770,7 +2770,8 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) if clientcount > 0 then for _,_name in pairs(clientlist) do if self.customcallsigns[_name] then - _name = self.customcallsigns[_name] + _name = self.customcallsigns[_name] + _name = string.gsub(_name, "(%d) ","%1") end clienttxt = clienttxt .. _name .. ", " end From 3308f16de64c837e0ba45b4f14664c0282d7fa2d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 19 Oct 2022 17:24:58 +0200 Subject: [PATCH 085/603] #TARGET * Some additions for ease of use, and SCENERY --- Moose Development/Moose/Ops/Target.lua | 152 +++++++++++++++++++------ 1 file changed, 117 insertions(+), 35 deletions(-) diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 5a5c5c842..f0a3379af 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -9,6 +9,8 @@ -- === -- -- ### Author: **funkyfranky** +-- ### Additions: **applevangelist** +-- -- @module Ops.Target -- @image OPS_Target.png @@ -114,9 +116,11 @@ TARGET.Category={ -- @type TARGET.ObjectStatus -- @field #string ALIVE Object is alive. -- @field #string DEAD Object is dead. +-- @field #string DAMAGED Object is damaged. TARGET.ObjectStatus={ ALIVE="Alive", DEAD="Dead", + DAMAGED="Damaged", } --- Resource. @@ -147,7 +151,7 @@ _TARGETID=0 --- TARGET class version. -- @field #string version -TARGET.version="0.5.4" +TARGET.version="0.5.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -204,7 +208,7 @@ function TARGET:New(TargetObject) self:AddTransition("*", "ObjectDestroyed", "*") -- A target object was destroyed. self:AddTransition("*", "ObjectDead", "*") -- A target object is dead (destroyed or despawned). - self:AddTransition("*", "Damaged", "*") -- Target was damaged. + self:AddTransition("*", "Damaged", "Damaged") -- Target was damaged. self:AddTransition("*", "Destroyed", "Dead") -- Target was completely destroyed. self:AddTransition("*", "Dead", "Dead") -- Target is dead. Could be destroyed or despawned. @@ -237,7 +241,51 @@ function TARGET:New(TargetObject) -- @function [parent=#TARGET] __Status -- @param #TARGET self -- @param #number delay Delay in seconds. - + + --- On After "ObjectDamaged" event. A (sub-) target object has been damaged, e.g. a UNIT of a GROUP, or an object of a SET + -- @function [parent=#TARGET] OnAfterObjectDamaged + -- @param #TARGET self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #TARGET.Object Target Target object. + + --- On After "ObjectDestroyed" event. A (sub-) target object has been destroyed, e.g. a UNIT of a GROUP, or an object of a SET + -- @function [parent=#TARGET] OnAfterObjectDestroyed + -- @param #TARGET self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #TARGET.Object Target Target object. + + --- On After "ObjectDead" event. A (sub-) target object is dead, e.g. a UNIT of a GROUP, or an object of a SET + -- @function [parent=#TARGET] OnAfterObjectDead + -- @param #TARGET self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #TARGET.Object Target Target object. + + --- On After "Damaged" event. The (whole) target object has been damaged. + -- @function [parent=#TARGET] OnAfterDamaged + -- @param #TARGET self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "ObjectDestroyed" event. The (whole) target object has been destroyed. + -- @function [parent=#TARGET] OnAfterDestroyed + -- @param #TARGET self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- On After "ObjectDead" event. The (whole) target object is dead. + -- @function [parent=#TARGET] OnAfterDead + -- @param #TARGET self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. -- Start. self:__Start(-1) @@ -303,6 +351,7 @@ function TARGET:AddObject(Object) end + return self end --- Set priority of the target. @@ -501,7 +550,7 @@ end -- @param #string Event Event. -- @param #string To To state. function TARGET:onafterStart(From, Event, To) - + self:T({From, Event, To}) -- Short info. local text=string.format("Starting Target") self:T(self.lid..text) @@ -511,6 +560,7 @@ function TARGET:onafterStart(From, Event, To) self:HandleEvent(EVENTS.RemoveUnit, self.OnEventUnitDeadOrLost) self:__Status(-1) + return self end --- On after "Status" event. @@ -520,7 +570,7 @@ end -- @param #string Event Event. -- @param #string To To state. function TARGET:onafterStatus(From, Event, To) - + self:T({From, Event, To}) -- FSM state. local fsmstate=self:GetState() @@ -529,18 +579,29 @@ function TARGET:onafterStatus(From, Event, To) for i,_target in pairs(self.targets) do local target=_target --#TARGET.Object + -- old life local life=target.Life - + -- curr life target.Life=self:GetTargetLife(target) + -- TODO: special case ED bug > life **increases** after hits on SCENERY + if target.Life > target.Life0 then + local delta = 2*(target.Life-target.Life0) + target.Life0 = target.Life0 + delta + life = target.Life0 + self.life0 = self.life0+delta + end + if target.Life object dead now for target object %s!", tostring(target.Name))) + if life < 1 and (not target.Status == TARGET.ObjectStatus.DEAD) then + self:E(self.lid..string.format("FF life is zero but no object dead event fired ==> object dead now for target object %s!", tostring(target.Name))) self:ObjectDead(target) + damaged = true end end @@ -574,6 +635,7 @@ function TARGET:onafterStatus(From, Event, To) if self:IsAlive() then self:__Status(-self.TStatus) end + return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -587,10 +649,11 @@ end -- @param #string To To state. -- @param #TARGET.Object Target Target object. function TARGET:onafterObjectDamaged(From, Event, To, Target) - + self:T({From, Event, To}) -- Debug info. self:T(self.lid..string.format("Object %s damaged", Target.Name)) - + + return self end --- On after "ObjectDestroyed" event. @@ -600,7 +663,7 @@ end -- @param #string To To state. -- @param #TARGET.Object Target Target object. function TARGET:onafterObjectDestroyed(From, Event, To, Target) - + self:T({From, Event, To}) -- Debug message. self:T(self.lid..string.format("Object %s destroyed", Target.Name)) @@ -610,6 +673,7 @@ function TARGET:onafterObjectDestroyed(From, Event, To, Target) -- Call object dead event. self:ObjectDead(Target) + return self end --- On after "ObjectDead" event. @@ -619,7 +683,7 @@ end -- @param #string To To state. -- @param #TARGET.Object Target Target object. function TARGET:onafterObjectDead(From, Event, To, Target) - + self:T({From, Event, To}) -- Debug message. self:T(self.lid..string.format("Object %s dead", Target.Name)) @@ -652,8 +716,11 @@ function TARGET:onafterObjectDead(From, Event, To, Target) self:Dead() end + else + self:Damaged() end - + + return self end --- On after "Damaged" event. @@ -662,9 +729,11 @@ end -- @param #string Event Event. -- @param #string To To state. function TARGET:onafterDamaged(From, Event, To) - + self:T({From, Event, To}) + self:T(self.lid..string.format("TARGET damaged")) - + + return self end --- On after "Destroyed" event. @@ -673,11 +742,14 @@ end -- @param #string Event Event. -- @param #string To To state. function TARGET:onafterDestroyed(From, Event, To) - + + self:T({From, Event, To}) + self:T(self.lid..string.format("TARGET destroyed")) self:Dead() - + + return self end --- On after "Dead" event. @@ -686,9 +758,11 @@ end -- @param #string Event Event. -- @param #string To To state. function TARGET:onafterDead(From, Event, To) - + self:T({From, Event, To}) + self:T(self.lid..string.format("TARGET dead")) - + + return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -706,7 +780,7 @@ function TARGET:OnEventUnitDeadOrLost(EventData) if self:IsElement(Name) and not self:IsCasualty(Name) then -- Debug info. - self:T3(self.lid..string.format("EVENT ID=%d: Unit %s dead or lost!", EventData.id, tostring(Name))) + self:T(self.lid..string.format("EVENT ID=%d: Unit %s dead or lost!", EventData.id, tostring(Name))) -- Add to the list of casualties. table.insert(self.casualties, Name) @@ -735,15 +809,19 @@ function TARGET:OnEventUnitDeadOrLost(EventData) -- Debug message. self:T2(self.lid..string.format("EVENT ID=%d: target %s dead/lost ==> destroyed", EventData.id, tostring(target.Name))) - + + target.Life = 0 + -- Trigger object destroyed event. self:ObjectDestroyed(target) - + else -- Debug message. self:T2(self.lid..string.format("EVENT ID=%d: target %s removed ==> dead", EventData.id, tostring(target.Name))) - + + target.Life = 0 + -- Trigger object dead event. self:ObjectDead(target) @@ -754,7 +832,7 @@ function TARGET:OnEventUnitDeadOrLost(EventData) end -- Event belongs to this TARGET end - + return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -763,7 +841,7 @@ end --- Create target data from a given object. -- @param #TARGET self --- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, AIRBASE or COORDINATE. +-- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, SCENERY, AIRBASE or COORDINATE. function TARGET:_AddObject(Object) local target={} --#TARGET.Object @@ -847,8 +925,8 @@ function TARGET:_AddObject(Object) target.Coordinate=scenery:GetCoordinate() - target.Life0=1 - target.Life=1 + target.Life0=scenery:GetLife0() + target.Life=scenery:GetLife() target.N0=target.N0+1 @@ -921,7 +999,8 @@ function TARGET:_AddObject(Object) if self.category==nil then self.category=self:GetTargetCategory(target) end - + + return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -990,8 +1069,9 @@ function TARGET:GetTargetLife(Target) elseif Target.Type==TARGET.ObjectType.SCENERY then - if Target.Status==TARGET.ObjectStatus.ALIVE then - return 1 + if Target.Object and Target.Object:IsAlive() then + local life = Target.Object:GetLife() + return life else return 0 end @@ -1015,7 +1095,8 @@ function TARGET:GetTargetLife(Target) else self:E("ERROR: unknown target object type in GetTargetLife!") end - + + return self end --- Get current life points. @@ -1090,7 +1171,8 @@ function TARGET:GetTargetThreatLevelMax(Target) else self:E("ERROR: unknown target object type in GetTargetThreatLevel!") end - + + return self end @@ -1557,7 +1639,7 @@ function TARGET:CountObjectives(Target) elseif Target.Type==TARGET.ObjectType.SCENERY then - if Target.Status==TARGET.ObjectStatus.ALIVE then + if Target.Status~=TARGET.ObjectStatus.DEAD then N=N+1 end @@ -1629,7 +1711,7 @@ function TARGET:IsCasualty(Name) end for _,name in pairs(self.casualties) do - if name==Name then + if tostring(name)==tostring(Name) then return true end end From 59e436ba69d6d1e2c2e0dbb6babcb9321ea69672 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 19 Oct 2022 19:14:16 +0200 Subject: [PATCH 086/603] #TARGET additions --- Moose Development/Moose/Ops/Target.lua | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index f0a3379af..88269f826 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -4,7 +4,7 @@ -- -- * Manages target, number alive, life points, damage etc. -- * Events when targets are damaged or destroyed --- * Various target objects: UNIT, GROUP, STATIC, AIRBASE, COORDINATE, SET_GROUP, SET_UNIT +-- * Various target objects: UNIT, GROUP, STATIC, SCENERY, AIRBASE, COORDINATE, ZONE, SET_GROUP, SET_UNIT, SET_STATIC, SET_SCENERY, SET_ZONE -- -- === -- @@ -158,7 +158,7 @@ TARGET.version="0.5.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Had cases where target life was 0 but target was not dead. Need to figure out why! --- TODO: Add pseudo functions. +-- DONE: Add pseudo functions. -- DONE: Initial object can be nil. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -167,7 +167,7 @@ TARGET.version="0.5.5" --- Create a new TARGET object and start the FSM. -- @param #TARGET self --- @param #table TargetObject Target object. +-- @param #table TargetObject Target object. Can be a: UNIT, GROUP, STATIC, SCENERY, AIRBASE, COORDINATE, ZONE, SET_GROUP, SET_UNIT, SET_STATIC, SET_SCENERY, SET_ZONE -- @return #TARGET self function TARGET:New(TargetObject) @@ -302,16 +302,19 @@ end -- * GROUP -- * UNIT -- * STATIC +-- * SCENERY -- * AIRBASE -- * COORDINATE -- * ZONE -- * SET_GROUP -- * SET_UNIT -- * SET_STATIC +-- * SET_SCENERY -- * SET_OPSGROUP +-- * SET_ZONE -- -- @param #TARGET self --- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, AIRBASE or COORDINATE. +-- @param Wrapper.Positionable#POSITIONABLE Object The target UNIT, GROUP, STATIC, SCENERY, AIRBASE, COORDINATE, ZONE, SET_GROUP, SET_UNIT, SET_STATIC, SET_SCENERY, SET_ZONE function TARGET:AddObject(Object) if Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") or Object:IsInstanceOf("SET_STATIC") or Object:IsInstanceOf("SET_SCENERY") or Object:IsInstanceOf("SET_OPSGROUP") then @@ -841,7 +844,7 @@ end --- Create target data from a given object. -- @param #TARGET self --- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, SCENERY, AIRBASE or COORDINATE. +-- @param Wrapper.Positionable#POSITIONABLE Object The target UNIT, GROUP, STATIC, SCENERY, AIRBASE, COORDINATE, ZONE, SET_GROUP, SET_UNIT, SET_STATIC, SET_SCENERY, SET_ZONE function TARGET:_AddObject(Object) local target={} --#TARGET.Object @@ -1378,6 +1381,11 @@ function TARGET:GetTargetName(Target) return Zone:GetName() + elseif Target.Type==TARGET.ObjectType.SCENERY then + + local Zone=Target.Object --Core.Zone#ZONE + + return Zone:GetName() end return "Unknown" @@ -1579,7 +1587,7 @@ function TARGET:GetObjective() for _,_target in pairs(self.targets) do local target=_target --#TARGET.Object - if target.Status==TARGET.ObjectStatus.ALIVE then + if target.Status~=TARGET.ObjectStatus.DEAD then return target end end From b6394ad7d3aa3211d62819810ea2e35638ffdcfa Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 20 Oct 2022 11:39:29 +0200 Subject: [PATCH 087/603] #MANTIS * Add systems from SMA mod --- Moose Development/Moose/Functional/Mantis.lua | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 74c724336..2aa4103e8 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -2,7 +2,7 @@ -- -- === -- --- **MANTIS** - Moose derived Modular, Automatic and Network capable Targeting and Interception System +-- **MANTIS** - Moose derived Modular, Automatic and Network capable Targeting and Interception System. -- Controls a network of SAM sites. Uses detection to switch on the AA site closest to the enemy. -- Automatic mode (default since 0.8) can set-up your SAM site network automatically for you. -- Leverage evasiveness from SEAD, leverage attack range setting. @@ -20,7 +20,7 @@ -- @module Functional.Mantis -- @image Functional.Mantis.jpg -- --- Date: Dec 2021 +-- Last Update: Oct 2022 ------------------------------------------------------------------------- --- **MANTIS** class, extends Core.Base#BASE @@ -61,10 +61,11 @@ --- *The worst thing that can happen to a good cause is, not to be skillfully attacked, but to be ineptly defended.* - Frédéric Bastiat -- --- Simple Class for a more intelligent Air Defense System +-- Moose class for a more intelligent Air Defense System -- --- #MANTIS --- Moose derived Modular, Automatic and Network capable Targeting and Interception System. +-- # MANTIS +-- +-- * Moose derived Modular, Automatic and Network capable Targeting and Interception System. -- * Controls a network of SAM sites. Uses detection to switch on the SAM site closest to the enemy. -- * **Automatic mode** (default since 0.8) can set-up your SAM site network automatically for you -- * **Classic mode** behaves like before @@ -100,9 +101,11 @@ -- * Roland -- * Silkworm (though strictly speaking this is a surface to ship missile) -- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19 --- * and from HDS (see note below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2 +-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2 +-- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M +-- **NOTE** If you are using the Swedish Military Assets (SMA), please note that the **group name** for RBS-SAM types also needs to contain the keyword "SMA" -- --- Following the example started above, an SA-6 site group name should start with "Red SAM SA-6" then, or a blue Patriot installation with e.g. "Blue SAM Patriot". +-- Following the example started above, an SA-6 site group name should start with "Red SAM SA-6" then, or a blue Patriot installation with e.g. "Blue SAM Patriot". -- **NOTE** If you are using the High-Digit-Sam Mod, please note that the **group name** for the following SAM types also needs to contain the keyword "HDS": -- -- * SA-2 (with V759 missile, e.g. "Red SAM SA-2 HDS") @@ -387,6 +390,29 @@ MANTIS.SamDataHDS = { ["HQ-2 HDS"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" }, } +--- SAM data SMA +-- @type MANTIS.SamDataSMA +-- @field #number Range Max firing range in km +-- @field #number Blindspot no-firing range (green circle) +-- @field #number Height Max firing height in km +-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range) +-- @field #string Radar Radar typename on unit level (used as key) +MANTIS.SamDataSMA = { + -- units from SMA Mod (Sweedish Military Assets) + -- https://forum.dcs.world/topic/295202-swedish-military-assets-for-dcs-by-currenthill/ + -- group name MUST contain SMA to ID launcher type correctly! + ["RBS98M SMA"] = { Range=20, Blindspot=0, Height=8, Type="Short", Radar="RBS-98" }, + ["RBS70 SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-70" }, + ["RBS70M SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="BV410_RBS70" }, + ["RBS90 SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-90" }, + ["RBS90M SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="BV410_RBS90" }, + ["RBS103A SMA"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103A" }, + ["RBS103B SMA"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_Rb103B" }, + ["RBS103AM SMA"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" }, + ["RBS103BM SMA"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_HX_Rb103B" }, + ["Lvkv9040M SMA"] = { Range=4, Blindspot=0, Height=2.5, Type="Short", Radar="LvKv9040" }, +} + ----------------------------------------------------------------------- -- MANTIS System ----------------------------------------------------------------------- @@ -550,7 +576,7 @@ do -- TODO Version -- @field #string version - self.version="0.8.8" + self.version="0.8.9" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) --- FSM Functions --- @@ -1270,11 +1296,12 @@ do -- @param #MANTIS self -- @param #string grpname Name of the group -- @param #boolean mod HDS mod flag + -- @param #boolean sma SMA mod flag -- @return #number range Max firing range -- @return #number height Max firing height -- @return #string type Long, medium or short range -- @return #number blind "blind" spot - function MANTIS:_GetSAMDataFromUnits(grpname,mod) + function MANTIS:_GetSAMDataFromUnits(grpname,mod,sma) self:T(self.lid.."_GetSAMRangeFromUnits") local found = false local range = self.checkradius @@ -1287,6 +1314,8 @@ do local SAMData = self.SamData if mod then SAMData = self.SamDataHDS + elseif sma then + SAMData = self.SamDataSMA end --self:I("Looking to auto-match for "..grpname) for _,_unit in pairs(units) do @@ -1332,8 +1361,11 @@ do local blind = 0 local found = false local HDSmod = false + local SMAMod = false if string.find(grpname,"HDS",1,true) then HDSmod = true + elseif string.find(grpname,"SMA",1,true) then + SMAMod = true end if self.automode then for idx,entry in pairs(self.SamData) do @@ -1352,8 +1384,8 @@ do end end -- secondary filter if not found - if (not found and self.automode) or HDSmod then - range, height, type = self:_GetSAMDataFromUnits(grpname,HDSmod) + if (not found and self.automode) or HDSmod or SMAMod then + range, height, type = self:_GetSAMDataFromUnits(grpname,HDSmod,SMAMod) elseif not found then self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname)) end From 5db3775b96c67f32fcc17b8cc926100f3ccb5943 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 21 Oct 2022 08:54:34 +0200 Subject: [PATCH 088/603] #OPERATION * Added GetTargets() #TARGET * Also call Dead() when no targets left over #PLAYERTASKCONTROLLER * Added FSM events for Flaring, Smoking, and Illumination * Added Illumination of targets in menu if it is night * Rename menu parent setting to SetParentMenu(Menu) --- Moose Development/Moose/Ops/Operation.lua | 30 ++++++++-- Moose Development/Moose/Ops/PlayerTask.lua | 70 ++++++++++++++++++++-- Moose Development/Moose/Ops/Target.lua | 10 +++- 3 files changed, 99 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Ops/Operation.lua b/Moose Development/Moose/Ops/Operation.lua index 90455a1d7..c612ae053 100644 --- a/Moose Development/Moose/Ops/Operation.lua +++ b/Moose Development/Moose/Ops/Operation.lua @@ -467,7 +467,7 @@ function OPERATION:SetPhaseConditonOver(Phase, Condition) return self end ---- Add codition function when the given phase is over. Must return a `#boolean`. +--- Add condition function when the given phase is over. Must return a `#boolean`. -- @param #OPERATION self -- @param #OPERATION.Phase Phase The phase. -- @param #function Function Function that needs to be `true`before the phase is over. @@ -739,6 +739,20 @@ function OPERATION:AddTarget(Target, Phase) return self end +--- Add Targets from operation. +-- @param #OPERATION self +-- @param #OPERATION.Phase Phase +-- @return #table Targets Table of #TARGET objects +function OPERATION:GetTargets(Phase) + local N = {} + for _,_target in pairs(self.targets) do + local target=_target --Ops.Target#TARGET + if target:IsAlive() and (Phase==nil or target.phase==Phase) then + table.insert(N,target) + end + end + return N +end --- Count targets alive. -- @param #OPERATION self @@ -896,7 +910,7 @@ function OPERATION:onafterStart(From, Event, To) -- Debug message. self:T(self.lid..string.format("Starting Operation!")) - + return self end @@ -967,6 +981,8 @@ function OPERATION:onafterStatusUpdate(From, Event, To) -- Next status update. self:__StatusUpdate(-30) + + return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -978,7 +994,6 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #OPERATION.Phase Phase The new phase. function OPERATION:onafterPhaseNext(From, Event, To) -- Get next phase. @@ -996,6 +1011,7 @@ function OPERATION:onafterPhaseNext(From, Event, To) end + return self end @@ -1023,6 +1039,7 @@ function OPERATION:onafterPhaseChange(From, Event, To, Phase) -- Phase is active. self:SetPhaseStatus(Phase, OPERATION.PhaseStatus.ACTIVE) + return self end --- On after "BranchSwitch" event. @@ -1038,7 +1055,8 @@ function OPERATION:onafterBranchSwitch(From, Event, To, Branch) -- Set active branch. self.branchActive=Branch - + + return self end --- On after "Over" event. @@ -1062,7 +1080,9 @@ function OPERATION:onafterOver(From, Event, To) local phase=_phase --#OPERATION.Phase self:SetPhaseStatus(phase, OPERATION.PhaseStatus.OVER) end - end + end + + return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index c62c0015a..2019d7177 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -654,7 +654,7 @@ function PLAYERTASK:onafterStatus(From, Event, To) -- Check Target status local targetdead = false - if self.Target:IsDead() or self.Target:IsDestroyed() then + if self.Target:IsDead() or self.Target:IsDestroyed() or self.Target:CountTargets() == 0 then targetdead = true self:__Success(-2) status = "Success" @@ -1397,7 +1397,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.44" +PLAYERTASKCONTROLLER.version="0.1.45" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -1474,6 +1474,9 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self:AddTransition("*", "TaskCancelled", "*") self:AddTransition("*", "TaskSuccess", "*") self:AddTransition("*", "TaskFailed", "*") + self:AddTransition("*", "TaskTargetSmoked", "*") + self:AddTransition("*", "TaskTargetFlared", "*") + self:AddTransition("*", "TaskTargetIlluminated", "*") self:AddTransition("*", "TaskRepeatOnFailed", "*") self:AddTransition("*", "Stop", "Stopped") @@ -1544,6 +1547,30 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) -- @param #string To To state. -- @param Ops.PlayerTask#PLAYERTASK Task + --- On After "TaskTargetSmoked" event. Task smoked. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskTargetSmoked + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task + + --- On After "TaskTargetFlared" event. Task flared. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskTargetFlared + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task + + --- On After "TaskTargetIlluminated" event. Task illuminated. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskTargetIlluminated + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task + end --- [Internal] Init localization @@ -2869,6 +2896,7 @@ function PLAYERTASKCONTROLLER:_SmokeTask(Group, Client) if self.UseSRS then self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) end + self:__TaskTargetSmoked(5,task) else text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end @@ -2897,6 +2925,36 @@ function PLAYERTASKCONTROLLER:_FlareTask(Group, Client) if self.UseSRS then self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) end + self:__TaskTargetFlared(5,task) + else + text = self.gettext:GetEntry("NOACTIVETASK",self.locale) + end + if not self.NoScreenOutput then + local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client) + end + return self +end + +--- [Internal] Illuminate task location +-- @param #PLAYERTASKCONTROLLER self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Client#CLIENT Client +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:_IlluminateTask(Group, Client) + self:T(self.lid.."_IlluminateTask") + local playername, ttsplayername = self:_GetPlayerName(Client) + local text = "" + if self.TasksPerPlayer:HasUniqueID(playername) then + local task = self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK + task:FlareTarget() + local textmark = self.gettext:GetEntry("FLARETASK",self.locale) + text = string.format(textmark, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr) + self:T(self.lid..text) + --local m=MESSAGE:New(text,"10","Tasking"):ToAll() + if self.UseSRS then + self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) + end + self:__TaskTargetIlluminated(5,task) else text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end @@ -2912,7 +2970,7 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_AbortTask(Group, Client) - self:T(self.lid.."_FlareTask") + self:T(self.lid.."_AbortTask") local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" if self.TasksPerPlayer:HasUniqueID(playername) then @@ -3080,6 +3138,10 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) -- no smoking/flaring here if A2A or designer has set noflaresmokemenu to true local smoke = MENU_GROUP_COMMAND_DELAYED:New(group,menusmoke,active,self._SmokeTask,self,group,client) local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client) + local IsNight = client:GetCoordinate():IsNight() + if IsNight then + local light = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._IlluminateTask,self,group,client) + end end end local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client) @@ -3244,7 +3306,7 @@ end -- @param #PLAYERTASKCONTROLLER self -- @param Core.Menu#MENU_MISSION Menu -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:SetParentName(Menu) +function PLAYERTASKCONTROLLER:SetParentMenu(Menu) self:T(self.lid.."SetParentName") self.MenuParent = Menu return self diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 88269f826..bb4b550dd 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -617,7 +617,9 @@ function TARGET:onafterStatus(From, Event, To) -- Log output verbose=1. if self.verbose>=1 then local text=string.format("%s: Targets=%d/%d Life=%.1f/%.1f Damage=%.1f", fsmstate, self:CountTargets(), self.N0, self:GetLife(), self:GetLife0(), self:GetDamage()) - if damaged then + if self:CountTargets() == 0 then + text=text.." Dead!" + elseif damaged then text=text.." Damaged!" end self:I(self.lid..text) @@ -633,7 +635,11 @@ function TARGET:onafterStatus(From, Event, To) end self:I(self.lid..text) end - + + if self:CountTargets() == 0 then + self:Dead() + end + -- Update status again in 30 sec. if self:IsAlive() then self:__Status(-self.TStatus) From a6c9ba2ec8161effd1afb5c353ae1151f6ea80c6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 21 Oct 2022 15:10:14 +0200 Subject: [PATCH 089/603] #Ops - fixes --- Moose Development/Moose/Ops/Awacs.lua | 20 ++++++++++---------- Moose Development/Moose/Ops/OpsGroup.lua | 6 +++--- Moose Development/Moose/Utilities/Utils.lua | 8 ++++++-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 81c850308..e9de1b9b4 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -2092,7 +2092,7 @@ function AWACS:_StartSettings(FlightGroup,Mission) self:Started() elseif self.ShiftChangeAwacsRequested and self.AwacsMissionReplacement and self.AwacsMissionReplacement:GetName() == Mission:GetName() then - self:I("Setting up Awacs Replacement") + self:T("Setting up Awacs Replacement") -- manage AWACS Replacement AwacsFG:SetDefaultRadio(self.Frequency,self.Modulation,false) AwacsFG:SwitchRadio(self.Frequency,self.Modulation) @@ -2962,7 +2962,7 @@ end -- @param Wrapper.Group#GROUP Group Group to use -- @return #AWACS self function AWACS:_ShowAwacsInfo(Group) - self:I(self.lid.."_ShowAwacsInfo") + self:T(self.lid.."_ShowAwacsInfo") local report = REPORT:New("Info") report:Add("====================") report:Add(string.format("AWACS %s",self.callsigntxt)) @@ -3738,7 +3738,7 @@ end -- @param #AWACS self -- @return #AWACS self function AWACS:_DeleteAnchorStackFromMarker(Name,Coord) - self:I(self.lid.."_DeleteAnchorStackFromMarker") + self:T(self.lid.."_DeleteAnchorStackFromMarker") if self.AnchorStacks:HasUniqueID(Name) and self.PlayerStationName == Name then local stack = self.AnchorStacks:ReadByID(Name) -- #AWACS.AnchorData local marker = stack.AnchorMarker @@ -3764,7 +3764,7 @@ end -- @param #AWACS self -- @return #AWACS self function AWACS:_MoveAnchorStackFromMarker(Name,Coord) - self:I(self.lid.."_MoveAnchorStackFromMarker") + self:T(self.lid.."_MoveAnchorStackFromMarker") if self.AnchorStacks:HasUniqueID(Name) and self.PlayerStationName == Name then local station = self.AnchorStacks:PullByID(Name) -- #AWACS.AnchorData local stationtag = string.format("Station: %s\nCoordinate: %s",Name,Coord:ToStringLLDDM()) @@ -3791,7 +3791,7 @@ end -- @param #AWACS self -- @return #AWACS self function AWACS:_CreateAnchorStackFromMarker(Name,Coord) - self:I(self.lid.."_CreateAnchorStackFromMarker") + self:T(self.lid.."_CreateAnchorStackFromMarker") local AnchorStackOne = {} -- #AWACS.AnchorData AnchorStackOne.AnchorBaseAngels = self.AnchorBaseAngels AnchorStackOne.Anchors = FIFO:New() -- Utilities.FiFo#FIFO @@ -5112,7 +5112,7 @@ function AWACS:_CheckAICAPOnStation() self:_ConsistencyCheck() local capmissions, alert5missions, interceptmissions = self:_CleanUpAIMissionStack() - self:I("CAP="..capmissions.." ALERT5="..alert5missions.." Requested="..self.AIRequested) + self:T("CAP="..capmissions.." ALERT5="..alert5missions.." Requested="..self.AIRequested) if self.MaxAIonCAP > 0 then @@ -5147,7 +5147,7 @@ function AWACS:_CheckAICAPOnStation() self.AIRequested = self.AIRequested + 1 local selectedAW = AWS[(((self.AIRequested-1) % availableAWS)+1)] selectedAW:AddMission(mission) - self:I("CAP="..capmissions.." ALERT5="..alert5missions.." Requested="..self.AIRequested) + self:T("CAP="..capmissions.." ALERT5="..alert5missions.." Requested="..self.AIRequested) end end @@ -5693,7 +5693,7 @@ function AWACS:onafterStart(From, Event, To) local MarkerOps = MARKEROPS_BASE:New("AWACS",{"Station","Delete","Move"}) local function Handler(Keywords,Coord,Text) - self:I(Text) + self:T(Text) for _,_word in pairs (Keywords) do if string.lower(_word) == "station" then -- get the station name from the text field @@ -5988,7 +5988,7 @@ end -- @param #string To -- @return #AWACS self function AWACS:onafterStatus(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) self:_SetClientMenus() @@ -6381,7 +6381,7 @@ function AWACS:onafterCheckRadioQueue(From,Event,To) self:T({RadioEntry}) if self.clientset:CountAlive() == 0 then - self:I(self.lid.."No player connected.") + self:T(self.lid.."No player connected.") self:__CheckRadioQueue(-5) return self end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 9d0bc7d4c..54dcedea6 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -10123,7 +10123,7 @@ function OPSGROUP:_CheckDamage() for _,_element in pairs(self.elements) do local element=_element --Ops.OpsGroup#OPSGROUP.Element - if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then + if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then -- Current life points. local life=element.unit:GetLife() @@ -11540,7 +11540,7 @@ end --- Set default Radio frequency and modulation. -- @param #OPSGROUP self -- @param #number Frequency Radio frequency in MHz. Default 251 MHz. --- @param #number Modulation Radio modulation. Default `radio.Modulation.AM`. +-- @param #number Modulation Radio modulation. Default `radio.modulation.AM`. -- @param #boolean OffSwitch If true, radio is OFF by default. -- @return #OPSGROUP self function OPSGROUP:SetDefaultRadio(Frequency, Modulation, OffSwitch) @@ -11569,7 +11569,7 @@ end --- Turn radio on or switch frequency/modulation. -- @param #OPSGROUP self -- @param #number Frequency Radio frequency in MHz. Default is value set in `SetDefaultRadio` (usually 251 MHz). --- @param #number Modulation Radio modulation. Default is value set in `SetDefaultRadio` (usually `radio.Modulation.AM`). +-- @param #number Modulation Radio modulation. Default is value set in `SetDefaultRadio` (usually `radio.modulation.AM`). -- @return #OPSGROUP self function OPSGROUP:SwitchRadio(Frequency, Modulation) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 9f9a3e1aa..756571f43 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1075,10 +1075,14 @@ end -- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @return #number Distance between the vectors. function UTILS.VecDist3D(a, b) - + + local d = math.huge + + if (not a) or (not b) then return d end + local c={x=b.x-a.x, y=b.y-a.y, z=b.z-a.z} - local d=math.sqrt(UTILS.VecDot(c, c)) + d=math.sqrt(UTILS.VecDot(c, c)) return d end From 7731b6c892db2bd9227a3eaa11dd6ce29ef6d505 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 21 Oct 2022 15:15:32 +0200 Subject: [PATCH 090/603] fixes --- Moose Development/Moose/Utilities/Utils.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 756571f43..163d0f34a 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1062,9 +1062,13 @@ end -- @return #number Distance between the vectors. function UTILS.VecDist2D(a, b) + local d = math.huge + + if (not a) or (not b) then return d end + local c={x=b.x-a.x, y=b.y-a.y} - local d=math.sqrt(c.x*c.x+c.y*c.y) + d=math.sqrt(c.x*c.x+c.y*c.y) return d end @@ -1076,6 +1080,7 @@ end -- @return #number Distance between the vectors. function UTILS.VecDist3D(a, b) + local d = math.huge if (not a) or (not b) then return d end From fc829c79c730e8105d289d899ba4287648f5aa1b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 31 Oct 2022 10:25:34 +0100 Subject: [PATCH 091/603] or self:HasAttribute("Modern Tanks") --- Moose Development/Moose/Wrapper/Group.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 749e9d3ad..c0cb7e31e 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1,4 +1,4 @@ ---- **Wrapper** - GROUP wraps the DCS Class Group objects. +----- **Wrapper** - GROUP wraps the DCS Class Group objects. -- -- === -- @@ -2430,7 +2430,7 @@ function GROUP:GetAttribute() local truck=self:HasAttribute("Trucks") and self:GetCategory()==Group.Category.GROUND local infantry=self:HasAttribute("Infantry") local artillery=self:HasAttribute("Artillery") - local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks") + local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks") or self:HasAttribute("Tanks") local aaa=self:HasAttribute("AAA") and (not self:HasAttribute("SAM elements")) local ewr=self:HasAttribute("EWR") local ifv=self:HasAttribute("IFV") From 33f4ace0386c8ecb5a78b73c14d9b41ad69d3ce4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 31 Oct 2022 12:22:59 +0100 Subject: [PATCH 092/603] #AWACS * Added airborne check --- Moose Development/Moose/Ops/Awacs.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index e9de1b9b4..14ccb5922 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -497,7 +497,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.45", -- #string + version = "0.2.46", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -914,7 +914,7 @@ AWACS.TaskStatus = { --@field #boolean FromAI ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO-List 0.2.41 +-- TODO-List 0.2.42 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- -- DONE - WIP - Player tasking, VID @@ -950,6 +950,7 @@ AWACS.TaskStatus = { -- DONE - Anchor Stack Management -- DONE - Shift Length AWACS/AI -- DONE - (WIP) Reporting +-- DONE - Do not report non-airborne groups ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -6232,16 +6233,20 @@ function AWACS:onafterNewCluster(From,Event,To,Cluster) for _,_contact in pairs (table) do local contact = _contact -- Ops.Intelligence#INTEL.Contact if contact and contact.group and contact.group:IsAlive() then - return contact + return contact, contact.group end end return nil end - local Contact = GetFirstAliveContact(ContactTable) -- Ops.Intelligence#INTEL.Contact + local Contact, Group = GetFirstAliveContact(ContactTable) -- Ops.Intelligence#INTEL.Contact if not Contact then return self end + if Group and not Group:IsAirborne() then + return self + end + local targetset = SET_GROUP:New() -- SET for TARGET for _,_grp in pairs(ContactTable) do @@ -6316,7 +6321,7 @@ function AWACS:onafterNewContact(From,Event,To,Contact) for _gid,_mgroup in pairs(self.ManagedGrps) do local managedgroup = _mgroup -- #AWACS.ManagedGroup local group = managedgroup.Group - if group and group:IsAlive() then + if group and group:IsAlive() and group:IsAirborne() then -- contact distance local cpos = Contact.position or Contact.group:GetCoordinate() -- Core.Point#COORDINATE local mpos = group:GetCoordinate() From 85cf78407c7822733af3d68b219a3e4019975e3d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 31 Oct 2022 15:44:50 +0100 Subject: [PATCH 093/603] #EVENT * Added events from 2.8.0 --- Moose Development/Moose/Core/Event.lua | 74 +++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 0b239f2d4..d59f1c244 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -248,6 +248,18 @@ EVENTS = { TriggerZone = world.event.S_EVENT_TRIGGER_ZONE or -1, LandingQualityMark = world.event.S_EVENT_LANDING_QUALITY_MARK or -1, BDA = world.event.S_EVENT_BDA or -1, + -- Added with DCS 2.8.0 + AIAbortMission = world.event.S_EVENT_AI_ABORT_MISSION or -1, + DayNight = world.event.S_EVENT_DAYNIGHT or -1, + FlightTime = world.event.S_EVENT_FLIGHT_TIME or -1, + SelfKillPilot = world.event.S_EVENT_PLAYER_SELF_KILL_PILOT or -1, + PlayerCaptureAirfield = world.event.S_EVENT_PLAYER_CAPTURE_AIRFIELD or -1, + EmergencyLanding = world.event.S_EVENT_EMERGENCY_LANDING or -1, + UnitCreateTask = world.event.S_EVENT_UNIT_CREATE_TASK or -1, + UnitDeleteTask = world.event.S_EVENT_UNIT_DELETE_TASK or -1, + SimulationStart = world.event.S_EVENT_SIMULATION_START or -1, + WeaponRearm = world.event.S_EVENT_WEAPON_REARM or -1, + WeaponDrop = world.event.S_EVENT_WEAPON_DROP or -1, } --- The Event structure @@ -560,9 +572,69 @@ local _EVENTMETA = { Event = "OnEventBDA", Text = "S_EVENT_BDA" }, + -- Added with DCS 2.8 + [EVENTS.AIAbortMission] = { + Order = 1, + Side = "I", + Event = "OnEventAIAbortMission", + Text = "S_EVENT_AI_ABORT_MISSION" + }, + [EVENTS.DayNight] = { + Order = 1, + Event = "OnEventDayNight", + Text = "S_EVENT_DAYNIGHT" + }, + [EVENTS.FlightTime] = { + Order = 1, + Event = "OnEventFlightTime", + Text = "S_EVENT_FLIGHT_TIME" + }, + [EVENTS.SelfKillPilot] = { + Order = 1, + Side = "I", + Event = "OnEventSelfKillPilot", + Text = "S_EVENT_PLAYER_SELF_KILL_PILOT" + }, + [EVENTS.PlayerCaptureAirfield] = { + Order = 1, + Event = "OnEventPlayerCaptureAirfield", + Text = "S_EVENT_PLAYER_CAPTURE_AIRFIELD" + }, + [EVENTS.EmergencyLanding] = { + Order = 1, + Side = "I", + Event = "OnEventEmergencyLanding", + Text = "S_EVENT_EMERGENCY_LANDING" + }, + [EVENTS.UnitCreateTask] = { + Order = 1, + Event = "OnEventUnitCreateTask", + Text = "S_EVENT_UNIT_CREATE_TASK" + }, + [EVENTS.UnitDeleteTask] = { + Order = 1, + Event = "OnEventUnitDeleteTask", + Text = "S_EVENT_UNIT_DELETE_TASK" + }, + [EVENTS.SimulationStart] = { + Order = 1, + Event = "OnEventSimulationStart", + Text = "S_EVENT_SIMULATION_START" + }, + [EVENTS.WeaponRearm] = { + Order = 1, + Side = "I", + Event = "OnEventWeaponRearm", + Text = "S_EVENT_WEAPON_REARM" + }, + [EVENTS.WeaponDrop] = { + Order = 1, + Side = "I", + Event = "OnEventWeaponDrop", + Text = "S_EVENT_WEAPON_DROP" + }, } - --- The Events structure -- @type EVENT.Events -- @field #number IniUnit From feca5acc7e9b72ef6e47a44b318ad4315a661fcd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 31 Oct 2022 16:07:41 +0100 Subject: [PATCH 094/603] #CTLD_HERCULES * Fix for `CTLD_HERCULES:Cargo_Track(cargo, initiator)` when flying very low --- Moose Development/Moose/Ops/CTLD.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index a4a5c945d..c4f12389a 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -4895,7 +4895,7 @@ CTLD_HERCULES = { ClassName = "CTLD_HERCULES", lid = "", Name = "", - Version = "0.0.1", + Version = "0.0.2", } --- Define cargo types. @@ -5306,7 +5306,7 @@ function CTLD_HERCULES:Cargo_Track(cargo, initiator) if self:Check_SurfaceType(cargo.Cargo_Contents) == 2 or self:Check_SurfaceType(cargo.Cargo_Contents) == 3 then cargo.Cargo_over_water = true--pallets gets destroyed in water end - local dcsvec3 = self.ObjectTracker[cargo.Cargo_Contents.id_] -- last known position + local dcsvec3 = self.ObjectTracker[cargo.Cargo_Contents.id_] or initiator:GetVec3() -- last known position self:T("SPAWNPOSITION: ") self:T({dcsvec3}) local Vec2 = { @@ -5409,7 +5409,7 @@ function CTLD_HERCULES:Cargo_Initialize(Initiator, Cargo_Contents, Cargo_Type_na local timer = TIMER:New(self.Cargo_Track,self,self.Cargo[self.j],Initiator) self.Cargo[self.j].scheduleFunctionID = timer - timer:Start(5,2,600) + timer:Start(1,1,600) else -- no paras @@ -5434,7 +5434,7 @@ function CTLD_HERCULES:Cargo_Initialize(Initiator, Cargo_Contents, Cargo_Type_na local timer = TIMER:New(self.Cargo_Track,self,self.Cargo[self.j],Initiator) self.Cargo[self.j].scheduleFunctionID = timer - timer:Start(5,2,600) + timer:Start(1,1,600) end end return self From 4b0d48922d7e527b3f1d79c80504edae956b71ed Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 1 Nov 2022 14:30:09 +0100 Subject: [PATCH 095/603] #CHIEF, CTLD, UNIT --- Moose Development/Moose/Ops/CTLD.lua | 31 ++++++++++++++++++------ Moose Development/Moose/Ops/Chief.lua | 3 ++- Moose Development/Moose/Wrapper/Unit.lua | 2 ++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index c4f12389a..bd52b2742 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -699,6 +699,7 @@ do -- my_ctld.smokedistance = 2000 -- Only smoke or flare zones if requesting player unit is this far away (in meters) -- my_ctld.suppressmessages = false -- Set to true if you want to script your own messages. -- my_ctld.repairtime = 300 -- Number of seconds it takes to repair a unit. +-- my_ctld.buildtime = 300 -- Number of seconds it takes to build a unit. Set to zero or nil to build instantly. -- my_ctld.cratecountry = country.id.GERMANY -- ID of crates. Will default to country.id.RUSSIA for RED coalition setups. -- my_ctld.allowcratepickupagain = true -- allow re-pickup crates that were dropped. -- my_ctld.enableslingload = false -- allow cargos to be slingloaded - might not work for all cargo types @@ -708,6 +709,7 @@ do -- my_ctld.basetype = "container_cargo" -- default shape of the cargo container -- my_ctld.droppedbeacontimeout = 600 -- dropped beacon lasts 10 minutes -- my_ctld.usesubcats = false -- use sub-category names for crates, adds an extra menu layer in "Get Crates", useful if you have > 10 crate types. +-- my_ctld.placeCratesAhead = false -- place crates straight ahead of the helicopter, in a random way. If true, crates are more neatly sorted. -- -- ## 2.1 User functions -- @@ -1074,7 +1076,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.16" +CTLD.version="1.0.17" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1212,8 +1214,9 @@ function CTLD:New(Coalition, Prefixes, Alias) -- message suppression self.suppressmessages = false - -- time to repair a unit/group + -- time to repairor build a unit/group self.repairtime = 300 + self.buildtime = 300 -- place spawned crates in front of aircraft self.placeCratesAhead = false @@ -2807,7 +2810,13 @@ function CTLD:_BuildCrates(Group, Unit,Engineering) local build = _build -- #CTLD.Buildable if build.CanBuild then self:_CleanUpCrates(crates,build,number) - self:_BuildObjectFromCrates(Group,Unit,build) + if self.buildtime and self.buildtime > 0 then + local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate()) + buildtimer:Start(self.buildtime) + self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) + else + self:_BuildObjectFromCrates(Group,Unit,build) + end end end end @@ -2906,13 +2915,13 @@ end -- @param Wrapper.Group#UNIT Unit -- @param #CTLD.Buildable Build -- @param #boolean Repair If true this is a repair and not a new build --- @param Core.Point#COORDINATE Coordinate Location for repair (e.g. where the destroyed unit was) +-- @param Core.Point#COORDINATE RepairLocation Location for repair (e.g. where the destroyed unit was) function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) self:T(self.lid .. " _BuildObjectFromCrates") -- Spawn-a-crate-content - if Group and Group:IsAlive() then - local position = Unit:GetCoordinate() or Group:GetCoordinate() - local unitname = Unit:GetName() or Group:GetName() + if Group and Group:IsAlive() or (RepairLocation and not Repair) then + --local position = Unit:GetCoordinate() or Group:GetCoordinate() + --local unitname = Unit:GetName() or Group:GetName() or "Unknown" local name = Build.Name local ctype = Build.Type -- #CTLD_CARGO.Enum local canmove = false @@ -2924,7 +2933,13 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) if type(temptable) == "string" then temptable = {temptable} end - local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,100) + local zone = nil + if RepairLocation and not Repair then + -- timed build + zone = ZONE_RADIUS:New(string.format("Build zone-%d",math.random(1,10000)),RepairLocation:GetVec2(),100) + else + zone = ZONE_GROUP:New(string.format("Unload zone-%d",math.random(1,10000)),Group,100) + end --local randomcoord = zone:GetRandomCoordinate(35):GetVec2() local randomcoord = Build.Coord or zone:GetRandomCoordinate(35):GetVec2() if Repair then diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 53f54a891..9d6325a4c 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -2710,7 +2710,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) -- Tanks - table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.CAS, 100)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.CAS, 90)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARMORATTACK, 40)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 826f4b160..59474de9c 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -361,6 +361,8 @@ function UNIT:IsPlayer() -- Get group. local group=self:GetGroup() + + if not group then return false end -- Units of template group. local units=group:GetTemplate().units From 3ba981a89055f3e328a603ae9d0e4a9ace72c9bf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 1 Nov 2022 16:32:03 +0100 Subject: [PATCH 096/603] #ZONE_POLYGON * Scan for Scenery - changed scan strategy as box seems not to work properly all the time --- Moose Development/Moose/Core/Zone.lua | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index cbd8344fc..5f89fdaed 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2451,14 +2451,30 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories ) local minVec3 = {x=vectors.x1, y=0, z=vectors.y1} local maxVec3 = {x=vectors.x2, y=0, z=vectors.y2} + local minmarkcoord = COORDINATE:NewFromVec3(minVec3) + local maxmarkcoord = COORDINATE:NewFromVec3(maxVec3) + local ZoneRadius = minmarkcoord:Get2DDistance(maxmarkcoord)/2 + + local CenterVec3 = self:GetCoordinate():GetVec3() + + --[[ this a bit shaky in functionality it seems local VolumeBox = { id = world.VolumeType.BOX, params = { min = minVec3, max = maxVec3 } - } + } + --]] + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = CenterVec3, + radius = ZoneRadius, + } + } + local function EvaluateZone( ZoneObject ) if ZoneObject then @@ -2500,7 +2516,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories ) end -- trying with box search - if ObjectCategory == Object.Category.SCENERY then + if ObjectCategory == Object.Category.SCENERY and self:IsVec3InZone(ZoneObject:getPoint()) then local SceneryType = ZoneObject:getTypeName() local SceneryName = ZoneObject:getName() self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {} @@ -2543,7 +2559,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories ) if searchscenery then -- Search objects. - world.searchObjects({Object.Category.SCENERY}, VolumeBox, EvaluateZone ) + world.searchObjects({Object.Category.SCENERY}, SphereSearch, EvaluateZone ) end end From 0839055b7efe99d3228280a30e5b03237ff09ad4 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 2 Nov 2022 11:24:40 +0100 Subject: [PATCH 097/603] Documentation fixes. (#1820) (#1822) * Documentation fixes. * Update Airboss.lua Escape links. Co-authored-by: TommyC81 --- Moose Development/Moose/Ops/Airboss.lua | 20 +++++++++---------- Moose Development/Moose/Sound/Radio.lua | 10 +++++----- .../Moose/Wrapper/Controllable.lua | 4 ++-- .../Moose/Wrapper/Positionable.lua | 16 +++++++-------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 256c156a4..a4166b27f 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -27,17 +27,17 @@ -- **Supported Carriers:** -- -- * [USS John C. Stennis](https://en.wikipedia.org/wiki/USS_John_C._Stennis) (CVN-74) --- * [USS Theodore Roosevelt](https://en.wikipedia.org/wiki/USS_Theodore_Roosevelt_(CVN-71)) (CVN-71) [Super Carrier Module] --- * [USS Abraham Lincoln](https://en.wikipedia.org/wiki/USS_Abraham_Lincoln_(CVN-72)) (CVN-72) [Super Carrier Module] --- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module] +-- * [USS Theodore Roosevelt](https://en.wikipedia.org/wiki/USS_Theodore_Roosevelt_(CVN-71\)) (CVN-71) [Super Carrier Module] +-- * [USS Abraham Lincoln](https://en.wikipedia.org/wiki/USS_Abraham_Lincoln_(CVN-72\)) (CVN-72) [Super Carrier Module] +-- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73\)) (CVN-73) [Super Carrier Module] -- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module] --- * [USS Forrestal](https://en.wikipedia.org/wiki/USS_Forrestal_(CV-59)) (CV-59) [Heatblur Carrier Module] --- * [HMS Hermes](https://en.wikipedia.org/wiki/HMS_Hermes_(R12)) (R12) [**WIP**] --- * [HMS Invincible](https://en.wikipedia.org/wiki/HMS_Invincible_(R05) (R05) [**WIP**] --- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**] --- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6)) (LHA-6) [**WIP**] +-- * [USS Forrestal](https://en.wikipedia.org/wiki/USS_Forrestal_(CV-59\)) (CV-59) [Heatblur Carrier Module] +-- * [HMS Hermes](https://en.wikipedia.org/wiki/HMS_Hermes_(R12\)) (R12) [**WIP**] +-- * [HMS Invincible](https://en.wikipedia.org/wiki/HMS_Invincible_(R05\)) (R05) [**WIP**] +-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1\)) (LHA-1) [**WIP**] +-- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6\)) (LHA-6) [**WIP**] -- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61) [**WIP**] --- * [HMAS Canberra](https://en.wikipedia.org/wiki/HMAS_Canberra_(L02)) (L02) [**WIP**] +-- * [HMAS Canberra](https://en.wikipedia.org/wiki/HMAS_Canberra_(L02\)) (L02) [**WIP**] -- -- **Supported Aircraft:** -- @@ -143,7 +143,7 @@ -- @field Wrapper.Airbase#AIRBASE airbase Carrier airbase object. -- @field #table waypoints Waypoint coordinates of carrier. -- @field #number currentwp Current waypoint, i.e. the one that has been passed last. --- @field Core.Radio#BEACON beacon Carrier beacon for TACAN and ICLS. +-- @field Core.Beacon#BEACON beacon Carrier beacon for TACAN and ICLS. -- @field #boolean TACANon Automatic TACAN is activated. -- @field #number TACANchannel TACAN channel. -- @field #string TACANmode TACAN mode, i.e. "X" or "Y". diff --git a/Moose Development/Moose/Sound/Radio.lua b/Moose Development/Moose/Sound/Radio.lua index 3f4aadcf3..56eb8ddf0 100644 --- a/Moose Development/Moose/Sound/Radio.lua +++ b/Moose Development/Moose/Sound/Radio.lua @@ -9,13 +9,13 @@ -- What are radio communications in DCS? -- -- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM), --- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**. +-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmitter's antenna can be set, and the transmission can be **looped**. -- -- How to supply DCS my own Sound Files? -- -- * Your sound files need to be encoded in **.ogg** or .wav, -- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings, --- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), +-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (which can be decompressed like a .zip file), -- * For simplicity sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. -- -- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Wrapper.Positionable#POSITIONABLE} @@ -26,7 +26,7 @@ -- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, -- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below). -- If an FC3 aircraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircraft isn't compatible, --- you won't hear/be able to use the TACAN beacon informations. +-- you won't hear/be able to use the TACAN beacon information. -- -- === -- @@ -103,7 +103,7 @@ RADIO = { function RADIO:New(Positionable) -- Inherit base - local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO + local self = BASE:Inherit( self, BASE:New() ) -- Sound.Radio#RADIO self:F(Positionable) if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid @@ -375,7 +375,7 @@ end --- Stops a transmission --- This function is especially usefull to stop the broadcast of looped transmissions +-- This function is especially useful to stop the broadcast of looped transmissions -- @param #RADIO self -- @return #RADIO self function RADIO:StopBroadcast() diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index d9fc41dd5..d93cd50ab 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -627,8 +627,8 @@ end -- For specific beacons like TACAN use the more convenient @{#BEACON} class. -- Note that a controllable can only have one beacon activated at a time with the execption of ICLS. -- @param #CONTROLLABLE self --- @param Core.Radio#BEACON.Type Type Beacon type (VOR, DME, TACAN, RSBN, ILS etc). --- @param Core.Radio#BEACON.System System Beacon system (VOR, DME, TACAN, RSBN, ILS etc). +-- @param Core.Beacon#BEACON.Type Type Beacon type (VOR, DME, TACAN, RSBN, ILS etc). +-- @param Core.Beacon#BEACON.System System Beacon system (VOR, DME, TACAN, RSBN, ILS etc). -- @param #number Frequency Frequency in Hz the beacon is running on. Use @{#UTILS.TACANToFrequency} to generate a frequency for TACAN beacons. -- @param #number UnitID The ID of the unit the beacon is attached to. Useful if more units are in one group. -- @param #number Channel Channel the beacon is using. For, e.g. TACAN beacons. diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index a40edbb76..72f9a9fce 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -11,7 +11,7 @@ -- @module Wrapper.Positionable -- @image Wrapper_Positionable.JPG ---- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) +--- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used internally by the moose designer :-) -- @extends Wrapper.Identifiable#IDENTIFIABLE --- @type POSITIONABLE @@ -1366,24 +1366,24 @@ function POSITIONABLE:Message( Message, Duration, Name ) return nil end ---- Create a @{Core.Radio#RADIO}, to allow radio transmission for this POSITIONABLE. +--- Create a @{Sound.Radio#RADIO}, to allow radio transmission for this POSITIONABLE. -- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message -- @param #POSITIONABLE self --- @return Core.Radio#RADIO Radio +-- @return Sound.Radio#RADIO Radio function POSITIONABLE:GetRadio() self:F2( self ) return RADIO:New( self ) end ---- Create a @{Core.Radio#BEACON}, to allow this POSITIONABLE to broadcast beacon signals +--- Create a @{Core.Beacon#BEACON}, to allow this POSITIONABLE to broadcast beacon signals. -- @param #POSITIONABLE self --- @return Core.Radio#RADIO Radio +-- @return Core.Beacon#BEACON Beacon function POSITIONABLE:GetBeacon() self:F2( self ) return BEACON:New( self ) end ---- Start Lasing a POSITIONABLE +--- Start Lasing a POSITIONABLE. -- @param #POSITIONABLE self -- @param #POSITIONABLE Target The target to lase. -- @param #number LaserCode Laser code or random number in [1000, 9999]. @@ -1424,7 +1424,7 @@ function POSITIONABLE:LaseCoordinate( Coordinate, LaserCode, Duration ) return self.Spot end ---- Stop Lasing a POSITIONABLE +--- Stop Lasing a POSITIONABLE. -- @param #POSITIONABLE self -- @return #POSITIONABLE function POSITIONABLE:LaseOff() @@ -1438,7 +1438,7 @@ function POSITIONABLE:LaseOff() return self end ---- Check if the POSITIONABLE is lasing a target +--- Check if the POSITIONABLE is lasing a target. -- @param #POSITIONABLE self -- @return #boolean true if it is lasing a target function POSITIONABLE:IsLasing() From 0c35cd9680afef9885be01fdd196b7721363bbdf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 2 Nov 2022 17:31:26 +0100 Subject: [PATCH 098/603] #CHIEF * Fix to auto-create CAS/CAS-Enhanced Missions from detected targets; Added BAI mission option for TANKS as target --- Moose Development/Moose/Ops/Auftrag.lua | 4 ++++ Moose Development/Moose/Ops/Chief.lua | 1 + 2 files changed, 5 insertions(+) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 895aaa387..fa10e8434 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -2227,6 +2227,10 @@ function AUFTRAG:NewFromTarget(Target, MissionType) mission=self:NewBOMBING(Target, Altitude) elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then mission=self:NewBOMBRUNWAY(Target, Altitude) + elseif MissionType==AUFTRAG.Type.CAS then + mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,Target:GetAverageCoordinate(),Heading,Leg,TargetTypes) + elseif MissionType==AUFTRAG.Type.CASENHANCED then + mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,RangeMax,NoEngageZoneSet,TargetTypes) elseif MissionType==AUFTRAG.Type.INTERCEPT then mission=self:NewINTERCEPT(Target) elseif MissionType==AUFTRAG.Type.SEAD then diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 9d6325a4c..5433a00a4 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -2712,6 +2712,7 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.CAS, 90)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.CASENHANCED, 90)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARMORATTACK, 40)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) From ce7a86842fee7d6b7a2859649dd8cc896e34b4a6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 4 Nov 2022 13:31:03 +0100 Subject: [PATCH 099/603] #INTEL --- Moose Development/Moose/Ops/Intelligence.lua | 29 +++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index 28437a98e..fe92965cb 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -159,13 +159,13 @@ INTEL.Ctype={ --- INTEL class version. -- @field #string version -INTEL.version="0.3.3" +INTEL.version="0.3.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Make forget times user inpupt. Currently these are hard coded. +-- TODO: Make forget times user input. Currently these are hard coded. -- TODO: Add min cluster size. Only create new clusters if they have a certain group size. -- TODO: process detected set asynchroniously for better performance. -- DONE: Add statics. @@ -1686,8 +1686,8 @@ function INTEL:CalcClusterDirection(cluster) -- Second group is going East, i.e. heading 270 -- Total is 360/2=180, i.e. South! -- It should not go anywhere as the two movements cancel each other. - -- Correct, edge case for N=2^x, but when 2 pairs of groups drive in exact opposite directions, the cluster will split at some point? - -- maybe add the speed as weight to get a weighted factor + -- Apple - Correct, edge case for N=2^x, but when 2 pairs of groups drive in exact opposite directions, the cluster will split at some point? + -- maybe add the speed as weight to get a weighted factor: if n==0 then return 0 @@ -2154,6 +2154,27 @@ function INTEL:UpdateClusterMarker(cluster) return self end +--- Get the contact with the highest threat level from the cluster. +-- @param #INTEL self +-- @param #INTEL.Cluster Cluster The cluster. +-- @return #INTEL.Contact the contact or nil if none +function INTEL:GetHighestThreatContact(Cluster) + local threatlevel=-1 + local rcontact = nil + + for _,_contact in pairs(Cluster.Contacts) do + + local contact=_contact --Ops.Intelligence#INTEL.Contact + + if contact.threatlevel>threatlevel then + threatlevel=contact.threatlevel + rcontact = contact + end + + end + return rcontact +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 2eea8520d8adc6a2f2d68c9929afbed628dc5add Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 7 Nov 2022 11:23:39 +0100 Subject: [PATCH 100/603] Merge CTLD Bronco update into master (#1829) * Update Range.lua (#1825) Add meter text to RANGE bombing result. * CTLD - added Bronco (#1828) Added data for the Bronco-OV-10A - needs further additions to ensure to work, as this is a plane, not a chopper #1827 Co-authored-by: TommyC81 --- Moose Development/Moose/Functional/Range.lua | 2 +- Moose Development/Moose/Ops/CTLD.lua | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 15f8b148b..32052f218 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -2259,7 +2259,7 @@ function RANGE:onafterImpact( From, Event, To, result, player ) end -- Send message to player. - local text = string.format( "%s, impact %03d° for %d ft", player.playername, result.radial, UTILS.MetersToFeet( result.distance ) ) + local text = string.format( "%s, impact %03d° for %d ft (%d m)", player.playername, result.radial, UTILS.MetersToFeet( result.distance ), result.distance ) if targetname then text = text .. string.format( " from bulls of target %s.", targetname ) else diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index bd52b2742..d18e4d32f 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1072,11 +1072,12 @@ CTLD.UnitTypes = { --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 -} + ["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450}, + } --- CTLD class version. -- @field #string version -CTLD.version="1.0.17" +CTLD.version="1.0.18" --- Instantiate a new CTLD. -- @param #CTLD self From c08d54c16b3cbd95920f7db1a8172b24ecc160af Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 7 Nov 2022 14:11:16 +0100 Subject: [PATCH 101/603] CTLD - Fix Ship Zones CTLD - Fix Ship Zones --- Moose Development/Moose/Ops/CTLD.lua | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index d18e4d32f..c655dd3a4 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1072,8 +1072,8 @@ CTLD.UnitTypes = { --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 - ["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450}, - } + ["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450}, +} --- CTLD class version. -- @field #string version @@ -3423,11 +3423,19 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship self:T(self.lid .. " AddCTLDZone") local zone = ZONE:FindByName(Name) - if not zone then + if not zone and Type ~= CTLD.CargoZoneType.SHIP then self:E(self.lid.."**** Zone does not exist: "..Name) return self end + if Type == CTLD.CargoZoneType.SHIP then + local Ship = UNIT:FindByName(Name) + if not Ship then + self:E(self.lid.."**** Ship does not exist: "..Name) + return self + end + end + local ctldzone = {} -- #CTLD.CargoZone ctldzone.active = Active or false ctldzone.color = Color or SMOKECOLOR.Red @@ -3685,21 +3693,22 @@ function CTLD:IsUnitInZone(Unit,Zonetype) local zonewidth = 20 if Zonetype == CTLD.CargoZoneType.SHIP then self:T("Checking Type Ship: "..zonename) - zone = UNIT:FindByName(zonename) + ZoneUNIT = UNIT:FindByName(zonename) zonecoord = zone:GetCoordinate() zoneradius = czone.shiplength zonewidth = czone.shipwidth + zone = ZONE_UNIT:New( ZoneUNIT:GetName(), ZoneUNIT, zoneradius/2) elseif ZONE:FindByName(zonename) then zone = ZONE:FindByName(zonename) self:T("Checking Zone: "..zonename) zonecoord = zone:GetCoordinate() - zoneradius = 1500 + --zoneradius = 1500 zonewidth = zoneradius elseif AIRBASE:FindByName(zonename) then zone = AIRBASE:FindByName(zonename):GetZone() self:T("Checking Zone: "..zonename) zonecoord = zone:GetCoordinate() - zoneradius = 2500 + zoneradius = 2000 zonewidth = zoneradius end local distance = self:_GetDistance(zonecoord,unitcoord) From 63aff9dcd182a2a92113fb6e0d708805264191ec Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 7 Nov 2022 19:12:36 +0100 Subject: [PATCH 102/603] #CTLD Fix Ship Zones --- Moose Development/Moose/Ops/CTLD.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index ac379e079..bb5a86982 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3692,11 +3692,11 @@ function CTLD:IsUnitInZone(Unit,Zonetype) local zonewidth = 20 if Zonetype == CTLD.CargoZoneType.SHIP then self:T("Checking Type Ship: "..zonename) - ZoneUNIT = UNIT:FindByName(zonename) - zonecoord = zone:GetCoordinate() + local ZoneUNIT = UNIT:FindByName(zonename) + zonecoord = ZoneUNIT:GetCoordinate() zoneradius = czone.shiplength zonewidth = czone.shipwidth - zone = ZONE_UNIT:New( ZoneUNIT:GetName(), ZoneUNIT, zoneradius/2) + zone = ZONE_UNIT:New( ZoneUNIT:GetName(), ZoneUNIT, zoneradius/2) elseif ZONE:FindByName(zonename) then zone = ZONE:FindByName(zonename) self:T("Checking Zone: "..zonename) From b057e838984efd891d8dc0ee7c75f7bc0eb465fc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 8 Nov 2022 15:43:00 +0100 Subject: [PATCH 103/603] * POINT/PLAYERTASK * Added option to get BR/BRA with add'l magnetic heading --- Moose Development/Moose/Core/Point.lua | 106 +++++++++++++-------- Moose Development/Moose/Ops/PlayerTask.lua | 31 +++++- 2 files changed, 91 insertions(+), 46 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 79a274918..fe4863f54 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1111,15 +1111,25 @@ do -- COORDINATE -- @param #number AngleRadians The angle in randians. -- @param #number Precision The precision. -- @param Core.Settings#SETTINGS Settings + -- @param #boolean MagVar If true, include magentic degrees -- @return #string The bearing text in degrees. - function COORDINATE:GetBearingText( AngleRadians, Precision, Settings, Language ) + function COORDINATE:GetBearingText( AngleRadians, Precision, Settings, MagVar ) local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS local AngleDegrees = UTILS.Round( UTILS.ToDegree( AngleRadians ), Precision ) local s = string.format( '%03d°', AngleDegrees ) - + + if MagVar then + local variation = UTILS.GetMagneticDeclination() or 0 + local AngleMagnetic = AngleDegrees - variation + + if AngleMagnetic < 0 then AngleMagnetic = 360-AngleMagnetic end + + s = string.format( '%03d°M|%03d°', AngleMagnetic,AngleDegrees ) + end + return s end @@ -1133,21 +1143,22 @@ do -- COORDINATE function COORDINATE:GetDistanceText( Distance, Settings, Language, Precision ) local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS - local Language = Language or "EN" + local Language = Language or Settings.Locale or _SETTINGS.Locale or "EN" + Language = string.lower(Language) local Precision = Precision or 0 local DistanceText if Settings:IsMetric() then - if Language == "EN" then + if Language == "en" then DistanceText = " for " .. UTILS.Round( Distance / 1000, Precision ) .. " km" - elseif Language == "RU" then + elseif Language == "ru" then DistanceText = " за " .. UTILS.Round( Distance / 1000, Precision ) .. " километров" end else - if Language == "EN" then + if Language == "en" then DistanceText = " for " .. UTILS.Round( UTILS.MetersToNM( Distance ), Precision ) .. " miles" - elseif Language == "RU" then + elseif Language == "ru" then DistanceText = " за " .. UTILS.Round( UTILS.MetersToNM( Distance ), Precision ) .. " миль" end end @@ -1161,19 +1172,21 @@ do -- COORDINATE function COORDINATE:GetAltitudeText( Settings, Language ) local Altitude = self.y local Settings = Settings or _SETTINGS - local Language = Language or "EN" - + local Language = Language or Settings.Locale or _SETTINGS.Locale or "EN" + + Language = string.lower(Language) + if Altitude ~= 0 then if Settings:IsMetric() then - if Language == "EN" then + if Language == "en" then return " at " .. UTILS.Round( self.y, -3 ) .. " meters" - elseif Language == "RU" then + elseif Language == "ru" then return " в " .. UTILS.Round( self.y, -3 ) .. " метры" end else - if Language == "EN" then + if Language == "en" then return " at " .. UTILS.Round( UTILS.MetersToFeet( self.y ), -3 ) .. " feet" - elseif Language == "RU" then + elseif Language == "ru" then return " в " .. UTILS.Round( self.y, -3 ) .. " ноги" end end @@ -1220,12 +1233,14 @@ do -- COORDINATE -- @param #number AngleRadians The angle in randians -- @param #number Distance The distance -- @param Core.Settings#SETTINGS Settings + -- @param #string Language (Optional) Language "en" or "ru" + -- @param #boolean MagVar If true, also state angle in magnetic -- @return #string The BR Text - function COORDINATE:GetBRText( AngleRadians, Distance, Settings, Language ) + function COORDINATE:GetBRText( AngleRadians, Distance, Settings, Language, MagVar ) local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS - local BearingText = self:GetBearingText( AngleRadians, 0, Settings, Language ) + local BearingText = self:GetBearingText( AngleRadians, 0, Settings, MagVar ) local DistanceText = self:GetDistanceText( Distance, Settings, Language, 0 ) local BRText = BearingText .. DistanceText @@ -1238,12 +1253,14 @@ do -- COORDINATE -- @param #number AngleRadians The angle in randians -- @param #number Distance The distance -- @param Core.Settings#SETTINGS Settings + -- @param #string Language (Optional) Language "en" or "ru" + -- @param #boolean MagVar If true, also state angle in magnetic -- @return #string The BRA Text - function COORDINATE:GetBRAText( AngleRadians, Distance, Settings, Language ) + function COORDINATE:GetBRAText( AngleRadians, Distance, Settings, Language, MagVar ) local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS - local BearingText = self:GetBearingText( AngleRadians, 0, Settings, Language ) + local BearingText = self:GetBearingText( AngleRadians, 0, Settings, MagVar ) local DistanceText = self:GetDistanceText( Distance, Settings, Language, 0 ) local AltitudeText = self:GetAltitudeText( Settings, Language ) @@ -2755,25 +2772,27 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from. -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @param #boolean MagVar If true, also get angle in MagVar for BR/BRA -- @return #string The BR text. - function COORDINATE:ToStringBR( FromCoordinate, Settings ) + function COORDINATE:ToStringBR( FromCoordinate, Settings, MagVar ) local DirectionVec3 = FromCoordinate:GetDirectionVec3( self ) local AngleRadians = self:GetAngleRadians( DirectionVec3 ) local Distance = self:Get2DDistance( FromCoordinate ) - return "BR, " .. self:GetBRText( AngleRadians, Distance, Settings ) + return "BR, " .. self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar ) end --- Return a BRA string from a COORDINATE to the COORDINATE. -- @param #COORDINATE self -- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from. -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @param #boolean MagVar If true, also get angle in MagVar for BR/BRA -- @return #string The BR text. - function COORDINATE:ToStringBRA( FromCoordinate, Settings, Language ) + function COORDINATE:ToStringBRA( FromCoordinate, Settings, MagVar ) local DirectionVec3 = FromCoordinate:GetDirectionVec3( self ) local AngleRadians = self:GetAngleRadians( DirectionVec3 ) local Distance = FromCoordinate:Get2DDistance( self ) local Altitude = self:GetAltitudeText() - return "BRA, " .. self:GetBRAText( AngleRadians, Distance, Settings, Language ) + return "BRA, " .. self:GetBRAText( AngleRadians, Distance, Settings, nil, MagVar ) end --- Create a BRAA NATO call string to this COORDINATE from the FromCOORDINATE. Note - BRA delivered if no aspect can be obtained and "Merged" if range < 3nm @@ -2868,14 +2887,15 @@ do -- COORDINATE -- @param #COORDINATE self -- @param DCS#coalition.side Coalition The coalition. -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @param #boolean MagVar If true, als get angle in magnetic -- @return #string The BR text. - function COORDINATE:ToStringBULLS( Coalition, Settings ) + function COORDINATE:ToStringBULLS( Coalition, Settings, MagVar ) local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( Coalition ) ) local DirectionVec3 = BullsCoordinate:GetDirectionVec3( self ) local AngleRadians = self:GetAngleRadians( DirectionVec3 ) local Distance = self:Get2DDistance( BullsCoordinate ) local Altitude = self:GetAltitudeText() - return "BULLS, " .. self:GetBRText( AngleRadians, Distance, Settings ) + return "BULLS, " .. self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar ) end --- Return an aspect string from a COORDINATE to the Angle of the object. @@ -2939,7 +2959,7 @@ do -- COORDINATE -- @param #COORDINATE self -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @return #string The MGRS Text - function COORDINATE:ToStringMGRS( Settings ) --R2.1 Fixes issue #424. + function COORDINATE:ToStringMGRS( Settings ) local MGRS_Accuracy = Settings and Settings.MGRS_Accuracy or _SETTINGS.MGRS_Accuracy local lat, lon = coord.LOtoLL( self:GetVec3() ) @@ -2955,8 +2975,9 @@ do -- COORDINATE -- @param #string ReferenceName The reference name. -- @param Wrapper.Controllable#CONTROLLABLE Controllable -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @param #boolean MagVar If true also show angle in magnetic -- @return #string The coordinate Text in the configured coordinate system. - function COORDINATE:ToStringFromRP( ReferenceCoord, ReferenceName, Controllable, Settings ) + function COORDINATE:ToStringFromRP( ReferenceCoord, ReferenceName, Controllable, Settings, MagVar ) self:F2( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } ) @@ -2968,12 +2989,12 @@ do -- COORDINATE local DirectionVec3 = ReferenceCoord:GetDirectionVec3( self ) local AngleRadians = self:GetAngleRadians( DirectionVec3 ) local Distance = self:Get2DDistance( ReferenceCoord ) - return "Targets are the last seen " .. self:GetBRText( AngleRadians, Distance, Settings ) .. " from " .. ReferenceName + return "Targets are the last seen " .. self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar ) .. " from " .. ReferenceName else local DirectionVec3 = ReferenceCoord:GetDirectionVec3( self ) local AngleRadians = self:GetAngleRadians( DirectionVec3 ) local Distance = self:Get2DDistance( ReferenceCoord ) - return "Target are located " .. self:GetBRText( AngleRadians, Distance, Settings ) .. " from " .. ReferenceName + return "Target are located " .. self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar ) .. " from " .. ReferenceName end return nil @@ -2988,8 +3009,9 @@ do -- COORDINATE -- @param #string ReferenceName The reference name. -- @param Wrapper.Controllable#CONTROLLABLE Controllable -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @param #boolean MagVar If true also get the angle as magnetic -- @return #string The coordinate Text in the configured coordinate system. - function COORDINATE:ToStringFromRPShort( ReferenceCoord, ReferenceName, Controllable, Settings ) + function COORDINATE:ToStringFromRPShort( ReferenceCoord, ReferenceName, Controllable, Settings, MagVar ) self:F2( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } ) @@ -3001,12 +3023,12 @@ do -- COORDINATE local DirectionVec3 = ReferenceCoord:GetDirectionVec3( self ) local AngleRadians = self:GetAngleRadians( DirectionVec3 ) local Distance = self:Get2DDistance( ReferenceCoord ) - return self:GetBRText( AngleRadians, Distance, Settings ) .. " from " .. ReferenceName + return self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar ) .. " from " .. ReferenceName else local DirectionVec3 = ReferenceCoord:GetDirectionVec3( self ) local AngleRadians = self:GetAngleRadians( DirectionVec3 ) local Distance = self:Get2DDistance( ReferenceCoord ) - return self:GetBRText( AngleRadians, Distance, Settings ) .. " from " .. ReferenceName + return self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar ) .. " from " .. ReferenceName end return nil @@ -3017,8 +3039,9 @@ do -- COORDINATE -- @param #COORDINATE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @param #boolean MagVar If true, also get angle in MagVar for BR/BRA -- @return #string The coordinate Text in the configured coordinate system. - function COORDINATE:ToStringA2G( Controllable, Settings ) + function COORDINATE:ToStringA2G( Controllable, Settings, MagVar ) self:F2( { Controllable = Controllable and Controllable:GetName() } ) @@ -3028,7 +3051,7 @@ do -- COORDINATE -- If no Controllable is given to calculate the BR from, then MGRS will be used!!! if Controllable then local Coordinate = Controllable:GetCoordinate() - return Controllable and self:ToStringBR( Coordinate, Settings ) or self:ToStringMGRS( Settings ) + return Controllable and self:ToStringBR( Coordinate, Settings, MagVar ) or self:ToStringMGRS( Settings ) else return self:ToStringMGRS( Settings ) end @@ -3052,33 +3075,34 @@ do -- COORDINATE -- @param #COORDINATE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @param #boolean MagVar If true, also get angle in MagVar for BR/BRA -- @return #string The coordinate Text in the configured coordinate system. - function COORDINATE:ToStringA2A( Controllable, Settings, Language ) -- R2.2 + function COORDINATE:ToStringA2A( Controllable, Settings, MagVar ) self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS - if Settings:IsA2A_BRAA() then + if Settings:IsA2A_BRAA() then if Controllable then local Coordinate = Controllable:GetCoordinate() - return self:ToStringBRA( Coordinate, Settings, Language ) + return self:ToStringBRA( Coordinate, Settings, MagVar ) else - return self:ToStringMGRS( Settings, Language ) + return self:ToStringMGRS( Settings ) end end if Settings:IsA2A_BULLS() then local Coalition = Controllable:GetCoalition() - return self:ToStringBULLS( Coalition, Settings, Language ) + return self:ToStringBULLS( Coalition, Settings, MagVar ) end if Settings:IsA2A_LL_DMS() then - return self:ToStringLLDMS( Settings, Language ) + return self:ToStringLLDMS( Settings ) end if Settings:IsA2A_LL_DDM() then - return self:ToStringLLDDM( Settings, Language ) + return self:ToStringLLDDM( Settings ) end if Settings:IsA2A_MGRS() then - return self:ToStringMGRS( Settings, Language ) + return self:ToStringMGRS( Settings ) end return nil @@ -3146,7 +3170,7 @@ do -- COORDINATE -- @param Wrapper.Controllable#CONTROLLABLE Controllable -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @return #string The pressure text in the configured measurement system. - function COORDINATE:ToStringPressure( Controllable, Settings ) -- R2.3 + function COORDINATE:ToStringPressure( Controllable, Settings ) self:F2( { Controllable = Controllable and Controllable:GetName() } ) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 12c1c6166..8ea42e478 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -922,6 +922,7 @@ do -- @field Ops.PlayerRecce#PLAYERRECCE PlayerRecce -- @field #number Coalition -- @field Core.Menu#MENU_MISSION MenuParent +-- @field #boolean ShowMagnetic Also show magnetic angles -- @extends Core.Fsm#FSM --- @@ -1234,6 +1235,7 @@ PLAYERTASKCONTROLLER = { PlayerRecce = nil, Coalition = nil, MenuParent = nil, + ShowMagnetic = true, } --- @@ -1400,7 +1402,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.45" +PLAYERTASKCONTROLLER.version="0.1.46" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -1456,6 +1458,8 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.CallsignTranslations = nil self.noflaresmokemenu = false + + self.ShowMagnetic = true if ClientFilter then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart() @@ -2016,6 +2020,20 @@ function PLAYERTASKCONTROLLER:SwitchUseGroupNames(OnOff) return self end +--- [User] Switch showing additional magnetic angles +-- @param #PLAYERTASKCONTROLLER self +-- @param #boolean OnOff If true, set to on (default), if nil or false, set to off +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SwitchMagenticAngles(OnOff) + self:T(self.lid.."SwitchMagenticAngles") + if OnOff then + self.ShowMagnetic = true + else + self.ShowMagnetic = false + end + return self +end + --- [Internal] Get task types for the menu -- @param #PLAYERTASKCONTROLLER self -- @return #table TaskTypes @@ -2681,9 +2699,9 @@ function PLAYERTASKCONTROLLER:_FlashInfo() local Coordinate = task.Target:GetCoordinate() local CoordText = "" if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then - CoordText = Coordinate:ToStringA2G(_client) + CoordText = Coordinate:ToStringA2G(_client, nil, self.ShowMagnetic) else - CoordText = Coordinate:ToStringA2A(_client) + CoordText = Coordinate:ToStringA2A(_client, nil, self.ShowMagnetic) end local targettxt = self.gettext:GetEntry("TARGET",self.locale) local text = "Target: "..CoordText @@ -2716,9 +2734,9 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) local Coordinate = task.Target:GetCoordinate() local CoordText = "" if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then - CoordText = Coordinate:ToStringA2G(Client) + CoordText = Coordinate:ToStringA2G(Client,nil,self.ShowMagnetic) else - CoordText = Coordinate:ToStringA2A(Client) + CoordText = Coordinate:ToStringA2A(Client,nil,self.ShowMagnetic) end -- Threat Level local ThreatLevel = task.Target:GetThreatLevelMax() @@ -2822,6 +2840,9 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) if string.find(CoordText," BR, ") then CoordText = string.gsub(CoordText," BR, "," Bee, Arr, ") end + if self.ShowMagnetic then + text=string.gsub(text,"°M|","° magnetic, ") + end local ThreatLocaleTextTTS = self.gettext:GetEntry("THREATTEXTTTS",self.locale) local ttstext = string.format(ThreatLocaleTextTTS,self.MenuName or self.Name,ttsplayername,ttstaskname,ThreatLevelText, targets, CoordText) -- POINTERTARGETLASINGTTS = ". Pointer over target and lasing." From dcaaa116086646b17886c55c709ba0ae603e1fa3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 8 Nov 2022 16:59:29 +0100 Subject: [PATCH 104/603] #AWACS * Minor Enhancements --- Moose Development/Moose/Ops/Awacs.lua | 109 +++----------------------- 1 file changed, 12 insertions(+), 97 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 14ccb5922..f3778c309 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -497,7 +497,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.46", -- #string + version = "0.2.47", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -1980,22 +1980,16 @@ function AWACS:_MessageVector(GID,Tag,Coordinate,Angels) local group = managedgroup.Group local groupposition = group:GetCoordinate() - --local BRtext = Coordinate:ToStringBR(groupposition) local BRtext,BRtextTTS = self:_ToStringBR(groupposition,Coordinate) local vector = self.gettext:GetEntry("VECTORTO",self.locale) local vectortts = self.gettext:GetEntry("VECTORTOTTS",self.locale) local angelstxt = self.gettext:GetEntry("ANGELS",self.locale) - --local text = string.format("%s, %s. Vector%s %s",tocallsign, self.callsigntxt,Tag,BRtextTTS) - --local textScreen = string.format("%s, %s, Vector%s %s",tocallsign, self.callsigntxt,Tag,BRtext) - local text = string.format(vectortts,tocallsign, self.callsigntxt,Tag,BRtextTTS) local textScreen = string.format(vector,tocallsign, self.callsigntxt,Tag,BRtext) if Angels then - --text = text .. ". Angels "..tostring(Angels).."." - --textScreen = textScreen .. ". Angels "..tostring(Angels).."." text = text .. angelstxt ..tostring(Angels).."." textScreen = textScreen ..angelstxt..tostring(Angels).."." end @@ -2215,7 +2209,7 @@ end -- @param #AWACS self -- @param Wrapper.Group#GROUP Group Group to use -- @param #number GID GID to use --- @param #booean IsPlayer Check in player if true +-- @param #boolean IsPlayer Check in player if true -- @return #string Callsign function AWACS:_GetCallSign(Group,GID, IsPlayer) self:T(self.lid.."_GetCallSign - GID "..tostring(GID)) @@ -2326,14 +2320,13 @@ function AWACS:_CleanUpContacts() self.Contacts:ForEach( function (Contact) local contact = Contact -- #AWACS.ManagedContact - if not contact.Contact.group:IsAlive() or contact.Target:IsDead() then + if not contact.Contact.group:IsAlive() or contact.Target:IsDead() or contact.Target:IsDestroyed() or contact.Target:CountTargets() == 0 then deadcontacts:Push(contact,contact.CID) self:T("DEAD contact CID="..contact.CID) end end ) - - --local aliveclusters = FIFO:New() + -- announce VANISHED if deadcontacts:Count() > 0 and (not self.NoGroupTags) then @@ -2460,16 +2453,12 @@ function AWACS:_TargetSelectionProcess(Untargeted) self:T(self.lid..string.format("Looking at group %s type %s",contactname,typename)) local contactcoord = contact.Cluster.coordinate or contact.Contact.position or contact.Contact.group:GetCoordinate() local contactvec2 = contactcoord:GetVec2() - -- self:T({contactcoord:ToStringMGRS()}) - -- self:T({contactvec2}) -- Bucket 0 - NOT in Rejection Zone :) if self.RejectZone then local isinrejzone = self.RejectZone:IsVec2InZone(contactvec2) - --local distance = self.OpsZone:Get2DDistance(contactcoord) if isinrejzone then self:T(self.lid.."Across Border = YES - ignore") - --targettable:Push(contact,distance) checked = true end end @@ -2517,7 +2506,6 @@ function AWACS:_TargetSelectionProcess(Untargeted) if (AOdist2 < 75) or (aspect == "Hot") then local text = string.format("In AO(Adj) dist = %d(%d) NM",AOdist,AOdist2) self:T(self.lid..text) - --if sizing > 2 then distance = math.floor(distance / sizing)+1 end targettable:Push(contact,distance) checked = true end @@ -2602,7 +2590,7 @@ function AWACS:_CreatePicture(AO,Callsign,GID,MaxEntries,IsGeneral) local contact = fifo:Pull() -- #AWACS.ManagedContact self:T({contact}) if contact and contact.Contact.group and contact.Contact.group:IsAlive() then - --local coordinate = contact.Contact.group:GetCoordinate() + local coordinate = contact.Cluster.coordinate or contact.Contact.position or contact.Contact.group:GetCoordinate() -- Core.Point#COORDINATE if not coordinate then self:E(self.lid.."NO Coordinate for this cluster! CID="..contact.CID) @@ -2633,9 +2621,7 @@ function AWACS:_CreatePicture(AO,Callsign,GID,MaxEntries,IsGeneral) local alt = contact.Contact.group:GetAltitude() or 8000 alt = UTILS.Round(UTILS.MetersToFeet(alt)/1000,0) -- Alpha Group. Bulls eye 0 2 1, 16 miles, 25 thousand. - --text = text .. " "..refBRAATTS.." miles, "..alt.." thousand." -- Alpha Group. Bulls eye 0 2 1, 16 miles, 25 thousand. text = string.format("%s %s %s, %d %s.",text,refBRAATTS,milestxt,alt,thsdtxt) - --textScreen = textScreen .. " "..refBRAA.." miles, "..alt.." thousand." -- Alpha Group, Bullseye 021, 16 miles, 25 thousand, textScreen = string.format("%s %s %s, %d %s.",textScreen,refBRAA,milestxt,alt,thsdtxt) else -- pilot reference @@ -2705,7 +2691,6 @@ function AWACS:_CreateBogeyDope(Callsign,GID) local groupcoord = group:GetCoordinate() local fifo = self.ContactsAO -- Utilities.FiFo#FIFO - --local maxentries = self.maxspeakentries local maxentries = 1 local counter = 0 @@ -2745,7 +2730,6 @@ function AWACS:_Picture(Group,IsGeneral) local textScreen = text local general = IsGeneral local GID, Outcome, gcallsign = self:_GetManagedGrpID(Group) - --local gcallsign = "" if general then local allst = self.gettext:GetEntry("ALLSTATIONS",self.locale) @@ -2772,7 +2756,6 @@ function AWACS:_Picture(Group,IsGeneral) -- get clusters from Intel local contactstable = self.Contacts:GetDataTable() - --local clustertable = self.intel:GetClusterTable() or {} -- sort into buckets for _,_contact in pairs(contactstable) do @@ -2824,15 +2807,11 @@ function AWACS:_Picture(Group,IsGeneral) local grptxt = self.gettext:GetEntry("GROUP",self.locale) local groupstxt = self.gettext:GetEntry("GROUPMULTI",self.locale) if clustersAO == 1 then - --text = text .. "One group. " text = string.format("%s%s %s. ",text,onetxt,grptxt) - --textScreen = textScreen .. "One group.\n" textScreen = string.format("%s%s %s.\n",textScreen,onetxt,grptxt) else text = string.format("%s%d %s. ",text,clustersAO,groupstxt) - --text = text .. clustersAO .. " groups. " textScreen = string.format("%s%d %s.\n",textScreen,clustersAO,groupstxt) - --textScreen = textScreen .. clustersAO .. " groups.\n" end self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false) @@ -2917,28 +2896,15 @@ function AWACS:_BogeyDope(Group) if contactsAO > 0 then local dope = self.gettext:GetEntry("DOPE",self.locale) text = string.format(dope,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - --[[ - if contactsAO == 1 then - text = text .. "One group. " - textScreen = text .. "\n" - else - text = text .. contactsAO .. " groups. " - textScreen = textScreen .. contactsAO .. " groups.\n" - end - --]] local onetxt = self.gettext:GetEntry("ONE",self.locale) local grptxt = self.gettext:GetEntry("GROUP",self.locale) local groupstxt = self.gettext:GetEntry("GROUPMULTI",self.locale) if contactsAO == 1 then - --text = text .. "One group. " text = string.format("%s%s %s. ",text,onetxt,grptxt) - --textScreen = textScreen .. "One group.\n" textScreen = string.format("%s%s %s.\n",textScreen,onetxt,grptxt) else text = string.format("%s%d %s. ",text,contactsAO,groupstxt) - --text = text .. clustersAO .. " groups. " textScreen = string.format("%s%d %s.\n",textScreen,contactsAO,groupstxt) - --textScreen = textScreen .. clustersAO .. " groups.\n" end self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false,true) @@ -2949,7 +2915,6 @@ function AWACS:_BogeyDope(Group) elseif self.AwacsFG then -- no, unknown - --text = string.format("%s. %s. Negative. You are not checked in.",self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) @@ -3248,7 +3213,6 @@ function AWACS:_Unable(Group) if managedtask.Status == AWACS.TaskStatus.REQUESTED then -- ok let's commit this one managedtask = self.ManagedTasks:PullByID(currtaskid) - --managedtask.AssignedGroupID = 0 managedtask.IsUnassigned = true managedtask.Status = AWACS.TaskStatus.FAILED self.ManagedTasks:Push(managedtask,currtaskid) @@ -3284,7 +3248,6 @@ end -- @return #AWACS self function AWACS:_TaskAbort(Group) self:T(self.lid.."_TaskAbort") - --local GID, Outcome = self:_GetManagedGrpID(Group) local Outcome,GID = self:_GetGIDFromGroupOrName(Group) local text = "" if Outcome then @@ -3300,7 +3263,6 @@ function AWACS:_TaskAbort(Group) -- ok let's un-commit this one managedtask = self.ManagedTasks:PullByID(currtaskid) managedtask.Status = AWACS.TaskStatus.FAILED - --managedtask.AssignedGroupID = 0 managedtask.IsUnassigned = true self.ManagedTasks:Push(managedtask,currtaskid) -- unlink group @@ -3431,8 +3393,7 @@ function AWACS:_CheckIn(Group) local alphacheckbulls = self:_ToStringBULLS(Group:GetCoordinate()) local alphacheckbullstts = self:_ToStringBULLS(Group:GetCoordinate(),false,true) - - --self.ManagedGrps[self.ManagedGrpID]=managedgroup + local alpha = self.gettext:GetEntry("ALPHACHECK",self.locale) text = string.format("%s. %s. %s. %s",managedgroup.CallSign,self.callsigntxt,alpha,alphacheckbulls) textTTS = text @@ -3555,11 +3516,9 @@ function AWACS:_CheckOut(Group,GID,dead) local Angels = managedgroup.AnchorStackAngels -- remove menus if managedgroup.IsPlayer then - -- DONE Move to FIFO if self.clientmenus:HasUniqueID(managedgroup.GroupName) then local menus = self.clientmenus:PullByID(managedgroup.GroupName) --#AWACS.MenuStructure menus.basemenu:Remove() - --self.clientmenus[AnchorAssigned.GroupName] = nil end end -- delete open tasks @@ -3591,7 +3550,6 @@ function AWACS:_SetClientMenus() self:T(self.lid.."_SetClientMenus") local clientset = self.clientset -- Core.Set#SET_CLIENT local aliveset = clientset:GetSetObjects() or {}-- #table of #CLIENT objects - --local clientmenus = {} local clientcount = 0 local clientcheckedin = 0 for _,_group in pairs(aliveset) do @@ -3603,7 +3561,6 @@ function AWACS:_SetClientMenus() cgrpname = cgrp:GetName() self:T(cgrpname) end - --cgrpname = string.match(cgrpname,"([%a%s]+)#") if self.MenuStrict then -- check if pilot has checked in if cgrp and cgrp:IsAlive() then @@ -3612,9 +3569,7 @@ function AWACS:_SetClientMenus() if checkedin then -- full menu minus checkin clientcheckedin = clientcheckedin + 1 - --self.clientmenus:Flush() local hasclientmenu = self.clientmenus:ReadByID(cgrpname) -- #AWACS.MenuStructure - --self:T({hasclientmenu}) local basemenu = hasclientmenu.basemenu -- Core.Menu#MENU_GROUP if hasclientmenu and (not hasclientmenu.menuset) then @@ -3622,7 +3577,6 @@ function AWACS:_SetClientMenus() self:T(self.lid.."Setting Menus for "..cgrpname) basemenu:RemoveSubMenus() - --basemenu:Refresh() local bogeydope = MENU_GROUP_COMMAND:New(cgrp,"Bogey Dope",basemenu,self._BogeyDope,self,cgrp) local picture = MENU_GROUP_COMMAND:New(cgrp,"Picture",basemenu,self._Picture,self,cgrp) local declare = MENU_GROUP_COMMAND:New(cgrp,"Declare",basemenu,self._Declare,self,cgrp) @@ -3665,10 +3619,8 @@ function AWACS:_SetClientMenus() elseif not self.clientmenus:HasUniqueID(cgrpname) then -- check in only local basemenu = MENU_GROUP:New(cgrp,self.Name,nil) - --basemenu:RemoveSubMenus() local checkin = MENU_GROUP_COMMAND:New(cgrp,"Check In",basemenu,self._CheckIn,self,cgrp) checkin:SetTag(cgrp:GetName()) - --basemenu:Set() basemenu:Refresh() local menus = { -- #AWACS.MenuStructure groupname = cgrpname, @@ -3682,8 +3634,6 @@ function AWACS:_SetClientMenus() else if cgrp and cgrp:IsAlive() and not self.clientmenus:HasUniqueID(cgrpname) then local basemenu = MENU_GROUP:New(cgrp,self.Name,nil) - --basemenu:RemoveSubMenus() - --basemenu:Refresh() local picture = MENU_GROUP_COMMAND:New(cgrp,"Picture",basemenu,self._Picture,self,cgrp) local bogeydope = MENU_GROUP_COMMAND:New(cgrp,"Bogey Dope",basemenu,self._BogeyDope,self,cgrp) local declare = MENU_GROUP_COMMAND:New(cgrp,"Declare",basemenu,self._Declare,self,cgrp) @@ -3885,7 +3835,6 @@ function AWACS:_CreateAnchorStack() AnchorStackOne.StationName = newname --push to AnchorStacks if self.debug then - --self.AnchorStacks:Flush() AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true) local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) @@ -4023,7 +3972,6 @@ function AWACS:_StartIntel(awacs) acceptzoneset:AddZone(self.BorderZone) end - --self.AwacsInZone intel:SetAcceptZones(acceptzoneset) if self.NoHelos then @@ -4475,8 +4423,8 @@ function AWACS:_CheckTaskQueue() end elseif entry.IsPlayerTask then -- Player task - -- TODO - if entry.Target:IsDead() or entry.Target:IsDestroyed() then + -- DONE + if entry.Target:IsDead() or entry.Target:IsDestroyed() or entry.Target:CountTargets() == 0 then -- success! entry.Status = AWACS.TaskStatus.SUCCESS elseif entry.Target:IsAlive() then @@ -4593,7 +4541,7 @@ function AWACS:_CheckTaskQueue() end -- target dead or out of bounds? - if entry.Target:IsDead() or entry.Target:IsDestroyed() then + if entry.Target:IsDead() or entry.Target:IsDestroyed() or entry.Target:CountTargets() == 0 then -- success! entry.Status = AWACS.TaskStatus.SUCCESS elseif entry.Target:IsAlive() then @@ -4819,7 +4767,6 @@ function AWACS:AddCAPAirWing(AirWing,Zone) AnchorStackOne.StationName = newname --push to AnchorStacks if self.debug then - --self.AnchorStacks:Flush() AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true) local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM()) AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition) @@ -4914,24 +4861,16 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo local popup = self.gettext:GetEntry("POPUP",self.locale) if IsNew and self.PlayerGuidance then - --BRAText = BRAText .. " New group." BRAText = string.format("%s %s.",BRAText,newgrp) - --TextScreen = TextScreen .. " New group." TextScreen = string.format("%s %s.",TextScreen,newgrp) elseif IsPopup then - --BRAText = BRAText .. " Pop-up group." BRAText = string.format("%s %s %s.",BRAText,popup,grptxt) - --TextScreen = TextScreen .. " Pop-up group." TextScreen = string.format("%s %s %s.",TextScreen,popup,grptxt) elseif IsBogeyDope and Tag and Tag ~= "" then - --BRAText = BRAText .. " "..Tag.." group." BRAText = string.format("%s %s %s.",BRAText,Tag,grptxt) - --TextScreen = TextScreen .. " "..Tag.." group." TextScreen = string.format("%s %s %s.",TextScreen,Tag,grptxt) else - --BRAText = BRAText .. " Group." BRAText = string.format("%s %s.",BRAText,GRPtxt) - --TextScreen = TextScreen .. " Group." TextScreen = string.format("%s %s.",TextScreen,GRPtxt) end @@ -5205,7 +5144,6 @@ function AWACS:_CheckAICAPOnStation() local OpsGroup = self:_GetAliveOpsGroupFromTable(OpsGroups) -- Ops.OpsGroup#OPSGROUP if OpsGroup then local OpsName = OpsGroup:GetName() or "Unknown" - --local OpsCallSign = OpsGroup:GetCallsignName() or "Unknown" local found,GID,OpsCallSign = self:_GetGIDFromGroupOrName(OpsGroup) report:Add(string.format("Mission FG %s",OpsName)) report:Add(string.format("Callsign %s",OpsCallSign)) @@ -5485,7 +5423,7 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets) -- BASE:I("AUFTRAG Condition Succes Eval Running") local success = true local target = target -- Ops.Target#TARGET - if target:IsDestroyed() then return true end + if target:IsDestroyed() or target:IsDead() or target:CountTargets() == 0 then return true end local tgtcoord = target:GetCoordinate() local tgtvec2 = nil if tgtcoord then @@ -5496,7 +5434,6 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets) if tgtvec2 then zones:ForEachZone( function(zone) - -- BASE:I("AUFTRAG Condition Succes ZONE Eval Running") if zone:IsVec2InZone(tgtvec2) then success = false end @@ -5504,7 +5441,6 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets) ) rzones:ForEachZone( function(zone) - -- BASE:I("AUFTRAG Condition Succes REJECT ZONE Eval Running") if zone:IsVec2InZone(tgtvec2) then success = true end @@ -5603,7 +5539,6 @@ function AWACS:onafterStart(From, Event, To) self.ControlZone = ZONE_RADIUS:New(controlzonename,self.OpsZone:GetVec2(),UTILS.NMToMeters(self.ControlZoneRadius)) if self.debug then self.ControlZone:DrawZone(self.coalition,{0,1,0},1,{1,0,0},0.05,3,true) - --MARKER:New(self.ControlZone:GetCoordinate(),"Radar Zone"):ToAll() self.OpsZone:DrawZone(self.coalition,{1,0,0},1,{1,0,0},0.2,5,true) local AOCoordString = self.AOCoordinate:ToStringLLDDM() local Rocktag = string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString) @@ -5658,7 +5593,6 @@ function AWACS:onafterStart(From, Event, To) return self end - --self.AwacsFG:SetSRS(self.PathToSRS,self.Gender,self.Culture,self.Voice,self.Port,self.PathToGoogleKey,"AWACS",self.Volume) self.callsigntxt = string.format("%s",self.CallSignClear[self.CallSign]) self:__CheckRadioQueue(-10) @@ -5717,17 +5651,14 @@ function AWACS:onafterStart(From, Event, To) -- Event functions function MarkerOps:OnAfterMarkAdded(From,Event,To,Text,Keywords,Coord) - --local m = MESSAGE:New(string.format("AWACS %s Mark Added.", self.Tag),10,"Info",true):ToAllIf(self.debug) Handler(Keywords,Coord,Text) end function MarkerOps:OnAfterMarkChanged(From,Event,To,Text,Keywords,Coord) - --BASE:I(string.format("%s Mark Changed.", self.Tag)) Handler(Keywords,Coord,Text) end function MarkerOps:OnAfterMarkDeleted(From,Event,To) - --BASE:I(string.format("%s Mark Deleted.", self.Tag)) end self.MarkerOps = MarkerOps @@ -5788,7 +5719,6 @@ function AWACS:_CheckAwacsStatus() local sunrise = self.gettext:GetEntry("SUNRISE",self.locale) local text = string.format(sunrise,self.callsigntxt,self.callsigntxt) self:_NewRadioEntry(text,text,0,false,false,false,false,true) - --self.AwacsFG:RadioTransmission(text,1,false) self:T(self.lid..text) self.sunrisedone = true end @@ -5925,13 +5855,10 @@ function AWACS:_CheckAwacsStatus() local ESTOSLeft = UTILS.Round((((self.EscortsTimeOnStation+self.ShiftChangeTime)*3600) - ESmissiontime),0) -- seconds ESTOSLeft = UTILS.Round(ESTOSLeft/60,0) -- minutes local ChangeTime = UTILS.Round(((self.ShiftChangeTime * 3600)/60),0) - --local Changedue = "No" - - --report:Add("====================") + report:Add("ESCORTS REPLACEMENT:") report:Add(string.format("Auftrag Status: %s",esstatus)) report:Add(string.format("TOS Left: %d min",ESTOSLeft)) - --report:Add(string.format("Needs ShiftChange: %s",Changedue)) local OpsGroups = ESmission:GetOpsGroups() local OpsGroup = self:_GetAliveOpsGroupFromTable(OpsGroups) -- Ops.OpsGroup#OPSGROUP @@ -6016,17 +5943,13 @@ function AWACS:onafterStatus(From, Event, To) self:_CheckMerges() - if self.debug then - --local outcome, targets = self:_TargetSelectionProcess() -- TODO for debug ATM - end - local outcome, targets = self:_TargetSelectionProcess(true) self:_CheckTaskQueue() local AI, Humans = self:_GetIdlePilots() -- assign Pilot if there are targets and available Pilots, prefer Humans to AI - -- TODO - Implemented AI First, Humans laters - need to work out how to loop the targets to assign a pilot + -- DONE - Implemented AI First, Humans laters - need to work out how to loop the targets to assign a pilot if outcome and #Humans > 0 and self.PlayerCapAssigment then -- add a task for AI self:_AssignPilotToTarget(Humans,targets) @@ -6163,7 +6086,6 @@ function AWACS:onafterAssignedAnchor(From, Event, To, GID, Anchor, AnchorStackNo local isAI = managedgroup.IsAI local Group = managedgroup.Group local CallSign = managedgroup.CallSign or "Ghost 1" - --local AnchorName = Anchor.StationZone:GetName() or "unknown" local AnchorName = Anchor.StationName or "unknown" local AnchorCoordTxt = Anchor.StationZoneCoordinateText or "unknown" local Angels = AnchorAngels or 25 @@ -6345,7 +6267,6 @@ end -- @return #AWACS self function AWACS:onafterLostContact(From,Event,To,Contact) self:T({From, Event, To, Contact}) - --self:_CleanUpContacts() return self end @@ -6359,7 +6280,6 @@ end -- @return #AWACS self function AWACS:onafterLostCluster(From,Event,To,Cluster,Mission) self:T({From, Event, To}) - --self:_CleanUpContacts() return self end @@ -6396,10 +6316,8 @@ function AWACS:onafterCheckRadioQueue(From,Event,To) if self.PathToGoogleKey then local gtext = RadioEntry.TextTTS gtext = string.format("%s",gtext) - --self.AwacsFG:RadioTransmission(gtext,1,false) self.AwacsSRS:PlayTextExt(gtext,nil,self.MultiFrequency,self.MultiModulation,self.Gender,self.Culture,self.Voice,self.Volume,"AWACS") else - --self.AwacsFG:RadioTransmission(RadioEntry.TextTTS,1,false) self.AwacsSRS:PlayTextExt(RadioEntry.TextTTS,nil,self.MultiFrequency,self.MultiModulation,self.Gender,self.Culture,self.Voice,self.Volume,"AWACS") end self:T(RadioEntry.TextTTS) @@ -6647,9 +6565,6 @@ function AWACS:onafterReAnchor(From, Event, To, GID) local lastknown = UTILS.DeepCopy(managedgroup.LastKnownPosition) local brtext = self:_ToStringBULLS(lastknown) local brtexttts = self:_ToStringBULLS(lastknown,false,true) - --if self.PathToGoogleKey then - --brtexttts = self:_ToStringBULLS(lastknown,true) - --end text = text .. " "..brtexttts.." "..milestxt.."." textScreen = textScreen .. " "..brtext.." "..milestxt.."." From e2bae31e55c9a9ef163fcfb9fa3d78ba1cb25660 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 8 Nov 2022 17:24:11 +0100 Subject: [PATCH 105/603] #AWACS * Minor Enhancements --- Moose Development/Moose/Ops/Awacs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index f3778c309..565fa451a 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -17,7 +17,7 @@ -- === -- -- ### Author: **applevangelist** --- @date Last Update September 2022 +-- @date Last Update November 2022 -- @module Ops.AWACS -- @image OPS_AWACS.jpg From dda96d8f0547817bce7ae3a94ec87950a01e0706 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 9 Nov 2022 13:08:06 +0100 Subject: [PATCH 106/603] AWACS - Speak task info Feature request --- Moose Development/Moose/Ops/Awacs.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 565fa451a..6194d7ea2 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -497,7 +497,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.47", -- #string + version = "0.2.48", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -3320,6 +3320,7 @@ function AWACS:_Showtask(Group) local targetstatus = currenttask.Target:GetState() local ToDo = currenttask.ToDo local description = currenttask.ScreenText + local descTTS = currenttask.ScreenText local callsign = Callsign if self.debug then @@ -3338,17 +3339,21 @@ function AWACS:_Showtask(Group) local targetpos = currenttask.Target:GetCoordinate() if pposition and targetpos then local alti = currenttask.Cluster.altitude or currenttask.Contact.altitude or currenttask.Contact.group:GetAltitude() - local direction = self:_ToStringBRA(pposition,targetpos,alti) + local direction, direcTTS = self:_ToStringBRA(pposition,targetpos,alti) description = description .. "\nBRA "..direction + descTTS = descTTS .."; "..direcTTS end elseif currenttask.ToDo == AWACS.TaskDescription.ANCHOR or currenttask.ToDo == AWACS.TaskDescription.REANCHOR then local targetpos = currenttask.Target:GetCoordinate() - local direction = self:_ToStringBR(pposition,targetpos) + local direction, direcTTS = self:_ToStringBR(pposition,targetpos) description = description .. "\nBR "..direction + descTTS = descTTS .. "; "..direcTTS end local statustxt = self.gettext:GetEntry("STATUS",self.locale) - MESSAGE:New(string.format("%s\n%s %s",description,statustxt,status),30,"AWACS",true):ToGroup(Group) - + --MESSAGE:New(string.format("%s\n%s %s",description,statustxt,status),30,"AWACS",true):ToGroup(Group) + local text = string.format("%s\n%s %s",description,statustxt,status) + local ttstext = string.format("%s\n%s %s",descTTS,statustxt,status) + self:_NewRadioEntry(ttstext,text,GID,true,true,true) end end end From 326733a3121f49c320ca9e4e8a0ab8481e052902 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 9 Nov 2022 16:10:34 +0100 Subject: [PATCH 107/603] AWACS --- Moose Development/Moose/Ops/Awacs.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 6194d7ea2..c9a088686 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -3341,19 +3341,19 @@ function AWACS:_Showtask(Group) local alti = currenttask.Cluster.altitude or currenttask.Contact.altitude or currenttask.Contact.group:GetAltitude() local direction, direcTTS = self:_ToStringBRA(pposition,targetpos,alti) description = description .. "\nBRA "..direction - descTTS = descTTS .."; "..direcTTS + descTTS = descTTS .."; "..direcTTS end elseif currenttask.ToDo == AWACS.TaskDescription.ANCHOR or currenttask.ToDo == AWACS.TaskDescription.REANCHOR then local targetpos = currenttask.Target:GetCoordinate() local direction, direcTTS = self:_ToStringBR(pposition,targetpos) description = description .. "\nBR "..direction - descTTS = descTTS .. "; "..direcTTS + descTTS = descTTS .. "; "..direcTTS end local statustxt = self.gettext:GetEntry("STATUS",self.locale) --MESSAGE:New(string.format("%s\n%s %s",description,statustxt,status),30,"AWACS",true):ToGroup(Group) local text = string.format("%s\n%s %s",description,statustxt,status) local ttstext = string.format("%s\n%s %s",descTTS,statustxt,status) - self:_NewRadioEntry(ttstext,text,GID,true,true,true) + self:_NewRadioEntry(ttstext,text,GID,true,true,true) end end end From ff10144ad711764e23bb0e88a8c48b1966d0fba1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 10 Nov 2022 12:20:39 +0100 Subject: [PATCH 108/603] #AWACS * SRS output for "ShowTask" * Some minor bug fixing --- Moose Development/Moose/Ops/Awacs.lua | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index c9a088686..419fee577 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -497,7 +497,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.48", -- #string + version = "0.2.49", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -2153,7 +2153,8 @@ function AWACS:_ToStringBULLS( Coordinate, ssml, TTS ) local Distance = UTILS.Round( UTILS.MetersToNM( Distance ), 0 ) if ssml then return string.format("%s %03d, %d",bullseyename,Bearing,Distance) - elseif TTS then + end + if TTS then Bearing = self:_ToStringBullsTTS(Bearing) local zero = self.gettext:GetEntry("ZERO",self.locale) local BearingTTS = string.gsub(Bearing,"0",zero) @@ -3320,7 +3321,7 @@ function AWACS:_Showtask(Group) local targetstatus = currenttask.Target:GetState() local ToDo = currenttask.ToDo local description = currenttask.ScreenText - local descTTS = currenttask.ScreenText + local descTTS = currenttask.ScreenText local callsign = Callsign if self.debug then @@ -3341,19 +3342,21 @@ function AWACS:_Showtask(Group) local alti = currenttask.Cluster.altitude or currenttask.Contact.altitude or currenttask.Contact.group:GetAltitude() local direction, direcTTS = self:_ToStringBRA(pposition,targetpos,alti) description = description .. "\nBRA "..direction - descTTS = descTTS .."; "..direcTTS + descTTS = descTTS ..";BRA "..direcTTS end elseif currenttask.ToDo == AWACS.TaskDescription.ANCHOR or currenttask.ToDo == AWACS.TaskDescription.REANCHOR then local targetpos = currenttask.Target:GetCoordinate() local direction, direcTTS = self:_ToStringBR(pposition,targetpos) description = description .. "\nBR "..direction - descTTS = descTTS .. "; "..direcTTS + descTTS = descTTS .. ";BR "..direcTTS end local statustxt = self.gettext:GetEntry("STATUS",self.locale) --MESSAGE:New(string.format("%s\n%s %s",description,statustxt,status),30,"AWACS",true):ToGroup(Group) - local text = string.format("%s\n%s %s",description,statustxt,status) - local ttstext = string.format("%s\n%s %s",descTTS,statustxt,status) - self:_NewRadioEntry(ttstext,text,GID,true,true,true) + local text = string.format("%s\n%s %s",description,statustxt,status) + local ttstext = string.format("%s. %s. %s",managedgroup.CallSign,self.callsigntxt,descTTS) + ttstext = string.gsub(ttstext,"\\n",";") + ttstext = string.gsub(ttstext,"VID","V I D") + self:_NewRadioEntry(ttstext,text,GID,true,true,false,false,true) end end end @@ -3398,11 +3401,10 @@ function AWACS:_CheckIn(Group) local alphacheckbulls = self:_ToStringBULLS(Group:GetCoordinate()) local alphacheckbullstts = self:_ToStringBULLS(Group:GetCoordinate(),false,true) - local alpha = self.gettext:GetEntry("ALPHACHECK",self.locale) text = string.format("%s. %s. %s. %s",managedgroup.CallSign,self.callsigntxt,alpha,alphacheckbulls) - textTTS = text - + textTTS = string.format("%s. %s. %s. %s",managedgroup.CallSign,self.callsigntxt,alpha,alphacheckbullstts) + self:__CheckedIn(1,managedgroup.GID) if self.PlayerStationName then From 4194614d656a7117e3a1c4689c9808a7707a332d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 10 Nov 2022 17:11:00 +0100 Subject: [PATCH 109/603] #AMMOTRUCK --- .../Moose/Functional/AmmoTruck.lua | 72 +++++++++++++++---- Moose Development/Moose/Modules.lua | 1 + 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index b1de76c85..4787979a1 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -17,7 +17,7 @@ -- @module Functional.AmmoTruck -- @image Functional.AmmoTruck.jpg -- --- Date: Sep 2022 +-- Date: Nov 2022 ------------------------------------------------------------------------- --- **AMMOTRUCK** class, extends Core.FSM#FSM @@ -67,7 +67,7 @@ -- -- local ammotruck = AMMOTRUCK:New(truckset,ariset,coalition.side.BLUE,"Logistics",ZONE:FindByName("HomeZone") -- --- ## 2 Options +-- ## 2 Options and their default values -- -- ammotruck.ammothreshold = 5 -- send a truck when down to this many rounds -- ammotruck.remunidist = 20000 -- 20km - send trucks max this far from home @@ -75,6 +75,7 @@ -- ammotruck.waitingtime = 1800 -- 30 mintes - wait max this long until remunition is done -- ammotruck.monitor = -60 - 1 minute - AMMOTRUCK checks on things every 1 minute -- ammotruck.routeonroad = true - Trucks will **try** to drive on roads +-- ammotruck.usearmygroup = false - if true, will make use of ARMYGROUP in the background (if used in DEV branch) -- -- ## 3 FSM Events to shape mission -- @@ -207,6 +208,61 @@ function AMMOTRUCK:New(Truckset,Targetset,Coalition,Alias,Homezone) self:__Start(math.random(5,10)) self:I(self.lid .. "Started") + + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "Stop". Stops the AMMOTRUCK and all its event handlers. + -- @function [parent=#AMMOTRUCK] Stop + -- @param #AMMOTRUCK self + + --- Triggers the FSM event "Stop" after a delay. Stops the AMMOTRUCK and all its event handlers. + -- @function [parent=#AMMOTRUCK] __Stop + -- @param #AMMOTRUCK self + -- @param #number delay Delay in seconds. + + --- On after "RouteTruck" event. + -- @function [parent=#AMMOTRUCK] OnAfterRouteTruck + -- @param #AMMOTRUCK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #AMMOTRUCK.data Truck + -- @param #AMMOTRUCK.data Artillery + + --- On after "TruckUnloading" event. + -- @function [parent=#AMMOTRUCK] OnAfterTruckUnloading + -- @param #AMMOTRUCK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #AMMOTRUCK.data Truck + + --- On after "TruckReturning" event. + -- @function [parent=#AMMOTRUCK] OnAfterTruckReturning + -- @param #AMMOTRUCK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #AMMOTRUCK.data Truck + + --- On after "RouteTruck" event. + -- @function [parent=#AMMOTRUCK] OnAfterRouteTruck + -- @param #AMMOTRUCK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #AMMOTRUCK.data Truck + + --- On after "TruckHome" event. + -- @function [parent=#AMMOTRUCK] OnAfterTruckHome + -- @param #AMMOTRUCK self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #AMMOTRUCK.data Truck + return self end @@ -266,7 +322,6 @@ end -- @return #AMMOTRUCK self function AMMOTRUCK:GetAmmoStatus(Group) local ammotot, shells, rockets, bombs, missiles, narti = Group:GetAmmunition() - --self:I({ammotot, shells, rockets, bombs, missiles, narti}) return rockets+missiles+narti end @@ -284,7 +339,6 @@ function AMMOTRUCK:CheckWaitingTargets(dataset) local Tdiff = Tnow - truck.timestamp if Tdiff > self.waitingtime then local hasammo = self:GetAmmoStatus(truck.group) - --if truck.group:GetAmmunition() <= self.ammothreshold then if hasammo <= self.ammothreshold then truck.statusquo = AMMOTRUCK.State.OUTOFAMMO else @@ -393,7 +447,6 @@ function AMMOTRUCK:CheckUnloadingTrucks(dataset) local Tnow = timer.getAbsTime() local Tpassed = Tnow - truck.timestamp local hasammo = self:GetAmmoStatus(truck.targetgroup) - --local ammostate = truck.targetgroup:GetAmmunition() if Tpassed > self.unloadtime and hasammo > self.ammothreshold then truck.statusquo = AMMOTRUCK.State.RETURNING truck.timestamp = timer.getAbsTime() @@ -482,7 +535,6 @@ function AMMOTRUCK:CheckTrucksAlive() local trucker = ARMYGROUP:New(truck) trucker:Activate() newtruck.group = trucker - --self.opstrucks:AddObject(newtruck) end end newtruck.statusquo = AMMOTRUCK.State.IDLE @@ -534,7 +586,6 @@ function AMMOTRUCK:onafterMonitor(From, Event, To) for _,_ari in pairs(self.targetlist) do local data = _ari -- #AMMOTRUCK.data if data.group and data.group:IsAlive() then - --data.ammo = data.group:GetAmmunition() data.ammo = self:GetAmmoStatus(data.group) data.timestamp = timer.getAbsTime() local text = string.format("Ari %s | Ammo %d | State %s",data.name,data.ammo,data.statusquo) @@ -649,15 +700,11 @@ function AMMOTRUCK:onafterRouteTruck(From, Event, To, Truckdata, Aridata) local tgtzone = ZONE_GROUP:New(aridata.name,tgtgrp,30) local tgtcoord = tgtzone:GetRandomCoordinate(15) if self.hasarmygroup then - --local targetzone = ZONE_RADIUS:New(aridata.group:GetName(),tgtcoord:GetVec2(),10) local mission = AUFTRAG:NewONGUARD(tgtcoord) local oldmission = truckdata.group:GetMissionCurrent() if oldmission then oldmission:Cancel() end - --mission:SetVerbosity(3) - --mission:SetMissionSpeed(UTILS.KmphToKnots(30)) mission:SetTime(5) mission:SetTeleport(false) - --mission:SetTime(nil,math.random(self.unloadtime,self.waitingtime)) truckdata.group:AddMission(mission) elseif self.routeonroad then truckdata.group:RouteGroundOnRoad(tgtcoord,30) @@ -688,6 +735,7 @@ end heading = heading < 180 and (360-heading) or (heading - 180) local cid = self.coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA cid = self.coalition == coalition.side.NEUTRAL and country.id.UN_PEACEKEEPERS or cid + local ammo = {} for i=1,5 do ammo[i] = SPAWNSTATIC:NewFromType("ammo_cargo","Cargos",cid) @@ -721,8 +769,6 @@ function AMMOTRUCK:onafterTruckReturning(From, Event, To, Truck) local mission = AUFTRAG:NewONGUARD(tgtcoord) local oldmission = truckdata.group:GetMissionCurrent() if oldmission then oldmission:Cancel() end - --mission:SetMissionSpeed(UTILS.KmphToKnots(30)) - --mission:SetEnableMarkers() mission:SetTime(5) mission:SetTeleport(false) truckdata.group:AddMission(mission) diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index d9decff69..ce170be0f 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -54,6 +54,7 @@ __Moose.Include( 'Scripts/Moose/Cargo/CargoCrate.lua' ) __Moose.Include( 'Scripts/Moose/Cargo/CargoGroup.lua' ) __Moose.Include( 'Scripts/Moose/Functional/AICSAR.lua' ) +__Moose.Include( 'Scripts/Moose/Functional/AmmoTruck.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Artillery.lua' ) __Moose.Include( 'Scripts/Moose/Functional/ATC_Ground.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Autolase.lua' ) From 3907e49b9ec1448b462ecd8a270d5a8dd63ff2f8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 10 Nov 2022 17:30:00 +0100 Subject: [PATCH 110/603] #Docu fixes --- Moose Development/Moose/AI/AI_Escort.lua | 2 +- Moose Development/Moose/AI/AI_Escort_Request.lua | 2 +- Moose Development/Moose/Functional/AmmoTruck.lua | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Escort.lua b/Moose Development/Moose/AI/AI_Escort.lua index e746da19c..0ca7a05eb 100644 --- a/Moose Development/Moose/AI/AI_Escort.lua +++ b/Moose Development/Moose/AI/AI_Escort.lua @@ -1,4 +1,4 @@ ---- **Functional** - Taking the lead of AI escorting your flight or of other AI. +--- **AI** - Taking the lead of AI escorting your flight or of other AI. -- -- === -- diff --git a/Moose Development/Moose/AI/AI_Escort_Request.lua b/Moose Development/Moose/AI/AI_Escort_Request.lua index 3ae7232b7..c7d109fff 100644 --- a/Moose Development/Moose/AI/AI_Escort_Request.lua +++ b/Moose Development/Moose/AI/AI_Escort_Request.lua @@ -1,4 +1,4 @@ ---- **Functional** - Taking the lead of AI escorting your flight or of other AI, upon request using the menu. +--- **AI** - Taking the lead of AI escorting your flight or of other AI, upon request using the menu. -- -- === -- diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index 4787979a1..7c6f66ca2 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -2,7 +2,9 @@ -- -- === -- --- **AMMOTRUCK** - Send a truck to supply artillery groups. +-- ## Features: +-- +-- * Send a truck to supply artillery groups. -- -- === -- @@ -15,7 +17,7 @@ -- ### Author : **applevangelist ** -- -- @module Functional.AmmoTruck --- @image Functional.AmmoTruck.jpg +-- @image rtillery.JPG -- -- Date: Nov 2022 From 744427da46cb11747bcd6661d0383a612f5a13e6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 10 Nov 2022 17:53:43 +0100 Subject: [PATCH 111/603] tdada --- Moose Development/Moose/Functional/AmmoTruck.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index 7c6f66ca2..371530b70 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -10,14 +10,14 @@ -- -- ## Missions: -- --- ### [tbd](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/tbd) +-- ### [AmmoTruck](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AMT%20-%20AmmoTruck/AmmoTruck%20100%20-%20NTTR%20-%20Basic) -- -- === -- -- ### Author : **applevangelist ** -- -- @module Functional.AmmoTruck --- @image rtillery.JPG +-- @image Artillery.JPG -- -- Date: Nov 2022 @@ -44,7 +44,7 @@ -- @field #boolean routeonroad Route truck on road if true (default) -- @extends Core.FSM#FSM ---- *Amateurs talk about tactics, but professionals study logistics.* - Gen. Robert H. Barrow, USMC +--- *Amateurs talk about tactics, but professionals study logistics.* - General Robert H Barrow, USMC -- -- Simple Class to re-arm your artillery with trucks. -- From f1ff66b39c05cc1c0fdcdc684a6dc48a15d16c39 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 11 Nov 2022 09:59:11 +0100 Subject: [PATCH 112/603] #AUFTRAG - slight improvement to NewHover --- Moose Development/Moose/Ops/Auftrag.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index e49dc25c4..d007aa28a 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1007,10 +1007,10 @@ function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt) mission.hoverSpeed = 0.1 -- the DCS Task itself will shortly be build with this so MPS mission.hoverTime = Time or 300 - mission.missionSpeed = UTILS.KnotsToMps(Speed or 150) - + self:SetMissionSpeed(Speed or 150) + self:SetMissionAltitude(MissionAlt or 1000) + -- Mission options: - mission.missionAltitude=mission.MissionAlt or UTILS.FeetToMeters(1000) mission.missionFraction=0.9 mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense From 38b17ff66a265701bcb111aec902dd54bd09800d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 11 Nov 2022 15:40:22 +0100 Subject: [PATCH 113/603] * Fix NewHover --- Moose Development/Moose/Ops/OpsGroup.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 05d6c6b10..7656d769b 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -4305,8 +4305,9 @@ function OPSGROUP:_UpdateTask(Task, Mission) --self:I({Task.dcstask.params}) local alt = Task.dcstask.params.hoverAltitude local time =Task.dcstask.params.hoverTime - local Speed=UTILS.MpsToKnots(Task.dcstask.params.missionSpeed) or UTILS.KmphToKnots(self.speedCruise) - local CruiseAlt = UTILS.FeetToMeters(Task.dcstask.params.missionAltitude) + local mSpeed = Task.dcstask.params.missionSpeed or self.speedCruise or 150 + local Speed = UTILS.KmphToKnots(mSpeed) + local CruiseAlt = UTILS.FeetToMeters(Task.dcstask.params.missionAltitude or 1000) local helo = self:GetGroup() helo:SetSpeed(0.01,true) helo:SetAltitude(alt,true,"BARO") From 159f7f3b29fcb0f4ce9e1c78b6b1224e4ddb50fe Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 13 Nov 2022 13:37:45 +0100 Subject: [PATCH 114/603] #CTLD * Change call order to move troops, vehicle on `onafter..` internally * added pseudo-function for "OnBefore..." --- Moose Development/Moose/Core/Zone.lua | 2 +- Moose Development/Moose/Ops/CTLD.lua | 137 +++++++++++++++++++++++--- 2 files changed, 126 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 5f89fdaed..c1a5b52bd 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2841,7 +2841,7 @@ do -- ZONE_ELASTIC --- Add a set of groups. Positions of the group will be considered as polygon vertices when contructing the convex hull. -- @param #ZONE_ELASTIC self - -- @param Core.Set#SET_GROUP SetGroup Set of groups. + -- @param Core.Set#SET_GROUP GroupSet Set of groups. -- @return #ZONE_ELASTIC self function ZONE_ELASTIC:AddSetGroup(GroupSet) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index bb5a86982..ccf592e4b 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -583,6 +583,7 @@ do -- @field #number verbose Verbosity level. -- @field #string lid Class id string for output to DCS log file. -- @field #number coalition Coalition side number, e.g. `coalition.side.RED`. +-- @field #boolean debug -- @extends Core.Fsm#FSM --- *Combat Troop & Logistics Deployment (CTLD): Everyone wants to be a POG, until there\'s POG stuff to be done.* (Mil Saying) @@ -1077,7 +1078,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.18" +CTLD.version="1.0.19" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1312,6 +1313,92 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param #CTLD self -- @param #number delay Delay in seconds. + --- FSM Function OnBeforeTroopsPickedUp. + -- @function [parent=#CTLD] OnBeforeTroopsPickedUp + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #CTLD_CARGO Cargo Cargo troops. + -- @return #CTLD self + + --- FSM Function OnBeforeTroopsExtracted. + -- @function [parent=#CTLD] OnBeforeTroopsExtracted + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #CTLD_CARGO Cargo Cargo troops. + -- @return #CTLD self + + --- FSM Function OnBeforeCratesPickedUp. + -- @function [parent=#CTLD] OnBeforeCratesPickedUp + -- @param #CTLD self + -- @param #string From State . + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #CTLD_CARGO Cargo Cargo crate. + -- @return #CTLD self + + --- FSM Function OnBeforeTroopsDeployed. + -- @function [parent=#CTLD] OnBeforeTroopsDeployed + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Troops Troops #GROUP Object. + -- @return #CTLD self + + --- FSM Function OnBeforeCratesDropped. + -- @function [parent=#CTLD] OnBeforeCratesDropped + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. + -- @return #CTLD self + + --- FSM Function OnBeforeCratesBuild. + -- @function [parent=#CTLD] OnBeforeCratesBuild + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB build. + -- @return #CTLD self + + --- FSM Function OnBeforeCratesRepaired. + -- @function [parent=#CTLD] OnBeforeCratesRepaired + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB repaired. + -- @return #CTLD self + + --- FSM Function OnBeforeTroopsRTB. + -- @function [parent=#CTLD] OnBeforeTroopsRTB + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + --- FSM Function OnAfterTroopsPickedUp. -- @function [parent=#CTLD] OnAfterTroopsPickedUp -- @param #CTLD self @@ -2590,9 +2677,7 @@ function CTLD:_UnloadTroops(Group, Unit) :InitRandomizeUnits(true,20,2) :InitDelayOff() :SpawnFromVec2(randomcoord) - if self.movetroopstowpzone and type ~= CTLD_CARGO.Enum.ENGINEERS then - self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) - end + self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type) end -- template loop cargo:SetWasDropped(true) -- engineering group? @@ -2604,7 +2689,6 @@ function CTLD:_UnloadTroops(Group, Unit) else self:_SendMessage(string.format("Dropped Troops %s into action!",name), 10, false, Group) end - self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter]) end -- if type end end -- cargotable loop else -- droppingatbase @@ -2958,9 +3042,6 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) :InitDelayOff() :SpawnFromVec2(randomcoord) end - if self.movetroopstowpzone and canmove then - self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) - end if Repair then self:__CratesRepaired(1,Group,Unit,self.DroppedTroops[self.TroopCounter]) else @@ -4215,7 +4296,7 @@ end self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New(name, grpname) end if self.eventoninject then - self:__TroopsDeployed(1,nil,nil,self.DroppedTroops[self.TroopCounter]) + self:__TroopsDeployed(1,nil,nil,self.DroppedTroops[self.TroopCounter],type) end end -- if type end return self @@ -4282,9 +4363,6 @@ end :InitDelayOff() :SpawnFromVec2(randomcoord) end - if self.movetroopstowpzone and canmove then - self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) - end if self.eventoninject then self:__CratesBuild(1,nil,nil,self.DroppedTroops[self.TroopCounter]) end @@ -4491,6 +4569,24 @@ end return self end + --- (Internal) FSM Function onafterTroopsDeployed. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Troops Troops #GROUP Object. + -- @param #CTLD.CargoZoneType Type Type of Cargo deployed + -- @return #CTLD self + function CTLD:onafterTroopsDeployed(From, Event, To, Group, Unit, Troops, Type) + self:T({From, Event, To}) + if self.movetroopstowpzone and Type ~= CTLD_CARGO.Enum.ENGINEERS then + self:_MoveGroupToZone(Troops) + end + return self + end + --- (Internal) FSM Function onbeforeCratesDropped. -- @param #CTLD self -- @param #string From State. @@ -4540,6 +4636,23 @@ end return self end + --- (Internal) FSM Function onafterCratesBuild. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB build. + -- @return #CTLD self + function CTLD:onafterCratesBuild(From, Event, To, Group, Unit, Vehicle) + self:T({From, Event, To}) + if self.movetroopstowpzone then + self:_MoveGroupToZone(Vehicle) + end + return self + end + --- (Internal) FSM Function onbeforeTroopsRTB. -- @param #CTLD self -- @param #string From State. From 6365298e4b6683a1b69edffbd33bc930cbf2c46c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 14 Nov 2022 17:37:49 +0100 Subject: [PATCH 115/603] #PLAYERTASK * Added a couple of QOL functions for internal INTEL object --- Moose Development/Moose/Ops/ATIS.lua | 16 +++--- Moose Development/Moose/Ops/PlayerTask.lua | 60 ++++++++++++++++++---- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index c9ae8ea41..083aa86a4 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -590,7 +590,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.9.10" +ATIS.version = "0.9.11" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1319,8 +1319,10 @@ function ATIS:onafterStatus( From, Event, To ) text = text .. string.format( ", Relay unit=%s (alive=%s)", tostring( self.relayunitname ), relayunitstatus ) end self:T( self.lid .. text ) - - self:__Status( -60 ) + + if not self:Is("Stopped") then + self:__Status( -60 ) + end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1348,9 +1350,11 @@ function ATIS:onafterCheckQueue( From, Event, To ) end end - - -- Check back in 5 seconds. - self:__CheckQueue( -math.abs( self.dTQueueCheck ) ) + + if not self:Is("Stopped") then + -- Check back in 5 seconds. + self:__CheckQueue( -math.abs( self.dTQueueCheck ) ) + end end --- Broadcast ATIS radio message. diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 8ea42e478..3209c0adb 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1,4 +1,4 @@ ----- **Ops** - PlayerTask (mission) for Players. +--- **Ops** - PlayerTask (mission) for Players. -- -- ## Main Features: -- @@ -1402,7 +1402,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.46" +PLAYERTASKCONTROLLER.version="0.1.47" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2573,7 +2573,7 @@ function PLAYERTASKCONTROLLER:_AddTask(Target) task:_SetController(self) self.TaskQueue:Push(task) - self:__TaskAdded(-1,task) + self:__TaskAdded(10,task) return self end @@ -2607,7 +2607,7 @@ function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask) PlayerTask:_SetController(self) PlayerTask:SetCoalition(self.Coalition) self.TaskQueue:Push(PlayerTask) - self:__TaskAdded(-1,PlayerTask) + self:__TaskAdded(10,PlayerTask) else self:E(self.lid.."***** NO valid PAYERTASK object sent!") end @@ -3255,7 +3255,21 @@ function PLAYERTASKCONTROLLER:AddAgent(Recce) if self.Intel then self.Intel:AddAgent(Recce) else - self:E(self.lid.."NO detection has been set up (yet)!") + self:E(self.lid.."*****NO detection has been set up (yet)!") + end + return self +end + +--- [User] Set up detection of STATIC objects. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +-- @param #PLAYERTASKCONTROLLER self +-- @param #boolean OnOff Set to `true`for on and `false`for off. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SwitchDetectStatics(OnOff) + self:T(self.lid.."SwitchDetectStatics") + if self.Intel then + self.Intel:SetDetectStatics(OnOff) + else + self:E(self.lid.."***** NO detection has been set up (yet)!") end return self end @@ -3269,7 +3283,21 @@ function PLAYERTASKCONTROLLER:AddAcceptZone(AcceptZone) if self.Intel then self.Intel:AddAcceptZone(AcceptZone) else - self:E(self.lid.."NO detection has been set up (yet)!") + self:E(self.lid.."*****NO detection has been set up (yet)!") + end + return self +end + +--- [User] Add accept SET_ZONE to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Set#SET_ZONE AcceptZoneSet Add a SET_ZONE to the accept zone set. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:AddAcceptZoneSet(AcceptZoneSet) + self:T(self.lid.."AddAcceptZoneSet") + if self.Intel then + self.Intel.acceptzoneset:AddSet(AcceptZoneSet) + else + self:E(self.lid.."*****NO detection has been set up (yet)!") end return self end @@ -3283,7 +3311,21 @@ function PLAYERTASKCONTROLLER:AddRejectZone(RejectZone) if self.Intel then self.Intel:AddRejectZone(RejectZone) else - self:E(self.lid.."NO detection has been set up (yet)!") + self:E(self.lid.."*****NO detection has been set up (yet)!") + end + return self +end + +--- [User] Add reject SET_ZONE to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Set#SET_ZONE RejectZoneSet Add a zone to the reject zone set. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:AddRejectZone(RejectZoneSet) + self:T(self.lid.."AddRejectZoneSet") + if self.Intel then + self.Intel.rejectzoneset:AddSet(RejectZoneSet) + else + self:E(self.lid.."*****NO detection has been set up (yet)!") end return self end @@ -3297,7 +3339,7 @@ function PLAYERTASKCONTROLLER:RemoveAcceptZone(AcceptZone) if self.Intel then self.Intel:RemoveAcceptZone(AcceptZone) else - self:E(self.lid.."NO detection has been set up (yet)!") + self:E(self.lid.."*****NO detection has been set up (yet)!") end return self end @@ -3311,7 +3353,7 @@ function PLAYERTASKCONTROLLER:RemoveRejectZone(RejectZone) if self.Intel then self.Intel:RemoveRejectZone(RejectZone) else - self:E(self.lid.."NO detection has been set up (yet)!") + self:E(self.lid.."*****NO detection has been set up (yet)!") end return self end From ed84d7453dc6295904f4ade602769b19afa465e9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 14 Nov 2022 18:14:04 +0100 Subject: [PATCH 116/603] #Bug fixes --- Moose Development/Moose/Core/Settings.lua | 2 +- Moose Development/Moose/Ops/PlayerTask.lua | 4 ++-- Moose Development/Moose/Utilities/Utils.lua | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index eeaa3db81..671b4cfb0 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -335,7 +335,7 @@ do -- SETTINGS --- Sets the SETTINGS MGRS accuracy. -- @param #SETTINGS self - -- @param #number MGRS_Accuracy + -- @param #number MGRS_Accuracy 0 to 5 -- @return #SETTINGS function SETTINGS:SetMGRS_Accuracy( MGRS_Accuracy ) self.MGRS_Accuracy = MGRS_Accuracy diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 3209c0adb..0e859cf74 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -3320,7 +3320,7 @@ end -- @param #PLAYERTASKCONTROLLER self -- @param Core.Set#SET_ZONE RejectZoneSet Add a zone to the reject zone set. -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:AddRejectZone(RejectZoneSet) +function PLAYERTASKCONTROLLER:AddRejectZoneSet(RejectZoneSet) self:T(self.lid.."AddRejectZoneSet") if self.Intel then self.Intel.rejectzoneset:AddSet(RejectZoneSet) @@ -3348,7 +3348,7 @@ end -- @param #PLAYERTASKCONTROLLER self -- @param Core.Zone#ZONE RejectZone Add a zone to the reject zone set. -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:RemoveRejectZone(RejectZone) +function PLAYERTASKCONTROLLER:RemoveRejectZoneSet(RejectZone) self:T(self.lid.."RemoveRejectZone") if self.Intel then self.Intel:RemoveRejectZone(RejectZone) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index de5c63933..df656431b 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -606,10 +606,12 @@ end -- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. UTILS.tostringMGRS = function(MGRS, acc) --R2.1 - if acc == 0 then + if acc <= 0 then return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph else - + + if acc > 5 then acc = 5 end + -- Test if Easting/Northing have less than 4 digits. --MGRS.Easting=123 -- should be 00123 --MGRS.Northing=5432 -- should be 05432 From 67a2deb3a4f1b9ef34f4963c2cc9d01c565eb8c7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 15 Nov 2022 16:33:12 +0100 Subject: [PATCH 117/603] #PLAYERTASK * Added option to show coord of target in info (always) --- Moose Development/Moose/Ops/PlayerTask.lua | 44 ++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 0e859cf74..986e78c0e 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -506,7 +506,7 @@ function PLAYERTASK:MarkTargetOnF10Map(Text,Coalition,ReadOnly) self.TargetMarker:Remove() end local text = Text or "Target of "..self.lid - self.TargetMarker = MARKER:New(coordinate,"Target of "..self.lid) + self.TargetMarker = MARKER:New(coordinate,text) if ReadOnly then self.TargetMarker:ReadOnly() end @@ -923,6 +923,8 @@ do -- @field #number Coalition -- @field Core.Menu#MENU_MISSION MenuParent -- @field #boolean ShowMagnetic Also show magnetic angles +-- @field #boolean InfoHasCoordinate +-- @field #boolean InfoHasLLDDM -- @extends Core.Fsm#FSM --- @@ -1108,6 +1110,7 @@ do -- FLASHMENU = "Flash Directions Switch", -- BRIEFING = "Briefing", -- TARGETLOCATION ="Target location", +-- COORDINATE = "Coordinate", -- }, -- -- e.g. @@ -1236,6 +1239,8 @@ PLAYERTASKCONTROLLER = { Coalition = nil, MenuParent = nil, ShowMagnetic = true, + InfoHasLLDDM = false, + InfoHasCoordinate = false, } --- @@ -1333,6 +1338,7 @@ PLAYERTASKCONTROLLER.Messages = { FLASHMENU = "Flash Directions Switch", BRIEFING = "Briefing", TARGETLOCATION ="Target location", + COORDINATE = "Coordinate", }, DE = { TASKABORT = "Auftrag abgebrochen!", @@ -1396,13 +1402,14 @@ PLAYERTASKCONTROLLER.Messages = { FLASHOFF = "%s - Richtungsangaben einblenden ist AUS!", FLASHMENU = "Richtungsangaben Schalter", BRIEFING = "Briefing", - TARGETLOCATION ="Zielkoordinate", + TARGETLOCATION ="Zielposition", + COORDINATE = "Koordinate", }, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.47" +PLAYERTASKCONTROLLER.version="0.1.48" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -1638,6 +1645,18 @@ function PLAYERTASKCONTROLLER:SetEnableSmokeFlareTask() return self end +--- [User] Show info text on screen with a coordinate info in any case (OFF by default) +-- @param #PLAYERTASKCONTROLLER self +-- @param #boolean OnOff Switch on = true or off = false +-- @param #boolean LLDDM Show LLDDM = true or LLDMS = false +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetInfoShowsCoordinate(OnOff,LLDDM) + self:T(self.lid.."SetInfoShowsCoordinate") + self.InfoHasCoordinate = OnOff + self.InfoHasLLDDM = LLDDM + return self +end + --- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns. -- @param #PLAYERTASKCONTROLLER self -- @param #boolean ShortCallsign If true, only call out the major flight number @@ -2733,6 +2752,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) local ttstaskname = string.format(ttsname,task.TTSType,task.PlayerTaskNr) local Coordinate = task.Target:GetCoordinate() local CoordText = "" + local CoordTextLLDM = nil if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then CoordText = Coordinate:ToStringA2G(Client,nil,self.ShowMagnetic) else @@ -2827,15 +2847,25 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) else local keine = self.gettext:GetEntry("NONE",self.locale) clienttxt = clienttxt .. keine - end - - -- Task Report + end text = text .. clienttxt + textTTS = textTTS .. clienttxt + -- Task Report + if self.InfoHasCoordinate then + if self.InfoHasLLDDM then + CoordTextLLDM = Coordinate:ToStringLLDDM() + else + CoordTextLLDM = Coordinate:ToStringLLDMS() + end + -- TARGETLOCATION + local locatxt = self.gettext:GetEntry("COORDINATE",self.locale) + text = string.format("%s\n%s: %s",text,locatxt,CoordTextLLDM) + end if task:HasFreetext() and not ( task.Type == AUFTRAG.Type.CTLD or task.Type == AUFTRAG.Type.CSAR) then local brieftxt = self.gettext:GetEntry("BRIEFING",self.locale) text = text .. string.format("\n%s: ",brieftxt)..task:GetFreetext() end - textTTS = textTTS .. clienttxt + if self.UseSRS then if string.find(CoordText," BR, ") then CoordText = string.gsub(CoordText," BR, "," Bee, Arr, ") From 709da6b6e6ed90d7c3f2db46a43e801b65a33eba Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 15 Nov 2022 17:55:24 +0100 Subject: [PATCH 118/603] #Fixes --- Moose Development/Moose/Ops/PlayerTask.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 986e78c0e..1209c342f 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1206,7 +1206,7 @@ do -- @field #PLAYERTASKCONTROLLER PLAYERTASKCONTROLLER = { ClassName = "PLAYERTASKCONTROLLER", - verbose = true, + verbose = false, lid = nil, TargetQueue = nil, ClientSet = nil, @@ -1409,7 +1409,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.48" +PLAYERTASKCONTROLLER.version="0.1.49" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -1807,7 +1807,7 @@ function PLAYERTASKCONTROLLER:EnableMarkerOps(Tag) local function Handler(Keywords,Coord,Text) if self.verbose then - local m = MESSAGE:New(string.format("Target added from marker at: %s", Coord:ToStringLLDMS()),15,"INFO"):ToAll() + local m = MESSAGE:New(string.format("Target added from marker at: %s", Coord:ToStringA2G(nil, nil, self.ShowMagnetic)),15,"INFO"):ToAll() end self:AddTarget(Coord) end @@ -1968,6 +1968,9 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) --local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext) local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext) self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation) + if EventData.IniUnitName then + self:_BuildMenus(CLIENT:FindByName(EventData.IniUnitName)) + end end end return self From 502a8a01863c8047a083691a3e80459dff3e5c29 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 16 Nov 2022 11:13:07 +0100 Subject: [PATCH 119/603] #SPAWN * Ensure we have a numbered table for InitRandomizeTemplate/Zone so math.random actually works * Also, pre-shuffle tables --- Moose Development/Moose/Core/Spawn.lua | 30 +++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index f6aec9e52..9c5225a0b 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -813,8 +813,13 @@ end -- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 ) function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable ) self:F( { self.SpawnTemplatePrefix, SpawnTemplatePrefixTable } ) - - self.SpawnTemplatePrefixTable = SpawnTemplatePrefixTable + + local temptable = {} + for _,_temp in pairs(SpawnTemplatePrefixTable) do + temptable[#temptable+1] = _temp + end + + self.SpawnTemplatePrefixTable = UTILS.ShuffleTable(temptable) self.SpawnRandomizeTemplate = true for SpawnGroupID = 1, self.SpawnMaxGroups do @@ -848,16 +853,12 @@ end -- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 ) -- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 ) -- -function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet ) -- R2.3 +function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet ) self:F( { self.SpawnTemplatePrefix } ) - self.SpawnTemplatePrefixTable = SpawnTemplateSet:GetSetNames() - self.SpawnRandomizeTemplate = true - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self:_RandomizeTemplate( SpawnGroupID ) - end - + local setnames = SpawnTemplateSet:GetSetNames() + self:InitRandomizeTemplate(setnames) + return self end @@ -921,8 +922,13 @@ end -- function SPAWN:InitRandomizeZones( SpawnZoneTable ) self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } ) - - self.SpawnZoneTable = SpawnZoneTable + + local temptable = {} + for _,_temp in pairs(SpawnZoneTable) do + temptable[#temptable+1] = _temp + end + + self.SpawnZoneTable = UTILS.ShuffleTable(temptable) self.SpawnRandomizeZones = true for SpawnGroupID = 1, self.SpawnMaxGroups do From 45b78a2e260cbf2e4b91772b77199ff501c16399 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 17 Nov 2022 13:21:57 +0100 Subject: [PATCH 120/603] Fixes --- Moose Development/Moose/Core/Point.lua | 2 +- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Core/Zone.lua | 2 +- Moose Development/Moose/Ops/CSAR.lua | 13 ++++-- Moose Development/Moose/Ops/PlayerTask.lua | 52 +++++++++++++++++++++- 5 files changed, 62 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index fe4863f54..ddf599606 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -504,7 +504,7 @@ do -- COORDINATE local gotscenery=false local function EvaluateZone(ZoneObject) - + BASE:I({ZoneObject}) if ZoneObject then -- Get category of scanned object. diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 5502e648b..3b0e6dd2f 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -6814,7 +6814,7 @@ do -- SET_SCENERY -- @param Core.Zone#ZONE Zone The zone to be scanned. Can be a ZONE_RADIUS (round) or a ZONE_POLYGON (e.g. Quad-Point) -- @return #SET_SCENERY function SET_SCENERY:NewFromZone(Zone) - local zone = Zone -- Core.Zone#ZONE_POLYGON + local zone = Zone -- Core.Zone#ZONE_RADIUS if type(Zone) == "string" then zone = ZONE:FindByName(Zone) end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index c1a5b52bd..d25a6a4bf 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -927,7 +927,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) local ZoneCoord = self:GetCoordinate() local ZoneRadius = self:GetRadius() - self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()}) + --self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()}) local SphereSearch = { id = world.VolumeType.SPHERE, diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 8ae06fb13..e8a100a5e 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -114,6 +114,7 @@ -- mycsar.countryneutral = country.id.UN_PEACEKEEPERS -- mycsar.topmenuname = "CSAR" -- set the menu entry name -- mycsar.ADFRadioPwr = 1000 -- ADF Beacons sending with 1KW as default +-- mycsar.PilotWeight = 80 -- Loaded pilots weigh 80kgs each -- -- ## 2.1 Experimental Features -- @@ -233,6 +234,7 @@ CSAR = { allheligroupset = nil, topmenuname = "CSAR", ADFRadioPwr = 1000, + PilotWeight = 80, } --- Downed pilots info. @@ -270,7 +272,7 @@ CSAR.AircraftType["Bronco-OV-10A"] = 2 --- CSAR class version. -- @field #string version -CSAR.version="1.0.15" +CSAR.version="1.0.16" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -278,7 +280,7 @@ CSAR.version="1.0.15" -- DONE: SRS Integration (to be tested) -- TODO: Maybe - add option to smoke/flare closest MASH --- TODO: shagrat Add cargoWeight to helicopter when pilot boarded +-- DONE: shagrat Add cargoWeight to helicopter when pilot boarded ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -418,10 +420,13 @@ function CSAR:New(Coalition, Template, Alias) self.wetfeettemplate = nil self.usewetfeet = false - -- added 0.1.8 + -- added 1.0.15 self.allowbronco = false -- set to true to use the Bronco mod as a CSAR plane self.ADFRadioPwr = 1000 + + -- added 1.0.16 + self.PilotWeight = 80 -- WARNING - here\'ll be dragons -- for this to work you need to de-sanitize your mission environment in \Scripts\MissionScripting.lua @@ -1397,7 +1402,7 @@ end -- @return #CSAR self function CSAR:_UpdateUnitCargoMass(_heliName) self:T(self.lid .. " _UpdateUnitCargoMass") - local calculatedMass = self:_PilotsOnboard(_heliName)*80 + local calculatedMass = self:_PilotsOnboard(_heliName)*(self.PilotWeight or 80) local Unit = UNIT:FindByName(_heliName) if Unit then Unit:SetUnitInternalCargo(calculatedMass) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 1209c342f..c4deae952 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -567,7 +567,7 @@ function PLAYERTASK:IlluminateTarget(Power,Height) if self.Target then local coordinate = self.Target:GetAverageCoordinate() if coordinate then - local bcoord = COORDINATE:NewFromVec2( coordinate:GetVec2(), Height ) + local bcoord = COORDINATE:NewFromVec2( coordinate:GetVec2(), Height ) bcoord:IlluminationBomb(Power) end end @@ -1799,16 +1799,36 @@ end -- @param #PLAYERTASKCONTROLLER self -- @param #string Tag (Optional) The tagname to use to identify commands, defaults to "TASK" -- @return #PLAYERTASKCONTROLLER self +-- @usage +-- Enable the function like so: +-- mycontroller:EnableMarkerOps("TASK") +-- Then as a player in a client slot, you can add a map marker on the F10 map. Next edit the text +-- in the marker to make it identifiable, e.g +-- +-- TASK Name=Tanks Sochi, Text=Destroy tank group located near Sochi! +-- +-- Where **TASK** is the tag that tells the controller this mark is a target location (must). +-- **Name=** ended by a comma **,** tells the controller the supposed menu entry name (optional). No extra spaces! End with a comma! +-- **Text=** tells the controller the supposed free text task description (optional, only taken if **Name=** is present first). No extra spaces! function PLAYERTASKCONTROLLER:EnableMarkerOps(Tag) self:T(self.lid.."EnableMarkerOps") local tag = Tag or "TASK" - local MarkerOps = MARKEROPS_BASE:New(tag) + local MarkerOps = MARKEROPS_BASE:New(tag,{"Name","Text"},true) local function Handler(Keywords,Coord,Text) if self.verbose then local m = MESSAGE:New(string.format("Target added from marker at: %s", Coord:ToStringA2G(nil, nil, self.ShowMagnetic)),15,"INFO"):ToAll() + local m = MESSAGE:New(string.format("Text: %s", Text),15,"INFO"):ToAll() end + local menuname = string.match(Text,"Name=(.+),") + local freetext = string.match(Text,"Text=(.+)") + if menuname then + Coord.menuname = menuname + if freetext then + Coord.freetext = freetext + end + end self:AddTarget(Coord) end @@ -2114,6 +2134,12 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue() if self.TargetQueue:Count() > 0 then local object = self.TargetQueue:Pull() local target = TARGET:New(object) + if object.menuname then + target.menuname = object.menuname + if object.freetext then + target.freetext = object.freetext + end + end self:_AddTask(target) end return self @@ -2543,9 +2569,23 @@ function PLAYERTASKCONTROLLER:_AddTask(Target) local countg = enemysetg:Count() local counts = enemysets:Count() if countg > 0 then + -- observe Tags coming from MarkerOps + if Target.menuname then + enemysetg.menuname = Target.menuname + if Target.freetext then + enemysetg.freetext = Target.freetext + end + end self:AddTarget(enemysetg) end if counts > 0 then + -- observe Tags coming from MarkerOps + if Target.menuname then + enemysets.menuname = Target.menuname + if Target.freetext then + enemysets.freetext = Target.freetext + end + end self:AddTarget(enemysets) end return self @@ -2565,6 +2605,14 @@ function PLAYERTASKCONTROLLER:_AddTask(Target) local task = PLAYERTASK:New(type,Target,self.repeatonfailed,self.repeattimes,ttstype) + -- observe Tags coming from MarkerOps + if Target.menuname then + task:SetMenuName(Target.menuname) + if Target.freetext then + task:AddFreetext(Target.freetext) + end + end + task.coalition = self.Coalition if type == AUFTRAG.Type.BOMBRUNWAY then From d77c347f245e266168ec7bce6d30514ea2d33441 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 17 Nov 2022 13:56:10 +0100 Subject: [PATCH 121/603] #CSAR * Make rescued pilot's weight configureable --- Moose Development/Moose/Ops/CSAR.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index e8a100a5e..da3853223 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -30,7 +30,7 @@ -- @module Ops.CSAR -- @image OPS_CSAR.jpg --- Date: October 2022 +-- Date: November 2022 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM From 74b1a23ca862f5e71895213fb3ee420d29fd679b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 18 Nov 2022 09:59:00 +0100 Subject: [PATCH 122/603] #GROUP/CONTROLLABLE * Added RecoveryTanker Task --- .../Moose/Wrapper/Controllable.lua | 34 ++++++++++++++++--- Moose Development/Moose/Wrapper/Group.lua | 24 +++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index beeaa9fff..8167b5327 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -62,6 +62,7 @@ -- * @{#CONTROLLABLE.TaskOrbitCircle}: (AIR) Orbit at the current position of the first unit of the controllable at a specified altitude. -- * @{#CONTROLLABLE.TaskOrbitCircleAtVec2}: (AIR) Orbit at a specified position at a specified altitude during a specified duration with a specified speed. -- * @{#CONTROLLABLE.TaskRefueling}: (AIR) Refueling from the nearest tanker. No parameters. +-- * @{#CONTROLLABLE.TaskRecoveryTanker}: (AIR) Set group to act as recovery tanker for a naval group. -- * @{#CONTROLLABLE.TaskRoute}: (AIR + GROUND) Return a Mission task to follow a given route defined by Points. -- * @{#CONTROLLABLE.TaskRouteToVec2}: (AIR + GROUND) Make the Controllable move to a given point. -- * @{#CONTROLLABLE.TaskRouteToVec3}: (AIR + GROUND) Make the Controllable move to a given point. @@ -1259,7 +1260,7 @@ end --- (AIR) Orbit at a position with at a given altitude and speed. Optionally, a race track pattern can be specified. -- @param #CONTROLLABLE self --- @param Core.Point#COORDINATE Coord Coordinate at which the CONTROLLABLE orbits. Can also be given as a `DCS#Vec3` or `DCS#Vec2` object. +-- @param Core.Point#COORDINATE Coord Coordinate at which the CONTROLLABLE orbits. -- @param #number Altitude Altitude in meters of the orbit pattern. Default y component of Coord. -- @param #number Speed Speed [m/s] flying the orbit pattern. Default 128 m/s = 250 knots. -- @param Core.Point#COORDINATE CoordRaceTrack (Optional) If this coordinate is specified, the CONTROLLABLE will fly a race-track pattern using this and the initial coordinate. @@ -1268,11 +1269,11 @@ function CONTROLLABLE:TaskOrbit( Coord, Altitude, Speed, CoordRaceTrack ) local Pattern = AI.Task.OrbitPattern.CIRCLE - local P1 = {x=Coord.x, y=Coord.z or Coord.y} + local P1 = Coord:GetVec2() local P2 = nil if CoordRaceTrack then Pattern = AI.Task.OrbitPattern.RACE_TRACK - P2 = {x=CoordRaceTrack.x, y=CoordRaceTrack.z or CoordRaceTrack.y} + P2 = CoordRaceTrack:GetVec2() end local Task = { @@ -1367,6 +1368,31 @@ function CONTROLLABLE:TaskRefueling() return DCSTask end +--- (AIR) Act as Recovery Tanker for a naval/carrier group. +-- @param #CONTROLLABLE self +-- @param Wrapper.Group#GROUP CarrierGroup +-- @param #number Speed Speed in meters per second +-- @param #number Altitude Altitude the tanker orbits at in meters +-- @param #number LastWptNumber (optional) Waypoint of carrier group that when reached, ends the recovery tanker task +-- @return DCS#Task The DCS task structure. +function CONTROLLABLE:TaskRecoveryTanker(CarrierGroup, Speed, Altitude, LastWptNumber) + + local LastWptFlag = type(LastWptNumber) == "number" and true or false + + local DCSTask = { + id = "RecoveryTanker", + params = { + groupId = CarrierGroup:GetID(), + speed = Speed, + altitude = Altitude, + lastWptIndexFlag = LastWptFlag, + lastWptIndex = LastWptNumber + } + } + + return DCSTask +end + --- (AIR HELICOPTER) Landing at the ground. For helicopters only. -- @param #CONTROLLABLE self -- @param DCS#Vec2 Vec2 The point where to land. @@ -3928,4 +3954,4 @@ function CONTROLLABLE:SetAltitude(Altitude, Keep, AltType) end end return self -end \ No newline at end of file +end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index c0cb7e31e..66a67d428 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2855,3 +2855,27 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) return callsign end + +--- +-- @param #GROUP self +-- @param Wrapper.Group#GROUP CarrierGroup. +-- @param #number Speed Speed in knots. +-- @param #boolean ToKIAS If true, adjust speed to altitude (KIAS). +-- @param #number Altitude Altitude the tanker orbits at in feet. +-- @param #number Delay (optional) Set the task after this many seconds. Defaults to one. +-- @param #number LastWaypoint (optional) Waypoint number of carrier group that when reached, ends the recovery tanker task. +-- @return #GROUP self +function GROUP:SetAsRecoveryTanker(CarrierGroup,Speed,ToKIAS,Altitude,Delay,LastWaypoint) + + local speed = ToKIAS == true and UTILS.KnotsToAltKIAS(Speed,Altitude) or Speed + speed = UTILS.KnotsToMps(speed) + + local alt = UTILS.FeetToMeters(Altitude) + local delay = Delay or 1 + + local task = self:TaskRecoveryTanker(CarrierGroup,speed,alt,LastWaypoint) + + self:SetTask(task,delay) + + return self +end From 6f02f671688708399213debfbaa8075a2a7afbfd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 18 Nov 2022 11:24:54 +0100 Subject: [PATCH 123/603] #GROUP * Added additional push of tanker task to GROUP:SetAsRecoveryTanker() for a working setup --- Moose Development/Moose/Wrapper/Group.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 66a67d428..6d6f09c19 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2877,5 +2877,8 @@ function GROUP:SetAsRecoveryTanker(CarrierGroup,Speed,ToKIAS,Altitude,Delay,Last self:SetTask(task,delay) + local tankertask = self:EnRouteTaskTanker() + self:PushTask(tankertask,delay+2) + return self end From 1a8f9fca26412f797ae908b0b06192e8334c0503 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 23 Nov 2022 12:58:26 +0100 Subject: [PATCH 124/603] #Auftrag * Fix for Orbit task --- Moose Development/Moose/Ops/Auftrag.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index d007aa28a..d5e62aab1 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -6031,7 +6031,7 @@ function AUFTRAG:GetDCSMissionTask() --end -- Create orbit task. - local DCStask=CONTROLLABLE.TaskOrbit(nil, orbitVec2, self.orbitAltitude, self.orbitSpeed, orbitRaceTrack) + local DCStask=CONTROLLABLE.TaskOrbit(nil, COORDINATE:NewFromVec2(orbitVec2), self.orbitAltitude, self.orbitSpeed, orbitRaceTrack) -- Add DCS task. table.insert(DCStasks, DCStask) From 136d4109f5c85a442f2992d481a5abb9db6848ce Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 27 Nov 2022 17:34:46 +0100 Subject: [PATCH 125/603] * less noise --- Moose Development/Moose/Core/Point.lua | 2 +- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Ops/ATIS.lua | 2 ++ Moose Development/Moose/Tasking/CommandCenter.lua | 2 +- Moose Development/Moose/Tasking/Mission.lua | 8 ++++---- Moose Development/Moose/Utilities/Utils.lua | 2 ++ Moose Development/Moose/Wrapper/Airbase.lua | 2 +- Moose Development/Moose/Wrapper/Controllable.lua | 2 +- 8 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index ddf599606..58e81c161 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -504,7 +504,7 @@ do -- COORDINATE local gotscenery=false local function EvaluateZone(ZoneObject) - BASE:I({ZoneObject}) + BASE:T({ZoneObject}) if ZoneObject then -- Get category of scanned object. diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 3b0e6dd2f..350c22b67 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -6800,7 +6800,7 @@ do -- SET_SCENERY if ZoneSet then for _,_zone in pairs(ZoneSet.Set) do - self:I("Zone type handed: "..tostring(_zone.ClassName)) + self:T("Zone type handed: "..tostring(_zone.ClassName)) table.insert(zonenames,_zone:GetName()) end self:AddSceneryByName(zonenames) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 083aa86a4..9cdf60ec3 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -268,6 +268,8 @@ -- Unfortunately, it is not possible to determine the duration of the complete transmission. So once the transmission is finished, there might be some radio silence before -- the next iteration begins. You can fine tune the time interval between transmissions with the @{#ATIS.SetQueueUpdateTime}() function. The default interval is 90 seconds. -- +-- An SRS Setup-Guide can be found here: [Moose TTS Setup Guide](https://github.com/FlightControl-Master/MOOSE_GUIDES/blob/master/documents/Moose%20TTS%20Setup%20Guide.pdf) +-- -- # Examples -- -- ## Caucasus: Batumi diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 6562fcb90..8af4786bc 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -512,7 +512,7 @@ function COMMANDCENTER:AssignTask( TaskGroup ) if Task then - self:I( "Assigning task " .. Task:GetName() .. " using auto assign method " .. self.AutoAssignMethod .. " to " .. TaskGroup:GetName() .. " with task priority " .. AssignPriority ) + self:T( "Assigning task " .. Task:GetName() .. " using auto assign method " .. self.AutoAssignMethod .. " to " .. TaskGroup:GetName() .. " with task priority " .. AssignPriority ) if not self.AutoAcceptTasks == true then Task:SetAutoAssignMethod( ACT_ASSIGN_MENU_ACCEPT:New( Task.TaskBriefing ) ) diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index ba84fe162..b0f39072f 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -413,7 +413,7 @@ end -- @param Wrapper.Group#GROUP PlayerGroup The GROUP of the player joining the Mission. -- @return #boolean true if Unit is part of a Task in the Mission. function MISSION:JoinUnit( PlayerUnit, PlayerGroup ) - self:I( { Mission = self:GetName(), PlayerUnit = PlayerUnit, PlayerGroup = PlayerGroup } ) + self:T( { Mission = self:GetName(), PlayerUnit = PlayerUnit, PlayerGroup = PlayerGroup } ) local PlayerUnitAdded = false @@ -571,7 +571,7 @@ do -- Group Assignment local MissionGroupName = MissionGroup:GetName() self.AssignedGroups[MissionGroupName] = MissionGroup - self:I( string.format( "Mission %s is assigned to %s", MissionName, MissionGroupName ) ) + self:T( string.format( "Mission %s is assigned to %s", MissionName, MissionGroupName ) ) return self end @@ -698,7 +698,7 @@ end function MISSION:AddTask( Task ) local TaskName = Task:GetTaskName() - self:I( { "==> Adding TASK ", MissionName = self:GetName(), TaskName = TaskName } ) + self:T( { "==> Adding TASK ", MissionName = self:GetName(), TaskName = TaskName } ) self.Tasks[TaskName] = Task @@ -717,7 +717,7 @@ end function MISSION:RemoveTask( Task ) local TaskName = Task:GetTaskName() - self:I( { "<== Removing TASK ", MissionName = self:GetName(), TaskName = TaskName } ) + self:T( { "<== Removing TASK ", MissionName = self:GetName(), TaskName = TaskName } ) self:F( TaskName ) self.Tasks[TaskName] = self.Tasks[TaskName] or { n = 0 } diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index df656431b..e9d9f01f6 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1576,6 +1576,8 @@ function UTILS.GMTToLocalTimeDifference() return 3 -- Damascus is UTC+3 hours elseif theatre==DCSMAP.MarianaIslands then return 10 -- Guam is UTC+10 hours. + elseif theatre==DCSMAP.Falklands then + return -3 -- Guam is UTC+10 hours. else BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre))) return 0 diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index f6e4c440f..a2fd0577a 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1368,7 +1368,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, local _nspots=nspots or group:GetSize() -- Debug info. - self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at terminal type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype))) + self:T(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at terminal type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype))) -- Table of valid spots. local validspots={} diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 8167b5327..3ae169828 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2395,7 +2395,7 @@ do -- Route methods -- @return DCS#Task Task. -- @return #boolean If true, path on road is possible. If false, task will route the group directly to its destination. function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed, OffRoadFormation, Shortcut, FromCoordinate, WaypointFunction, WaypointFunctionArguments ) - self:I( { ToCoordinate = ToCoordinate, Speed = Speed, OffRoadFormation = OffRoadFormation, WaypointFunction = WaypointFunction, Args = WaypointFunctionArguments } ) + self:T( { ToCoordinate = ToCoordinate, Speed = Speed, OffRoadFormation = OffRoadFormation, WaypointFunction = WaypointFunction, Args = WaypointFunctionArguments } ) -- Defaults. Speed = Speed or 20 From df94c89ce8b1c8e1ebb29d2b737cc15feeb991b2 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:28:55 +0100 Subject: [PATCH 126/603] RAT - Remove the #... at the end on ATC callouts RAT - Remove the #... at the end on ATC callouts to nicefy immersion --- Moose Development/Moose/Functional/RAT.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 0643cf01b..a54533eeb 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -5671,6 +5671,9 @@ function RAT:_ATCClearForLanding(airport, flight) -- Debug message. local text1=string.format("ATC %s: Flight %s cleared for landing (flag=%d).", airport, flight, flagvalue) + if string.find(flight,"#") then + flight = string.match(flight,"^(.+)#") + end local text2=string.format("ATC %s: Flight %s you are cleared for landing.", airport, flight) BASE:T( RAT.id..text1) MESSAGE:New(text2, 10):ToAllIf(RAT.ATC.messages) @@ -5713,6 +5716,9 @@ function RAT:_ATCFlightLanded(name) local text1=string.format("ATC %s: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60) local text2=string.format("ATC %s: Number of flights still on final %d.", dest, RAT.ATC.airport[dest].Nonfinal) local text3=string.format("ATC %s: Traffic report: Number of planes landed in total %d. Flights/hour = %3.2f.", dest, RAT.ATC.airport[dest].traffic, TrafficPerHour) + if string.find(name,"#") then + name = string.match(name,"^(.+)#") + end local text4=string.format("ATC %s: Flight %s landed. Welcome to %s.", dest, name, dest) BASE:T(RAT.id..text1) BASE:T(RAT.id..text2) From 65d25f654404c6c5955f266c1d6cfdb7611beb6a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 29 Nov 2022 15:40:35 +0100 Subject: [PATCH 127/603] #PLAYERTASK * PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent) Silent option added #CTLD * Small fix for BEACON Zones --- Moose Development/Moose/Ops/CTLD.lua | 7 ++++--- Moose Development/Moose/Ops/PlayerTask.lua | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 0a7c38665..e370b430e 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update October 2022 +-- Last Update December 2022 do @@ -1078,7 +1078,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.19" +CTLD.version="1.0.20" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3611,7 +3611,8 @@ function CTLD:CheckDroppedBeacons() for _,_beacon in pairs (self.droppedBeacons) do local beacon = _beacon -- #CTLD.CargoZone - local T0 = beacon.timestamp + if not beacon.timestamp then beacon.timestamp = timer.getTime() end + local T0 = beacon.timestamp if timer.getTime() - T0 > timeout then local name = beacon.name self.droppedbeaconref[name] = nil diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index c4deae952..2c6f3a75f 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1409,7 +1409,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.49" +PLAYERTASKCONTROLLER.version="0.1.50" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2651,6 +2651,7 @@ end --- [User] Add a PLAYERTASK object to the list of (open) tasks -- @param #PLAYERTASKCONTROLLER self -- @param Ops.PlayerTask#PLAYERTASK PlayerTask +-- @param #boolean Silent If true, make no "has new task" announcement -- @return #PLAYERTASKCONTROLLER self -- @usage -- Example to create a PLAYERTASK of type CTLD and give Players 10 minutes to complete: @@ -2671,13 +2672,15 @@ end -- ) -- -- taskmanager:AddPlayerTaskToQueue(PlayerTask) -function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask) +function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent) self:T(self.lid.."AddPlayerTaskToQueue") if PlayerTask and PlayerTask.ClassName and PlayerTask.ClassName == "PLAYERTASK" then PlayerTask:_SetController(self) PlayerTask:SetCoalition(self.Coalition) self.TaskQueue:Push(PlayerTask) - self:__TaskAdded(10,PlayerTask) + if not Silent then + self:__TaskAdded(10,PlayerTask) + end else self:E(self.lid.."***** NO valid PAYERTASK object sent!") end From 3281b669ae8a8021ecdbc3a02a4a6b3bb2b6a08f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 1 Dec 2022 13:23:57 +0100 Subject: [PATCH 128/603] * minor fixes --- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Functional/RAT.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 350c22b67..d6077667c 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -179,7 +179,7 @@ do -- SET_BASE return Names end - --- Gets a list of the Objects in the Set. + --- Returns a table of the Objects in the Set. -- @param #SET_BASE self -- @return #SET_BASE self function SET_BASE:GetSetObjects() -- R2.3 diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index a54533eeb..081132413 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -3492,7 +3492,7 @@ function RAT:Status(message, forID) local fuel=group:GetFuel()*100.0 local airborne=group:InAir() local coords=group:GetCoordinate() - local alt=coords.y + local alt=coords.y or 1000 --local vel=group:GetVelocityKMH() local departure=ratcraft.departure:GetName() local destination=ratcraft.destination:GetName() From b7413f1dff4e827f9c1260e6302462218a901efb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 2 Dec 2022 16:36:33 +0100 Subject: [PATCH 129/603] * Small fixes --- Moose Development/Moose/Core/Message.lua | 12 ++++++++++++ Moose Development/Moose/Core/SpawnStatic.lua | 2 +- Moose Development/Moose/Ops/CTLD.lua | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 780446109..5a0fc3bcb 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -443,3 +443,15 @@ function MESSAGE:ToLog() return self end + +--- Sends a MESSAGE to DCS log file if the given Condition is true. +-- @param #MESSAGE self +-- @return #MESSAGE self +function MESSAGE:ToLogIf( Condition ) + + if Condition and Condition == true then + env.info(self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" )) + end + return self +end + diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index d73336464..4420f70ed 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -275,7 +275,7 @@ end --- Initialize as dead. -- @param #SPAWNSTATIC self --- @param #boolean IsCargo If true, this static is dead. +-- @param #boolean IsDead If true, this static is dead. -- @return #SPAWNSTATIC self function SPAWNSTATIC:InitDead(IsDead) self.InitStaticDead=IsDead diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index e370b430e..145c2c9e0 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3690,7 +3690,7 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip, IsDropped) local Sound = "l10n/DEFAULT/"..Sound trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight elseif Zone then - local ZoneCoord = Zone:GetCoordinate() + local ZoneCoord = Zone:GetCoordinate(2) local ZoneVec3 = ZoneCoord:GetVec3() local Frequency = Mhz * 1000000 -- Freq in Hertz local Sound = "l10n/DEFAULT/"..Sound From 3295dd36353787646c3f50e96b8c0ccdbf275bfe Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 2 Dec 2022 16:52:20 +0100 Subject: [PATCH 130/603] # CTLD BEACON zone fixes --- Moose Development/Moose/Ops/CTLD.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 145c2c9e0..2639c6cae 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3522,6 +3522,7 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship ctldzone.name = Name or "NONE" ctldzone.type = Type or CTLD.CargoZoneType.MOVE -- #CTLD.CargoZoneType ctldzone.hasbeacon = HasBeacon or false + ctldzone.timestamp = timer.getTime() if HasBeacon then ctldzone.fmbeacon = self:_GetFMBeacon(Name) @@ -3611,7 +3612,7 @@ function CTLD:CheckDroppedBeacons() for _,_beacon in pairs (self.droppedBeacons) do local beacon = _beacon -- #CTLD.CargoZone - if not beacon.timestamp then beacon.timestamp = timer.getTime() end + if not beacon.timestamp then beacon.timestamp = timer.getTime() + timeout end local T0 = beacon.timestamp if timer.getTime() - T0 > timeout then local name = beacon.name From d011c8e72f84ac25266458877bbc238ab2eed3b0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 2 Dec 2022 17:30:59 +0100 Subject: [PATCH 131/603] #CTLD Frequency Beacons --- Moose Development/Moose/Ops/CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 2639c6cae..40b3d9686 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3687,13 +3687,13 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip, IsDropped) if IsDropped and Zone then local ZoneCoord = Zone local ZoneVec3 = ZoneCoord:GetVec3() - local Frequency = Mhz * 1000000 -- Freq in Hertz + local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz local Sound = "l10n/DEFAULT/"..Sound trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight elseif Zone then local ZoneCoord = Zone:GetCoordinate(2) local ZoneVec3 = ZoneCoord:GetVec3() - local Frequency = Mhz * 1000000 -- Freq in Hertz + local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz local Sound = "l10n/DEFAULT/"..Sound trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight end From 60d91cb2fba0d1f5d6bb3788245e43e6d1d1f953 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 3 Dec 2022 14:36:14 +0100 Subject: [PATCH 132/603] Small fixes --- Moose Development/Moose/Ops/CTLD.lua | 12 ++++++++++-- Moose Development/Moose/Ops/PlayerRecce.lua | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 40b3d9686..e36752b3f 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3522,8 +3522,12 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship ctldzone.name = Name or "NONE" ctldzone.type = Type or CTLD.CargoZoneType.MOVE -- #CTLD.CargoZoneType ctldzone.hasbeacon = HasBeacon or false - ctldzone.timestamp = timer.getTime() - + + if Type == CTLD.CargoZoneType.BEACON then + self.droppedbeaconref[ctldzone.name] = zone:GetCoordinate() + ctldzone.timestamp = timer.getTime() + end + if HasBeacon then ctldzone.fmbeacon = self:_GetFMBeacon(Name) ctldzone.uhfbeacon = self:_GetUHFBeacon(Name) @@ -3690,12 +3694,16 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip, IsDropped) local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz local Sound = "l10n/DEFAULT/"..Sound trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight + --local status = string.format("***** Beacon added Freq %s Mod %s", Mhz, UTILS.GetModulationName(Modulation)) + --MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug) elseif Zone then local ZoneCoord = Zone:GetCoordinate(2) local ZoneVec3 = ZoneCoord:GetVec3() local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz local Sound = "l10n/DEFAULT/"..Sound trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight + --local status = string.format("***** Beacon added Freq %s Mod %s", Mhz, UTILS.GetModulationName(Modulation)) + --MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug) end return self end diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index ef80816c8..f71b33fa8 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1394,7 +1394,7 @@ end -- @param #string To -- @return #PLAYERRECCE self function PLAYERRECCE:onafterStatus(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) if not self.timestamp then self.timestamp = timer.getTime() From 2b572f49481e994bc63a5d843039dd2f9383df4e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 6 Dec 2022 12:48:42 +0100 Subject: [PATCH 133/603] #PLAYERTASK * slower callout for MGRS coordinates #CTLD * Reduce log noise --- Moose Development/Moose/Ops/CTLD.lua | 2 +- Moose Development/Moose/Ops/PlayerTask.lua | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index e36752b3f..61ff3edbf 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -4621,7 +4621,7 @@ end -- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB build. -- @return #CTLD self function CTLD:onbeforeCratesBuild(From, Event, To, Group, Unit, Vehicle) - self:I({From, Event, To}) + self:T({From, Event, To}) if Unit and Unit:IsPlayer() and self.PlayerTaskQueue then local playername = Unit:GetPlayerName() local dropcoord = Vehicle:GetCoordinate() or COORDINATE:New(0,0,0) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 2c6f3a75f..0f6d95c78 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update November 2022 +-- @date Last Update December 2022 do @@ -1409,7 +1409,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.50" +PLAYERTASKCONTROLLER.version="0.1.51" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2922,10 +2922,20 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) if self.UseSRS then if string.find(CoordText," BR, ") then - CoordText = string.gsub(CoordText," BR, "," Bee, Arr, ") + CoordText = string.gsub(CoordText," BR, "," Bee, Arr; ") end if self.ShowMagnetic then - text=string.gsub(text,"°M|","° magnetic, ") + text=string.gsub(text,"°M|","° magnetic; ") + end + if string.find(CoordText,"MGRS") then + local Text = string.gsub(CoordText,"%d","%1;") -- "0 5 1 " + Text = string.gsub(Text," $","") -- "0 5 1" + CoordText = string.gsub(Text,"0","zero") + CoordText = string.gsub(Text,"MGRS","MGRS;") + if self.PathToGoogleKey then + CoordText = string.format("%s",CoordText) + end + --self:I(self.lid.." | ".. CoordText) end local ThreatLocaleTextTTS = self.gettext:GetEntry("THREATTEXTTTS",self.locale) local ttstext = string.format(ThreatLocaleTextTTS,self.MenuName or self.Name,ttsplayername,ttstaskname,ThreatLevelText, targets, CoordText) From 73d53176d104acba3da481d738a0dfe8f5883f8a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 6 Dec 2022 18:05:29 +0100 Subject: [PATCH 134/603] #PLAYERRECCE * Smoke own position on ground, not mid-air --- Moose Development/Moose/Ops/PlayerRecce.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index f71b33fa8..0d0ce1166 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -104,7 +104,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.15", + version = "0.0.16", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, @@ -958,9 +958,11 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) end if self.SmokeOwn[playername] then - local cc = client:GetCoordinate() + local cc = client:GetVec2() + -- don't smoke mid-air + local lc = COORDINATE:NewFromVec2(cc,1) local color = self.SmokeColor.ownsmoke - cc:Smoke(color) + lc:Smoke(color) end return self From 96344e3abf48399034d0c6bd42e0d962c9d6674b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 7 Dec 2022 18:54:56 +0100 Subject: [PATCH 135/603] Radio enum --- Moose Development/Moose/DCS.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 4db719cdb..2a0c98ae4 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -135,6 +135,22 @@ do -- env end -- env +do -- radio + + ---@type radio + -- @field #radio.modulation modulation + + --- + -- @type radio.modulation + -- @field AM + -- @field FM + + radio = {} + radio.modulation = {} + radio.modulation.AM = 0 + radio.modulation.FM = 1 + +end do -- timer From aed8cb9cf546c87b8e0366113070286ef0998aee Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 7 Dec 2022 18:55:03 +0100 Subject: [PATCH 136/603] #CTLD - enforce modulation on beacons --- Moose Development/Moose/Ops/CTLD.lua | 51 +++++++++++++++++++--------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 61ff3edbf..eeb42a93a 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1015,7 +1015,16 @@ CTLD = { -- @type CTLD.ZoneBeacon -- @field #string name -- Name of zone for the coordinate -- @field #number frequency -- in mHz --- @field #number modulation -- i.e.radio.modulation.FM or radio.modulation.AM +-- @field #number modulation -- i.e.CTLD.RadioModulation.FM or CTLD.RadioModulation.AM + +--- Radio Modulation +-- @type CTLD.RadioModulation +-- @field #number AM +-- @field #number FM +CTLD.RadioModulation = { + AM = 0, + FM = 1, +} --- Zone Info. -- @type CTLD.CargoZone @@ -3440,7 +3449,7 @@ function CTLD:_GetFMBeacon(Name) table.insert(self.UsedFMFrequencies, FM) beacon.name = Name beacon.frequency = FM / 1000000 - beacon.modulation = radio.modulation.FM + beacon.modulation = CTLD.RadioModulation.FM return beacon end @@ -3460,7 +3469,7 @@ function CTLD:_GetUHFBeacon(Name) table.insert(self.UsedUHFFrequencies, UHF) beacon.name = Name beacon.frequency = UHF / 1000000 - beacon.modulation = radio.modulation.AM + beacon.modulation = CTLD.RadioModulation.AM return beacon end @@ -3481,7 +3490,7 @@ function CTLD:_GetVHFBeacon(Name) table.insert(self.UsedVHFFrequencies, VHF) beacon.name = Name beacon.frequency = VHF / 1000000 - beacon.modulation = radio.modulation.FM + beacon.modulation = CTLD.RadioModulation.FM return beacon end @@ -3690,20 +3699,26 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip, IsDropped) local Sound = Sound or "beacon.ogg" if IsDropped and Zone then local ZoneCoord = Zone - local ZoneVec3 = ZoneCoord:GetVec3() + local ZoneVec3 = ZoneCoord:GetVec3(1) local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz + --local Frequency = Mhz*1000000 local Sound = "l10n/DEFAULT/"..Sound - trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight - --local status = string.format("***** Beacon added Freq %s Mod %s", Mhz, UTILS.GetModulationName(Modulation)) - --MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug) + local name = string.format("%s-%f-%s",Zone:GetName(),Mhz,tostring(Modulation)) + --trigger.action.stopRadioTransmission(name) + trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, tonumber(Frequency), 100000,name) -- Beacon in MP only runs for 30secs straight + local status = string.format("***** Beacon added Freq %s Mod %s", Frequency, UTILS.GetModulationName(Modulation)) + MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug) elseif Zone then - local ZoneCoord = Zone:GetCoordinate(2) + local ZoneCoord = Zone:GetCoordinate(1) local ZoneVec3 = ZoneCoord:GetVec3() local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz + --local Frequency = Mhz*1000000 local Sound = "l10n/DEFAULT/"..Sound - trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight - --local status = string.format("***** Beacon added Freq %s Mod %s", Mhz, UTILS.GetModulationName(Modulation)) - --MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug) + local name = string.format("%s-%f-%s",Zone:GetName(),Mhz,tostring(Modulation)) + --trigger.action.stopRadioTransmission(name) + trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, tonumber(Frequency), 100000,name) -- Beacon in MP only runs for 30secs straight + local status = string.format("***** Beacon added Freq %s Mod %s", Frequency, UTILS.GetModulationName(Modulation)) + MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug) end return self end @@ -3730,10 +3745,14 @@ function CTLD:_RefreshRadioBeacons() local Name = czone.name local FM = FMbeacon.frequency -- MHz local VHF = VHFbeacon.frequency -- KHz - local UHF = UHFbeacon.frequency -- MHz - self:_AddRadioBeacon(Name,Sound,FM,radio.modulation.FM, IsShip, IsDropped) - self:_AddRadioBeacon(Name,Sound,VHF,radio.modulation.FM, IsShip, IsDropped) - self:_AddRadioBeacon(Name,Sound,UHF,radio.modulation.AM, IsShip, IsDropped) + local UHF = UHFbeacon.frequency -- MHz + -- local co = coroutine.create(self._AddRadioBeacon) + --coroutine.resume(co, self, Name,Sound,FM,CTLD.RadioModulation.FM, IsShip, IsDropped) + --coroutine.resume(co, self, Name,Sound,VHF,CTLD.RadioModulation.FM, IsShip, IsDropped) + --coroutine.resume(co, self, Name,Sound,UHF,CTLD.RadioModulation.AM, IsShip, IsDropped) + self:_AddRadioBeacon(Name,Sound,FM, CTLD.RadioModulation.FM, IsShip, IsDropped) + self:_AddRadioBeacon(Name,Sound,VHF,CTLD.RadioModulation.FM, IsShip, IsDropped) + self:_AddRadioBeacon(Name,Sound,UHF,CTLD.RadioModulation.AM, IsShip, IsDropped) end end end From 2fc203166513aa0da0075e76b60bca711094e225 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 7 Dec 2022 18:55:32 +0100 Subject: [PATCH 137/603] #minor enhancements --- Moose Development/Moose/Ops/Awacs.lua | 6 +- Moose Development/Moose/Ops/PlayerRecce.lua | 5 +- Moose Development/Moose/Ops/PlayerTask.lua | 97 +++++++++++++-------- 3 files changed, 67 insertions(+), 41 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 3e910433a..984c32367 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -497,7 +497,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.49", -- #string + version = "0.2.50", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -914,7 +914,7 @@ AWACS.TaskStatus = { --@field #boolean FromAI ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO-List 0.2.42 +-- TODO-List 0.2.50 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- -- DONE - WIP - Player tasking, VID @@ -3468,7 +3468,7 @@ function AWACS:_CheckInAI(FlightGroup,Group,AuftragsNr) local CAPVoice = self.CAPVoice if self.PathToGoogleKey then - CAPVoice = AWACS.CapVoices[math.floor(math.random(1,10))] + CAPVoice = self.CapVoices[math.floor(math.random(1,10))] end FlightGroup:SetSRS(self.PathToSRS,self.CAPGender,self.CAPCulture,CAPVoice,self.Port,self.PathToGoogleKey,"FLIGHT") diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 0d0ce1166..746fff5e3 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -366,7 +366,10 @@ function PLAYERRECCE:SetReferencePoint(Coordinate,Name) if self.RPMarker then self.RPMarker:Remove() end - local text = string.format("%s RP %s\n%s\n%s\n%s",self.Name,Name,Coordinate:ToStringLLDDM(),Coordinate:ToStringLLDMS(),Coordinate:ToStringMGRS()) + local llddm = Coordinate:ToStringLLDDM() + local lldms = Coordinate:ToStringLLDMS() + local mgrs = Coordinate:ToStringMGRS() + local text = string.format("%s RP %s\n%s\n%s\n%s",self.Name,Name,llddm,lldms,mgrs) self.RPMarker = MARKER:New(Coordinate,text) self.RPMarker:ReadOnly() self.RPMarker:ToCoalition(self.Coalition) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 0f6d95c78..1e38829c0 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -770,7 +770,7 @@ function PLAYERTASK:onafterClientAdded(From, Event, To, Client) self:T({From, Event, To}) if Client and self.verbose then local text = string.format("Player %s joined task %03d!",Client:GetPlayerName() or "Generic",self.PlayerTaskNr) - self:I(self.lid..text) + self:T(self.lid..text) end self.timestamp = timer.getAbsTime() return self @@ -925,6 +925,7 @@ do -- @field #boolean ShowMagnetic Also show magnetic angles -- @field #boolean InfoHasCoordinate -- @field #boolean InfoHasLLDDM +-- @field #table PlayerMenuTag -- @extends Core.Fsm#FSM --- @@ -1232,6 +1233,7 @@ PLAYERTASKCONTROLLER = { PlayerFlashMenu = {}, PlayerJoinMenu = {}, PlayerInfoMenu = {}, + PlayerMenuTag = {}, noflaresmokemenu = false, TransmitOnlyWithPlayers = true, buddylasing = false, @@ -2099,7 +2101,7 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType() self:T(self.lid.."_GetTasksPerType") local tasktypes = self:_GetAvailableTaskTypes() - self:T({tasktypes}) + --self:T({tasktypes}) -- Sort tasks per threat level first local datatable = self.TaskQueue:GetDataTable() @@ -3120,25 +3122,26 @@ end -- @param Core.Menu#MENU_BASE topmenu -- @param #table tasktypes -- @param #table taskpertype +-- @param #string newtag -- @return #table taskinfomenu -function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) +function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag) self:T(self.lid.."_BuildTaskInfoMenu") local taskinfomenu = nil if self.taskinfomenu then local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) - local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu) + local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu):SetTag(newtag) local ittypes = {} local itaskmenu = {} + local tnow = timer.getTime() for _tasktype,_data in pairs(tasktypes) do - ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu) + ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu):SetTag(newtag) local tasks = taskpertype[_tasktype] or {} local n = 0 for _,_task in pairs(tasks) do _task = _task -- Ops.PlayerTask#PLAYERTASK local pilotcount = _task:CountClients() local newtext = "]" - local tnow = timer.getTime() -- marker for new tasks if tnow - _task.timestamp < 60 then newtext = "*]" @@ -3151,7 +3154,7 @@ function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) end end - local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task) + local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task):SetTag(newtag) --taskentry:SetTag(playername) itaskmenu[#itaskmenu+1] = taskentry -- keep max items limit @@ -3191,6 +3194,11 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) local group = client:GetGroup() local unknown = self.gettext:GetEntry("UNKNOWN",self.locale) local playername = client:GetPlayerName() or unknown + + local oldtag = self.PlayerMenuTag[playername] + local newtag = playername..timer.getAbsTime() + self.PlayerMenuTag[playername] = newtag + if group and client then --- -- TOPMENU @@ -3202,6 +3210,8 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) if self:_CheckPlayerHasTask(playername) and not fromsuccess then playerhastask = true end local topmenu = nil + --local oldmenu = nil + local rebuilddone = false self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort)) @@ -3215,16 +3225,20 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) -- 2)+3) Join or abort? if joinorabort then self.PlayerMenu[playername]:RemoveSubMenus() - self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) + self.PlayerMenu[playername]:SetTag(newtag) topmenu = self.PlayerMenu[playername] elseif (not playerhastask) or enforced then -- 4) last build > 30 secs? local T0 = timer.getAbsTime() - local TDiff = T0-self.PlayerMenu[playername].MenuTag + local TDiff = T0-self.PlayerMenu[playername].PTTimeStamp self:T("TDiff = "..string.format("%.2d",TDiff)) if TDiff >= self.holdmenutime then - self.PlayerMenu[playername]:RemoveSubMenus() - self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) + --self.PlayerMenu[playername]:RemoveSubMenus() + --oldmenu = self.PlayerMenu[playername] + --self.PlayerMenu[playername] = nil + self.PlayerMenu[playername] = MENU_GROUP_DELAYED:New(group,menuname,self.MenuParent) + self.PlayerMenu[playername]:SetTag(newtag) + self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime() timedbuild = true end topmenu = self.PlayerMenu[playername] @@ -3233,14 +3247,17 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) -- 1) new player# topmenu = MENU_GROUP_DELAYED:New(group,menuname,self.MenuParent) self.PlayerMenu[playername] = topmenu - self.PlayerMenu[playername]:SetTag(timer.getAbsTime()) + self.PlayerMenu[playername]:SetTag(newtag) + self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime() + enforced = true end --- -- ACTIVE TASK MENU --- if playerhastask and enforced then - --self:T("Building Active Task Menus for "..playername) + self:T("Building Active Task Menus for "..playername) + rebuilddone = true local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale) local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale) local menumark = self.gettext:GetEntry("MENUMARK",self.locale) @@ -3248,44 +3265,45 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale) local menuabort = self.gettext:GetEntry("MENUABORT",self.locale) - local active = MENU_GROUP_DELAYED:New(group,menuactive,topmenu) - local info = MENU_GROUP_COMMAND_DELAYED:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client) - local mark = MENU_GROUP_COMMAND_DELAYED:New(group,menumark,active,self._MarkTask,self,group,client) + local active = MENU_GROUP_DELAYED:New(group,menuactive,topmenu):SetTag(newtag) + local info = MENU_GROUP_COMMAND_DELAYED:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client):SetTag(newtag) + local mark = MENU_GROUP_COMMAND_DELAYED:New(group,menumark,active,self._MarkTask,self,group,client):SetTag(newtag) if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then if self.noflaresmokemenu ~= true then -- no smoking/flaring here if A2A or designer has set noflaresmokemenu to true - local smoke = MENU_GROUP_COMMAND_DELAYED:New(group,menusmoke,active,self._SmokeTask,self,group,client) - local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client) + local smoke = MENU_GROUP_COMMAND_DELAYED:New(group,menusmoke,active,self._SmokeTask,self,group,client):SetTag(newtag) + local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client):SetTag(newtag) local IsNight = client:GetCoordinate():IsNight() if IsNight then - local light = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._IlluminateTask,self,group,client) + local light = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._IlluminateTask,self,group,client):SetTag(newtag) end end end - local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client) + local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client):SetTag(newtag) if self.activehasinfomenu and self.taskinfomenu then - --self:T("Building Active-Info Menus for "..playername) + self:T("Building Active-Info Menus for "..playername) local tasktypes = self:_GetAvailableTaskTypes() local taskpertype = self:_GetTasksPerType() if self.PlayerInfoMenu[playername] then - self.PlayerInfoMenu[playername]:RemoveSubMenus() + self.PlayerInfoMenu[playername]:RemoveSubMenus(nil,oldtag) end - self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) + self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag) end elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask and (timedbuild or joinorabort)) then - --self:T("Building Join Menus for "..playername) + self:T("Building Join Menus for "..playername) + rebuilddone = true --- -- JOIN TASK MENU --- local tasktypes = self:_GetAvailableTaskTypes() local taskpertype = self:_GetTasksPerType() local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale) - local joinmenu = MENU_GROUP_DELAYED:New(group,menujoin,topmenu) + local joinmenu = MENU_GROUP_DELAYED:New(group,menujoin,topmenu):SetTag(newtag) local ttypes = {} local taskmenu = {} for _tasktype,_data in pairs(tasktypes) do - ttypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,joinmenu) + ttypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,joinmenu):SetTag(newtag) local tasks = taskpertype[_tasktype] or {} local n = 0 for _,_task in pairs(tasks) do @@ -3305,7 +3323,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) end end - local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task) + local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task):SetTag(newtag) --taskentry:SetTag(playername) taskmenu[#taskmenu+1] = taskentry n = n + 1 @@ -3315,25 +3333,30 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) end end if self.taskinfomenu then - --self:T("Building Join-Info Menus for "..playername) + self:T("Building Join-Info Menus for "..playername) if self.PlayerInfoMenu[playername] then - self.PlayerInfoMenu[playername]:RemoveSubMenus() + self.PlayerInfoMenu[playername]:RemoveSubMenus(nil,oldtag) end - self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype) + self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag) end - elseif self.TaskQueue:Count() == 0 then - -- no tasks (yet) + end + if self.AllowFlash then + local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) + local flashmenu = MENU_GROUP_COMMAND_DELAYED:New(group,flashtext,topmenu,self._SwitchFlashing,self,group,client):SetTag(newtag) + end + if self.TaskQueue:Count() == 0 then + self:T("No open tasks info") local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) - local joinmenu = MENU_GROUP_DELAYED:New(group,menunotasks,topmenu) + local joinmenu = MENU_GROUP_DELAYED:New(group,menunotasks,self.PlayerMenu[playername]):SetTag(newtag) + rebuilddone = true end --- -- REFRESH MENU --- - if self.AllowFlash then - local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) - local flashmenu = MENU_GROUP_COMMAND_DELAYED:New(group,flashtext,self.PlayerMenu[playername],self._SwitchFlashing,self,group,client) + if rebuilddone then + self.PlayerMenu[playername]:RemoveSubMenus(nil,oldtag) + self.PlayerMenu[playername]:Refresh() end - self.PlayerMenu[playername]:Set() end end end From 2c75ea1a187ab6a64513920e45d09ec6183a7649 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Thu, 8 Dec 2022 14:33:50 +0100 Subject: [PATCH 138/603] Update Set.lua Improve GetLast and GetRandom --- Moose Development/Moose/Core/Set.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index d6077667c..a38022e34 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -387,8 +387,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetLast() - - local ObjectName = self.Index[#self.Index] + local tablemax = table.maxn(self.Index) + local ObjectName = self.Index[tablemax] local LastObject = self.Set[ObjectName] self:T3( { LastObject } ) return LastObject @@ -398,8 +398,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetRandom() - - local RandomItem = self.Set[self.Index[math.random( #self.Index )]] + local tablemax = table.maxn(self.Index) + local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] self:T3( { RandomItem } ) return RandomItem end From 7dc8285fd5426338298389ef1313ddcb4000b01b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 9 Dec 2022 12:37:24 +0100 Subject: [PATCH 139/603] #CTLD * Added disallow building in loadzones: my_ctld.nobuildinloadzones = true --- Moose Development/Moose/Ops/CTLD.lua | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 4dabfddd4..62c0a445b 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -711,6 +711,7 @@ do -- my_ctld.droppedbeacontimeout = 600 -- dropped beacon lasts 10 minutes -- my_ctld.usesubcats = false -- use sub-category names for crates, adds an extra menu layer in "Get Crates", useful if you have > 10 crate types. -- my_ctld.placeCratesAhead = false -- place crates straight ahead of the helicopter, in a random way. If true, crates are more neatly sorted. +-- my_ctld.nobuildinloadzones = true -- forbid players to build stuff in LOAD zones if set to `true` -- -- ## 2.1 User functions -- @@ -1087,7 +1088,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.20" +CTLD.version="1.0.21" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1252,6 +1253,9 @@ function CTLD:New(Coalition, Prefixes, Alias) self.usesubcats = false self.subcats = {} + -- disallow building in loadzones + self.nobuildinloadzones = true + local AliaS = string.gsub(self.alias," ","_") self.filename = string.format("CTLD_%s_Persist.csv",AliaS) @@ -2102,10 +2106,10 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) self.CargoCounter = self.CargoCounter + 1 local realcargo = nil if drop then - realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,subcat) + realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,nil,subcat) table.insert(droppedcargo,realcargo) else - realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,subcat) + realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],false,cargotype.PerCrateMass,nil,subcat) Cargo:RemoveStock() end table.insert(self.Spawned_Cargo, realcargo) @@ -2833,6 +2837,14 @@ function CTLD:_BuildCrates(Group, Unit,Engineering) return self end end + if not Engineering and self.nobuildinloadzones then + -- are we in a load zone? + local inloadzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) + if inloadzone then + self:_SendMessage("You cannot build in a loading area, Pilot!", 10, false, Group) + return self + end + end -- get nearby crates local finddist = self.CrateDistance or 35 local crates,number = self:_FindCratesNearby(Group,Unit, finddist,true) -- #table From 90eca9f6a37004a1bd7191910bd52bfe78bd8c02 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 11 Dec 2022 15:51:41 +0100 Subject: [PATCH 140/603] #minor enhancements --- Moose Development/Moose/Core/Set.lua | 8 +++----- Moose Development/Moose/Ops/Awacs.lua | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index a38022e34..3f3b2a401 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -376,7 +376,6 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetFirst() - local ObjectName = self.Index[1] local FirstObject = self.Set[ObjectName] self:T3( { FirstObject } ) @@ -387,7 +386,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetLast() - local tablemax = table.maxn(self.Index) + local tablemax = table.maxn(self.Index) local ObjectName = self.Index[tablemax] local LastObject = self.Set[ObjectName] self:T3( { LastObject } ) @@ -398,7 +397,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetRandom() - local tablemax = table.maxn(self.Index) + local tablemax = table.maxn(self.Index) local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] self:T3( { RandomItem } ) return RandomItem @@ -408,8 +407,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @return #number Count function SET_BASE:Count() - - return self.Index and #self.Index or 0 + return self.Index and table.maxn(self.Index) or 0 end --- Copies the Filter criteria from a given Set (for rebuilding a new Set based on an existing Set). diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 984c32367..a9fd58387 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -17,7 +17,7 @@ -- === -- -- ### Author: **applevangelist** --- @date Last Update November 2022 +-- @date Last Update December 2022 -- @module Ops.AWACS -- @image OPS_AWACS.jpg @@ -104,7 +104,7 @@ do -- @field #boolean NoGroupTags Set to true if you don't want group tags. -- @field #boolean SuppressScreenOutput Set to true to suppress all screen output. -- @field #boolean NoMissileCalls Suppress missile callouts --- @field #boolean PlayerCapAssigment Assign players to CAP tasks when they are logged on +-- @field #boolean PlayerCapAssignment Assign players to CAP tasks when they are logged on -- @field #number GoogleTTSPadding -- @field #number WindowsTTSPadding -- @field #boolean AllowMarkers @@ -356,7 +356,7 @@ do -- testawacs.maxassigndistance = 100 -- Don't assign targets further out than this, in NM. -- testawacs.debug = false -- set to true to produce more log output. -- testawacs.NoMissileCalls = true -- suppress missile callouts --- testawacs.PlayerCapAssigment = true -- no intercept task assignments for players +-- testawacs.PlayerCapAssignment = true -- no intercept task assignments for players -- testawacs.invisible = false -- set AWACS to be invisible to hostiles -- testawacs.immortal = false -- set AWACS to be immortal -- -- By default, the radio queue is checked every 10 secs. This is altered by the calculated length of the sentence to speak @@ -578,7 +578,7 @@ AWACS = { NoMissileCalls = true, GoogleTTSPadding = 1, WindowsTTSPadding = 2.5, - PlayerCapAssigment = true, + PlayerCapAssignment = true, AllowMarkers = false, PlayerStationName = nil, GCI = false, @@ -1123,7 +1123,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station self.MenuStrict = true self.maxassigndistance = 100 --nm self.NoMissileCalls = true - self.PlayerCapAssigment = true + self.PlayerCapAssignment = true -- managed groups self.ManagedGrps = {} -- #table of #AWACS.ManagedGroup entries @@ -3587,13 +3587,15 @@ function AWACS:_SetClientMenus() local bogeydope = MENU_GROUP_COMMAND:New(cgrp,"Bogey Dope",basemenu,self._BogeyDope,self,cgrp) local picture = MENU_GROUP_COMMAND:New(cgrp,"Picture",basemenu,self._Picture,self,cgrp) local declare = MENU_GROUP_COMMAND:New(cgrp,"Declare",basemenu,self._Declare,self,cgrp) - local tasking = MENU_GROUP:New(cgrp,"Tasking",basemenu) local showtask = MENU_GROUP_COMMAND:New(cgrp,"Showtask",tasking,self._Showtask,self,cgrp) - local commit = MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp) - local unable = MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp) - local abort = MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp) - --local judy = MENU_GROUP_COMMAND:New(cgrp,"Judy",tasking,self._Judy,self,cgrp) + + if self.PlayerCapAssignment then + local commit = MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp) + local unable = MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp) + local abort = MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp) + --local judy = MENU_GROUP_COMMAND:New(cgrp,"Judy",tasking,self._Judy,self,cgrp) + end if self.AwacsROE == AWACS.ROE.POLICE or self.AwacsROE == AWACS.ROE.VID then local vid = MENU_GROUP:New(cgrp,"VID as",tasking) @@ -5957,7 +5959,7 @@ function AWACS:onafterStatus(From, Event, To) local AI, Humans = self:_GetIdlePilots() -- assign Pilot if there are targets and available Pilots, prefer Humans to AI -- DONE - Implemented AI First, Humans laters - need to work out how to loop the targets to assign a pilot - if outcome and #Humans > 0 and self.PlayerCapAssigment then + if outcome and #Humans > 0 and self.PlayerCapAssignment then -- add a task for AI self:_AssignPilotToTarget(Humans,targets) end From 2e4f7956ac67629ad14b6390b20d2c91c1468e49 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 12 Dec 2022 16:23:23 +0100 Subject: [PATCH 141/603] #PlayerTask * typename options added --- Moose Development/Moose/Ops/PlayerTask.lua | 88 ++++++++++++++++++++- Moose Development/Moose/Wrapper/Airbase.lua | 2 +- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 1e38829c0..b6fae66a1 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -57,6 +57,7 @@ do -- @field #table NextTaskSuccess -- @field #table NextTaskFailure -- @field #string FinalState +-- @field #string TypeName -- @extends Core.Fsm#FSM @@ -926,6 +927,7 @@ do -- @field #boolean InfoHasCoordinate -- @field #boolean InfoHasLLDDM -- @field #table PlayerMenuTag +-- @field #boolean UseTypeNames -- @extends Core.Fsm#FSM --- @@ -1112,6 +1114,13 @@ do -- BRIEFING = "Briefing", -- TARGETLOCATION ="Target location", -- COORDINATE = "Coordinate", +-- INFANTRY = "Infantry", +-- TECHNICAL = "Technical", +-- ARTILLERY = "Artillery", +-- TANKS = "Tanks", +-- AIRDEFENSE = "Airdefense", +-- SAM = "SAM", +-- GROUP = "Group", -- }, -- -- e.g. @@ -1243,6 +1252,7 @@ PLAYERTASKCONTROLLER = { ShowMagnetic = true, InfoHasLLDDM = false, InfoHasCoordinate = false, + UseTypeNames = false, } --- @@ -1341,6 +1351,13 @@ PLAYERTASKCONTROLLER.Messages = { BRIEFING = "Briefing", TARGETLOCATION ="Target location", COORDINATE = "Coordinate", + INFANTRY = "Infantry", + TECHNICAL = "Technical", + ARTILLERY = "Artillery", + TANKS = "Tanks", + AIRDEFENSE = "Airdefense", + SAM = "SAM", + GROUP = "Group", }, DE = { TASKABORT = "Auftrag abgebrochen!", @@ -1406,12 +1423,19 @@ PLAYERTASKCONTROLLER.Messages = { BRIEFING = "Briefing", TARGETLOCATION ="Zielposition", COORDINATE = "Koordinate", + INFANTRY = "Infantrie", + TECHNICAL = "Technische", + ARTILLERY = "Artillerie", + TANKS = "Panzer", + AIRDEFENSE = "Flak", + SAM = "Luftabwehr", + GROUP = "Einheit", }, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.51" +PLAYERTASKCONTROLLER.version="0.1.52" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -1469,6 +1493,8 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.noflaresmokemenu = false self.ShowMagnetic = true + + self.UseTypeNames = false if ClientFilter then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart() @@ -1607,6 +1633,24 @@ function PLAYERTASKCONTROLLER:_InitLocalization() return self end +--- [User] Show target menu entries of type names for GROUND targets (off by default!), e.g. "Tank Group..." +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetEnableUseTypeNames() + self:T(self.lid.."SetEnableUseTypeNames") + self.UseTypeNames = true + return self +end + +--- [User] Do not show target menu entries of type names for GROUND targets +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetDisableUseTypeNames() + self:T(self.lid.."SetDisableUseTypeNames") + self.UseTypeNames = false + return self +end + --- [User] Set flash directions option for player (player based info) -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff Set to `true` to switch on and `false` to switch off. Default is OFF. @@ -2134,7 +2178,7 @@ end function PLAYERTASKCONTROLLER:_CheckTargetQueue() self:T(self.lid.."_CheckTargetQueue") if self.TargetQueue:Count() > 0 then - local object = self.TargetQueue:Pull() + local object = self.TargetQueue:Pull() -- Wrapper.Positionable#POSITIONABLE local target = TARGET:New(object) if object.menuname then target.menuname = object.menuname @@ -2142,6 +2186,38 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue() target.freetext = object.freetext end end + + if self.UseTypeNames and object:IsGround() then + -- * Threat level 0: Unit is unarmed. + -- * Threat level 1: Unit is infantry. + -- * Threat level 2: Unit is an infantry vehicle. + -- * Threat level 3: Unit is ground artillery. + -- * Threat level 4: Unit is a tank. + -- * Threat level 5: Unit is a modern tank or ifv with ATGM. + -- * Threat level 6: Unit is a AAA. + -- * Threat level 7: Unit is a SAM or manpad, IR guided. + -- * Threat level 8: Unit is a Short Range SAM, radar guided. + -- * Threat level 9: Unit is a Medium Range SAM, radar guided. + -- * Threat level 10: Unit is a Long Range SAM, radar guided. + local threat = object:GetThreatLevel() + local typekey = "INFANTRY" + if threat == 0 or threat == 2 then + typekey = "TECHNICAL" + elseif threat == 3 then + typekey = "ARTILLERY" + elseif threat == 4 or threat == 5 then + typekey = "TANKS" + elseif threat == 6 or threat == 7 then + typekey = "AIRDEFENSE" + elseif threat >= 8 then + typekey = "SAM" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + local gname = self.gettext:GetEntry("GROUP",self.locale) + target.TypeName = string.format("%s %s",typename,gname) + --self:T(self.lid.."Target TypeName = "..target.TypeName) + end + self:_AddTask(target) end return self @@ -2616,6 +2692,7 @@ function PLAYERTASKCONTROLLER:_AddTask(Target) end task.coalition = self.Coalition + task.TypeName = Target.TypeName if type == AUFTRAG.Type.BOMBRUNWAY then -- task to handle event shot @@ -3154,6 +3231,13 @@ function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) end end + if self.UseTypeNames then + if _task.TypeName then + --local name = self.gettext:GetEntry(_task.TypeName,self.locale) + text = string.format("%s (%03d) [%d%s",_task.TypeName,_task.PlayerTaskNr,pilotcount,newtext) + --self:T(self.lid.."Menu text = "..text) + end + end local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task):SetTag(newtag) --taskentry:SetTag(playername) itaskmenu[#itaskmenu+1] = taskentry diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index a2fd0577a..cb8e35175 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -839,7 +839,7 @@ end -- Black listed spots overrule white listed spots. -- **NOTE** that terminal IDs are not necessarily the same as those displayed in the mission editor! -- @param #AIRBASE self --- @param #table TerminalIdBlacklist Table of white listed terminal IDs. +-- @param #table TerminalIdWhitelist Table of white listed terminal IDs. -- @return #AIRBASE self -- @usage AIRBASE:FindByName("Batumi"):SetParkingSpotWhitelist({2, 3, 4}) --Only allow terminal IDs 2, 3, 4 function AIRBASE:SetParkingSpotWhitelist(TerminalIdWhitelist) From 4368bef32f80c8e80aba97574a4d02c24071303a Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 13 Dec 2022 16:59:06 +0100 Subject: [PATCH 142/603] Update Set.lua Added SET_CLIENT:FilterCallsigns() and SET_CLIENT:FilterPlayernames() --- Moose Development/Moose/Core/Set.lua | 122 ++++++++++++++++++++------- 1 file changed, 91 insertions(+), 31 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 3f3b2a401..e7dbff714 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -386,7 +386,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetLast() - local tablemax = table.maxn(self.Index) + local tablemax = table.maxn(self.Index) local ObjectName = self.Index[tablemax] local LastObject = self.Set[ObjectName] self:T3( { LastObject } ) @@ -397,7 +397,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetRandom() - local tablemax = table.maxn(self.Index) + local tablemax = table.maxn(self.Index) local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] self:T3( { RandomItem } ) return RandomItem @@ -3808,6 +3808,8 @@ do -- SET_CLIENT Countries = nil, ClientPrefixes = nil, Zones = nil, + Playernames = nil, + Callsigns = nil, }, FilterMeta = { Coalitions = { @@ -3880,6 +3882,40 @@ do -- SET_CLIENT return ClientFound end + --- Builds a set of clients of certain callsigns. + -- @param #SET_CLIENT self + -- @param #string Callsigns Can be a string e.g. "Ford", or a table of strings e.g. {"Ford","Enfield","Chevy"} + -- @return #SET_CLIENT self + function SET_CLIENT:Filtercallsigns( Callsigns ) + if not self.Filter.Callsigns then + self.Filter.Callsigns = {} + end + if type( Callsigns ) ~= "table" then + Callsigns = { Callsigns } + end + for callsignID, callsign in pairs( Callsigns ) do + self.Filter.Coalitions[Callsigns] = Callsigns + end + return self + end + + --- Builds a set of clients of certain playernames. + -- @param #SET_CLIENT self + -- @param #string Playernames Can be a string e.g. "Apple", or a table of strings e.g. {"Walter","Hermann","Gonzo"} + -- @return #SET_CLIENT self + function SET_CLIENT:FilterPlayernames( Playernames ) + if not self.Filter.Playernames then + self.Filter.Playernames = {} + end + if type( Playernames ) ~= "table" then + Playernames = { Playernames } + end + for PlayernameID, Playernames in pairs( Playernames ) do + self.Filter.Coalitions[Playernames] = Playernames + end + return self + end + --- Builds a set of clients of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_CLIENT self @@ -4235,20 +4271,44 @@ do -- SET_CLIENT self:T( { "Evaluated Prefix", MClientPrefix } ) MClientInclude = MClientInclude and MClientPrefix end - end - if self.Filter.Zones then - local MClientZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - self:T3( "Zone:", ZoneName ) - local unit = MClient:GetClientGroupUnit() - if unit and unit:IsInZone(Zone) then - MClientZone = true - end - end - MClientInclude = MClientInclude and MClientZone - end - + if self.Filter.Zones then + local MClientZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + self:T3( "Zone:", ZoneName ) + local unit = MClient:GetClientGroupUnit() + if unit and unit:IsInZone(Zone) then + MClientZone = true + end + end + MClientInclude = MClientInclude and MClientZone + end + + if self.Filter.Playernames then + local MClientPlayername = false + local playername = MClient:GetPlayerName() + for _,_Playername in pairs(self.Filter.Playernames) do + if playername and playername == _Playername then + MClientPlayername = true + end + end + self:T( { "Evaluated Playername", MClientPlayername } ) + MClientInclude = MClientInclude and MClientPlayername + end + + if self.Filter.Callsigns then + local MClientCallsigns = false + local callsign = MClient:GetCallsign() + for _,_Callsign in pairs(self.Filter.Callsigns) do + if callsign and string.find(callsign,_Callsign) then + MClientCallsigns = true + end + end + self:T( { "Evaluated Callsign", MClientCallsigns } ) + MClientInclude = MClientInclude and MClientCallsigns + end + + end self:T2( MClientInclude ) return MClientInclude end @@ -6786,24 +6846,24 @@ do -- SET_SCENERY -- @return #SET_SCENERY -- @usage -- -- Define a new SET_SCENERY Object. This Object will contain a reference to all added Scenery Objects. - -- ZoneSet = SET_ZONE:New():FilterPrefixes("Bridge"):FilterOnce() - -- mysceneryset = SET_SCENERY:New(ZoneSet) + -- ZoneSet = SET_ZONE:New():FilterPrefixes("Bridge"):FilterOnce() + -- mysceneryset = SET_SCENERY:New(ZoneSet) function SET_SCENERY:New(ZoneSet) - - local zoneset = {} + + local zoneset = {} -- Inherits from BASE local self = BASE:Inherit( self, SET_BASE:New( zoneset ) ) -- Core.Set#SET_SCENERY - - local zonenames = {} - - if ZoneSet then - for _,_zone in pairs(ZoneSet.Set) do - self:T("Zone type handed: "..tostring(_zone.ClassName)) - table.insert(zonenames,_zone:GetName()) - end - self:AddSceneryByName(zonenames) - end - + + local zonenames = {} + + if ZoneSet then + for _,_zone in pairs(ZoneSet.Set) do + self:T("Zone type handed: "..tostring(_zone.ClassName)) + table.insert(zonenames,_zone:GetName()) + end + self:AddSceneryByName(zonenames) + end + return self end @@ -6986,6 +7046,6 @@ do -- SET_SCENERY -- @return #SET_SCENERY self function SET_SCENERY:IsIncludeObject( MScenery ) self:F2( MScenery ) - return true + return true end end From 9cf209d62b88af6a1897b34fbadb3738f3e003c5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 14 Dec 2022 09:42:10 +0100 Subject: [PATCH 143/603] #SET_Client additions --- Moose Development/Moose/Core/Set.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index e7dbff714..42faef612 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -3808,8 +3808,8 @@ do -- SET_CLIENT Countries = nil, ClientPrefixes = nil, Zones = nil, - Playernames = nil, - Callsigns = nil, + Playernames = nil, + Callsigns = nil, }, FilterMeta = { Coalitions = { @@ -3884,7 +3884,7 @@ do -- SET_CLIENT --- Builds a set of clients of certain callsigns. -- @param #SET_CLIENT self - -- @param #string Callsigns Can be a string e.g. "Ford", or a table of strings e.g. {"Ford","Enfield","Chevy"} + -- @param #string Callsigns Can be a string e.g. "Ford", or a table of strings e.g. {"Uzi","Enfield","Chevy"} -- @return #SET_CLIENT self function SET_CLIENT:Filtercallsigns( Callsigns ) if not self.Filter.Callsigns then @@ -4286,9 +4286,9 @@ do -- SET_CLIENT if self.Filter.Playernames then local MClientPlayername = false - local playername = MClient:GetPlayerName() + local playername = MClient:GetPlayerName() or "Unknown" for _,_Playername in pairs(self.Filter.Playernames) do - if playername and playername == _Playername then + if playername and string.find(playername,_Playername) then MClientPlayername = true end end From 52d20ed8efd25e4b11af5f3d2b343be0bac56b44 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 14 Dec 2022 14:48:49 +0100 Subject: [PATCH 144/603] Additions --- Moose Development/Moose/Core/Set.lua | 12 ++++++------ Moose Development/Moose/Ops/PlayerTask.lua | 13 +++++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 42faef612..7008e691d 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -3884,9 +3884,9 @@ do -- SET_CLIENT --- Builds a set of clients of certain callsigns. -- @param #SET_CLIENT self - -- @param #string Callsigns Can be a string e.g. "Ford", or a table of strings e.g. {"Uzi","Enfield","Chevy"} + -- @param #string Callsigns Can be a single string e.g. "Ford", or a table of strings e.g. {"Uzi","Enfield","Chevy"}. Refers to the callsigns as they can be set in the mission editor. -- @return #SET_CLIENT self - function SET_CLIENT:Filtercallsigns( Callsigns ) + function SET_CLIENT:FilterCallsigns( Callsigns ) if not self.Filter.Callsigns then self.Filter.Callsigns = {} end @@ -3894,14 +3894,14 @@ do -- SET_CLIENT Callsigns = { Callsigns } end for callsignID, callsign in pairs( Callsigns ) do - self.Filter.Coalitions[Callsigns] = Callsigns + self.Filter.Callsigns[callsign] = callsign end return self end --- Builds a set of clients of certain playernames. -- @param #SET_CLIENT self - -- @param #string Playernames Can be a string e.g. "Apple", or a table of strings e.g. {"Walter","Hermann","Gonzo"} + -- @param #string Playernames Can be a single string e.g. "Apple", or a table of strings e.g. {"Walter","Hermann","Gonzo"}. Useful if you have e.g. a common squadron prefix. -- @return #SET_CLIENT self function SET_CLIENT:FilterPlayernames( Playernames ) if not self.Filter.Playernames then @@ -3910,8 +3910,8 @@ do -- SET_CLIENT if type( Playernames ) ~= "table" then Playernames = { Playernames } end - for PlayernameID, Playernames in pairs( Playernames ) do - self.Filter.Coalitions[Playernames] = Playernames + for PlayernameID, playername in pairs( Playernames ) do + self.Filter.Playernames[playername] = playername end return self end diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index b6fae66a1..675f69db9 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1435,7 +1435,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.52" +PLAYERTASKCONTROLLER.version="0.1.53" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2731,6 +2731,7 @@ end -- @param #PLAYERTASKCONTROLLER self -- @param Ops.PlayerTask#PLAYERTASK PlayerTask -- @param #boolean Silent If true, make no "has new task" announcement +-- @param #boolen TaskFilter If true, apply the white/black-list task filters here, also -- @return #PLAYERTASKCONTROLLER self -- @usage -- Example to create a PLAYERTASK of type CTLD and give Players 10 minutes to complete: @@ -2751,9 +2752,17 @@ end -- ) -- -- taskmanager:AddPlayerTaskToQueue(PlayerTask) -function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent) +function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent,TaskFilter) self:T(self.lid.."AddPlayerTaskToQueue") if PlayerTask and PlayerTask.ClassName and PlayerTask.ClassName == "PLAYERTASK" then + if TaskFilter then + if self.UseWhiteList and (not self:_CheckTaskTypeAllowed(PlayerTask.Type)) then + return self + end + if self.UseBlackList and self:_CheckTaskTypeDisallowed(PlayerTask.Type) then + return self + end + end PlayerTask:_SetController(self) PlayerTask:SetCoalition(self.Coalition) self.TaskQueue:Push(PlayerTask) From df4cf982dd42ac54ad1f482e81d431682d5233d7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 15 Dec 2022 11:47:15 +0100 Subject: [PATCH 145/603] #PLAYERTASK - additions for multiple setups --- Moose Development/Moose/Ops/PlayerTask.lua | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 675f69db9..08468cb48 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1435,14 +1435,14 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.53" +PLAYERTASKCONTROLLER.version="0.1.54" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self -- @param #string Name Name of this controller -- @param #number Coalition of this controller, e.g. coalition.side.BLUE -- @param #string Type Type of the tasks controlled, defaults to PLAYERTASKCONTROLLER.Type.A2G --- @param #string ClientFilter (optional) Additional prefix filter for the SET_CLIENT +-- @param #string ClientFilter (optional) Additional prefix filter for the SET_CLIENT. Can be handed as @{Core.Set#SET_CLIENT} also. -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) @@ -1495,10 +1495,18 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.ShowMagnetic = true self.UseTypeNames = false + + local IsClientSet = false + + if ClientFilter and type(ClientFilter) == "table" and ClientFilter.ClassName and ClientFilter.ClassName == "SET_CLIENT" then + -- we have a predefined SET_CLIENT + self.ClientSet = ClientFilter + IsClientSet = true + end - if ClientFilter then + if ClientFilter and not IsClientSet then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart() - else + elseif not IsClientSet then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterStart() end @@ -2007,6 +2015,9 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) end elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then + if self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then + return self + end self:T(self.lid.."Event for player: "..EventData.IniPlayerName) local frequency = self.Frequency local freqtext = "" @@ -3282,7 +3293,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) end for _,_client in pairs(clients) do - if _client then + if _client and _client:IsAlive() then local client = _client -- Wrapper.Client#CLIENT local group = client:GetGroup() local unknown = self.gettext:GetEntry("UNKNOWN",self.locale) @@ -3768,7 +3779,7 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To) self:_BuildMenus(nil,enforcedmenu) if self.verbose then - local text = string.format("New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",targetcount,taskcount,playercount,assignedtasks) + local text = string.format("%s | New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",self.MenuName, targetcount,taskcount,playercount,assignedtasks) self:I(text) end From b8e2d438c68f8f9b8706e1888eeb6dbea7f91ace Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 15 Dec 2022 11:47:36 +0100 Subject: [PATCH 146/603] #SET_CLIENT * Additions --- Moose Development/Moose/Core/Set.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 7008e691d..f68589382 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4203,9 +4203,10 @@ do -- SET_CLIENT if self.Filter.Active ~= nil then local MClientActive = false - if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true) then + if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true and MClient:IsAlive() == true) then MClientActive = true end + --self:I( { "Evaluated Active", MClientActive } ) MClientInclude = MClientInclude and MClientActive end @@ -4287,6 +4288,7 @@ do -- SET_CLIENT if self.Filter.Playernames then local MClientPlayername = false local playername = MClient:GetPlayerName() or "Unknown" + --self:I(playername) for _,_Playername in pairs(self.Filter.Playernames) do if playername and string.find(playername,_Playername) then MClientPlayername = true @@ -4299,6 +4301,7 @@ do -- SET_CLIENT if self.Filter.Callsigns then local MClientCallsigns = false local callsign = MClient:GetCallsign() + --self:I(callsign) for _,_Callsign in pairs(self.Filter.Callsigns) do if callsign and string.find(callsign,_Callsign) then MClientCallsigns = true From d88b31f43a73378d45b3677658055a190360fa4b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 15 Dec 2022 18:28:24 +0100 Subject: [PATCH 147/603] #UTILS * UTILS.LoadSetOfStatics(Path,Filename) ignore statics which do not exist --- Moose Development/Moose/Utilities/Utils.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index dd073f753..0e3b3d37d 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -2414,11 +2414,14 @@ function UTILS.LoadSetOfStatics(Path,Filename) local dataset = UTILS.Split(_entry,",") -- staticname,position.x,position.y,position.z local staticname = dataset[1] - local posx = tonumber(dataset[2]) - local posy = tonumber(dataset[3]) - local posz = tonumber(dataset[4]) - local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz}) - datatable:AddObject(STATIC:FindByName(staticname,false)) + --local posx = tonumber(dataset[2]) + --local posy = tonumber(dataset[3]) + --local posz = tonumber(dataset[4]) + --local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz}) + local StaticObject = STATIC:FindByName(staticname,false) + if StaticObject then + datatable:AddObject(StaticObject) + end end else return nil From a151c5587aaf94db3d1be2a9aae4f183798981ed Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 19 Dec 2022 13:07:01 +0100 Subject: [PATCH 148/603] AUFTRAG - fix for racetrack orbit --- Moose Development/Moose/Ops/Auftrag.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index fa2d49679..c05fa5a4c 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -6087,7 +6087,7 @@ function AUFTRAG:GetDCSMissionTask() --end -- Create orbit task. - local DCStask=CONTROLLABLE.TaskOrbit(nil, COORDINATE:NewFromVec2(orbitVec2), self.orbitAltitude, self.orbitSpeed, orbitRaceTrack) + local DCStask=CONTROLLABLE.TaskOrbit(nil, COORDINATE:NewFromVec2(orbitVec2), self.orbitAltitude, self.orbitSpeed, COORDINATE:NewFromVec2(orbitRaceTrack)) -- Add DCS task. table.insert(DCStasks, DCStask) From 9c665f83d3d534ddae80bccf9d1be70952d69bf1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 19 Dec 2022 13:52:49 +0100 Subject: [PATCH 149/603] #Fixes --- Moose Development/Moose/Ops/CTLD.lua | 3 ++- Moose Development/Moose/Wrapper/Unit.lua | 21 ++++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 62c0a445b..3e2f92ef5 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -2106,6 +2106,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) self.CargoCounter = self.CargoCounter + 1 local realcargo = nil if drop then + --CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory) realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,nil,subcat) table.insert(droppedcargo,realcargo) else @@ -4778,7 +4779,7 @@ end for _,_cargo in pairs (stcstable) do local cargo = _cargo -- #CTLD_CARGO local object = cargo:GetPositionable() -- Wrapper.Static#STATIC - if object and object:IsAlive() and cargo:WasDropped() then + if object and object:IsAlive() and (cargo:WasDropped() or not cargo:HasMoved()) then statics[#statics+1] = cargo end end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 59474de9c..428676ec7 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -325,14 +325,19 @@ function UNIT:IsAlive() local DCSUnit = self:GetDCSObject() -- DCS#Unit if DCSUnit then - local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() + local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() and DCSUnit:getLife() > 1 return UnitIsAlive end return nil end - +--- Returns if the Unit is dead. +-- @param #UNIT self +-- @return #boolean `true` if Unit is dead, else false or nil if the unit does not exist +function UNIT:IsDead() + return not self:IsAlive() +end --- Returns the Unit's callsign - the localized string. -- @param #UNIT self @@ -626,7 +631,7 @@ function UNIT:IsFuelSupply() return false end ---- Returns the unit's group if it exist and nil otherwise. +--- Returns the unit's group if it exists and nil otherwise. -- @param Wrapper.Unit#UNIT self -- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist. function UNIT:GetGroup() @@ -635,8 +640,14 @@ function UNIT:GetGroup() local DCSUnit = self:GetDCSObject() if DCSUnit then - local UnitGroup = GROUP:FindByName( DCSUnit:getGroup():getName() ) - return UnitGroup + local grp = DCSUnit:getGroup() + if grp then + local UnitGroup = GROUP:FindByName( grp:getName() ) + return UnitGroup + else + local UnitGroup = GROUP:FindByName(self.GroupName) + return UnitGroup + end end return nil From c08f79de50243102730c8b45ccb55eb2f18d5acd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 19 Dec 2022 14:03:35 +0100 Subject: [PATCH 150/603] #CTLD --- Moose Development/Moose/Ops/CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 3e2f92ef5..17149318d 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1088,7 +1088,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.21" +CTLD.version="1.0.22" --- Instantiate a new CTLD. -- @param #CTLD self From aa072b5ad8cb72f5c796ab0108399c5bae031747 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 19 Dec 2022 16:12:41 +0100 Subject: [PATCH 151/603] fixes --- Moose Development/Moose/Core/Set.lua | 7 +----- Moose Development/Moose/Wrapper/Unit.lua | 30 ++++++++++-------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index f68589382..4f2a76acb 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1952,12 +1952,7 @@ do -- SET_UNIT -- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. -- * @{#SET_UNIT.ForEachUnitInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence completely in a @{Core.Zone}, providing the UNIT object and optional parameters to the called function. -- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence not in a @{Core.Zone}, providing the UNIT object and optional parameters to the called function. - -- - -- Planned iterators methods in development are (so these are not yet available): - -- - -- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT. - -- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Core.Zone}, providing the UNIT and optional parameters to the called function. - -- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Core.Zone}, providing the UNIT and optional parameters to the called function. + -- * @{#SET_UNIT:ForEachUnitPerThreatLevel}: Iterate the SET_UNIT **sorted *per Threat Level** and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters -- -- ## 5) SET_UNIT atomic methods -- diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 428676ec7..0b6814029 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -325,7 +325,7 @@ function UNIT:IsAlive() local DCSUnit = self:GetDCSObject() -- DCS#Unit if DCSUnit then - local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() and DCSUnit:getLife() > 1 + local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() -- and DCSUnit:getLife() > 1 return UnitIsAlive end @@ -635,27 +635,23 @@ end -- @param Wrapper.Unit#UNIT self -- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist. function UNIT:GetGroup() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local grp = DCSUnit:getGroup() - if grp then - local UnitGroup = GROUP:FindByName( grp:getName() ) - return UnitGroup - else - local UnitGroup = GROUP:FindByName(self.GroupName) - return UnitGroup + self:F2( self.UnitName ) + local UnitGroup = GROUP:FindByName(self.GroupName) + if UnitGroup then + return UnitGroup + else + local DCSUnit = self:GetDCSObject() + if DCSUnit then + local grp = DCSUnit:getGroup() + if grp then + local UnitGroup = GROUP:FindByName( grp:getName() ) + return UnitGroup + end end end - return nil end - --- Need to add here functions to check if radar is on and which object etc. - --- Returns the prefix name of the DCS Unit. A prefix name is a part of the name before a '#'-sign. -- DCS Units spawned with the @{Core.Spawn#SPAWN} class contain a '#'-sign to indicate the end of the (base) DCS Unit name. -- The spawn sequence number and unit number are contained within the name after the '#' sign. From 3564843e1b217a32a6171a9b5e66bcfb5029c553 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 20 Dec 2022 18:18:44 +0100 Subject: [PATCH 152/603] #fixes --- Moose Development/Moose/Ops/Auftrag.lua | 19 +++++++------------ Moose Development/Moose/Ops/PlayerTask.lua | 10 +++++++++- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index c05fa5a4c..52904d0ec 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -6075,20 +6075,15 @@ function AUFTRAG:GetDCSMissionTask() -- Race-track vector. orbitRaceTrack=UTILS.Vec2Translate(orbitVec2, self.orbitLeg, heading) end - - -- Debug - --UTILS.RemoveMark(self.orbitCenterMarkID) - --self.orbitCenterMarkID=COORDINATE:NewFromVec2(orbitVec2):MarkToAll("Orbit Center") - - -- Debug show arrow. - --if orbitRaceTrack then - --UTILS.RemoveMark(self.orbitArrowMarkID) - --self.orbitArrowMarkID=COORDINATE:NewFromVec2(orbitVec2):ArrowToAll(COORDINATE:NewFromVec2(orbitRaceTrack)) - --end + + local orbitRaceTrackCoord = nil + if orbitRaceTrack then + orbitRaceTrackCoord = COORDINATE:NewFromVec2(orbitRaceTrack) + end -- Create orbit task. - local DCStask=CONTROLLABLE.TaskOrbit(nil, COORDINATE:NewFromVec2(orbitVec2), self.orbitAltitude, self.orbitSpeed, COORDINATE:NewFromVec2(orbitRaceTrack)) - + local DCStask=CONTROLLABLE.TaskOrbit(nil, COORDINATE:NewFromVec2(orbitVec2), self.orbitAltitude, self.orbitSpeed, orbitRaceTrackCoord) + -- Add DCS task. table.insert(DCStasks, DCStask) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 08468cb48..3485cd4c1 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -96,7 +96,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.10" +PLAYERTASK.version="0.1.11" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -285,6 +285,14 @@ function PLAYERTASK:GetCoalition() return self.coalition end +--- [User] Get the Ops.Target#TARGET object for this task +-- @param #PLAYERTASK self +-- @return Ops.Target#TARGET Target +function PLAYERTASK:GetTarget() + self:T(self.lid.."GetTarget") + return self.Target +end + --- [USER] Add a free text description to this task. -- @param #PLAYERTASK self -- @param #string Text From 669e55d43519bfe983556e72014435633ca37604 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 21 Dec 2022 12:52:24 +0100 Subject: [PATCH 153/603] #Fixes --- Moose Development/Moose/Core/Set.lua | 22 ++++++++++++++++++++++ Moose Development/Moose/Ops/PlayerTask.lua | 1 + Moose Development/Moose/Ops/Target.lua | 11 ++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 4f2a76acb..1ea3eb03e 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -6990,7 +6990,29 @@ do -- SET_SCENERY return CountU end + + --- Get a table of alive objects. + -- @param #SET_GROUP self + -- @return #table Table of alive objects + -- @return Core.Set#SET_SCENERY SET of alive objects + function SET_SCENERY:GetAliveSet() + self:F2() + local AliveSet = SET_SCENERY:New() + + -- Clean the Set before returning with only the alive Groups. + for GroupName, GroupObject in pairs( self.Set ) do + local GroupObject = GroupObject -- Wrapper.Group#GROUP + if GroupObject then + if GroupObject:IsAlive() then + AliveSet:Add( GroupName, GroupObject ) + end + end + end + + return AliveSet.Set or {}, AliveSet + end + --- Iterate the SET_SCENERY and call an iterator function for each **alive** SCENERY, providing the SCENERY and optional parameters. -- @param #SET_SCENERY self -- @param #function IteratorFunction The function that will be called when there is an alive SCENERY in the SET_SCENERY. The function needs to accept a SCENERY parameter. diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 3485cd4c1..e8a415289 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -2821,6 +2821,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force) Task:AddClient(Client) local joined = self.gettext:GetEntry("PILOTJOINEDTASK",self.locale) -- PILOTJOINEDTASK = "%s, %s. You have been assigned %s task %03d", + --self:I(string.format("Task %s | TaskType %s | Number %s | Type %s",self.MenuName or self.Name, Task.TTSType, tonumber(Task.PlayerTaskNr),type(Task.PlayerTaskNr))) local text = string.format(joined,ttsplayername, self.MenuName or self.Name, Task.TTSType, Task.PlayerTaskNr) self:T(self.lid..text) if not self.NoScreenOutput then diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 012eb7542..96b96715b 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -617,7 +617,7 @@ function TARGET:onafterStatus(From, Event, To) -- Log output verbose=1. if self.verbose>=1 then local text=string.format("%s: Targets=%d/%d Life=%.1f/%.1f Damage=%.1f", fsmstate, self:CountTargets(), self.N0, self:GetLife(), self:GetLife0(), self:GetDamage()) - if self:CountTargets() == 0 then + if self:CountTargets() == 0 or self:GetDamage() >= 100 then text=text.." Dead!" elseif damaged then text=text.." Damaged!" @@ -636,7 +636,7 @@ function TARGET:onafterStatus(From, Event, To) self:I(self.lid..text) end - if self:CountTargets() == 0 then + if self:CountTargets() == 0 or self:GetDamage() >= 100 then self:Dead() end @@ -935,6 +935,9 @@ function TARGET:_AddObject(Object) target.Coordinate=scenery:GetCoordinate() target.Life0=scenery:GetLife0() + + if target.Life0==0 then target.Life0 = 1 end + target.Life=scenery:GetLife() target.N0=target.N0+1 @@ -1071,7 +1074,9 @@ function TARGET:GetTargetLife(Target) elseif Target.Type==TARGET.ObjectType.STATIC then if Target.Object and Target.Object:IsAlive() then - return 1 + local life=Target.Object:GetLife() + return life + --return 1 else return 0 end From e74c377bfd3c268d124737abb7ff2985daad43ac Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 21 Dec 2022 14:06:28 +0100 Subject: [PATCH 154/603] #CTLD * Added option to inject troops into helo --- Moose Development/Moose/Ops/CTLD.lua | 69 +++++++++++++++++++----- Moose Development/Moose/Wrapper/Unit.lua | 3 -- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 17149318d..4b90a90f5 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1088,7 +1088,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.22" +CTLD.version="1.0.23" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1648,12 +1648,54 @@ function CTLD:_SendMessage(Text, Time, Clearscreen, Group) return self end +--- (Internal) Find a troops CTLD_CARGO object in stock +-- @param #CTLD self +-- @param #string Name of the object +-- @return #CTLD_CARGO Cargo object, nil if it cannot be found +function CTLD:_FindTroopsCargoObject(Name) + self:T(self.lid .. " _FindTroopsCargoObject") + local cargo = nil + for _,_cargo in pairs(self.Cargo_Troops)do + local cargo = _cargo -- #CTLD_CARGO + if cargo.Name == Name then + return cargo + end + end + return nil +end + +--- (User) Pre-load troops into a helo, e.g. for airstart. Unit **must** be alive in-game, i.e. player has taken the slot! +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object +-- @param #string Troopname The name of the Troops to be loaded. Must be created prior in the CTLD setup! +-- @return #CTLD self +-- @usage +-- local client = UNIT:FindByName("Helo-1-1") +-- if client and client:IsAlive() then +-- myctld:PreloadTroops(client,"Infantry") +-- end +function CTLD:PreloadTroops(Unit,Troopname) + self:T(self.lid .. " PreloadTroops") + local name = Troopname or "Unknown" + if Unit and Unit:IsAlive() then + local cargo = self:_FindTroopsCargoObject(name) + local group = Unit:GetGroup() + if cargo then + self:_LoadTroops(group,Unit,cargo,true) + else + self:E(self.lid.." Troops preload - Cargo Object "..name.." not found!") + end + end + return self +end + --- (Internal) Function to load troops into a heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit -- @param #CTLD_CARGO Cargotype -function CTLD:_LoadTroops(Group, Unit, Cargotype) +-- @param #boolean Inject +function CTLD:_LoadTroops(Group, Unit, Cargotype, Inject) self:T(self.lid .. " _LoadTroops") -- check if we have stock local instock = Cargotype:GetStock() @@ -1661,7 +1703,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) local cgotype = Cargotype:GetType() local cgonetmass = Cargotype:GetNetMass() local maxloadable = self:_GetMaxLoadableMass(Unit) - if type(instock) == "number" and tonumber(instock) <= 0 and tonumber(instock) ~= -1 then + if type(instock) == "number" and tonumber(instock) <= 0 and tonumber(instock) ~= -1 and not Inject then -- nothing left over self:_SendMessage(string.format("Sorry, all %s are gone!", cgoname), 10, false, Group) return self @@ -1669,21 +1711,22 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) -- landed or hovering over load zone? local grounded = not self:IsUnitInAir(Unit) local hoverload = self:CanHoverLoad(Unit) - --local dooropen = UTILS.IsLoadingDoorOpen(Unit:GetName()) and self.pilotmustopendoors -- check if we are in LOAD zone local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) if not inzone then inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) end - if not inzone then - self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group) - if not self.debug then return self end - elseif not grounded and not hoverload then - self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) - if not self.debug then return self end - elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then - self:_SendMessage("You need to open the door(s) to load troops!", 10, false, Group) - if not self.debug then return self end + if not Inject then + if not inzone then + self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group) + if not self.debug then return self end + elseif not grounded and not hoverload then + self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) + if not self.debug then return self end + elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to load troops!", 10, false, Group) + if not self.debug then return self end + end end -- load troops into heli local group = Group -- Wrapper.Group#GROUP diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index f5f3a7749..0b6814029 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -631,9 +631,6 @@ function UNIT:IsFuelSupply() return false end ---- Returns the unit's group if it exists and nil otherwise. --- @param Wrapper.Unit#UNIT self --- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist. --- Returns the unit's group if it exists and nil otherwise. -- @param Wrapper.Unit#UNIT self -- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist. From 68092e5966492d64abb886e9a86e76dc54216159 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 23 Dec 2022 13:42:48 +0100 Subject: [PATCH 155/603] #CTLD --- Moose Development/Moose/Ops/CTLD.lua | 42 +++++++++++++++++++--------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 4b90a90f5..677c810e8 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1088,7 +1088,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.23" +CTLD.version="1.0.24" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1171,6 +1171,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- radio beacons self.RadioSound = "beacon.ogg" + self.RadioPath = "l10n/DEFAULT/" -- zones stuff self.pickupZones = {} @@ -3757,28 +3758,43 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip, IsDropped) local ZoneCoord = Zone local ZoneVec3 = ZoneCoord:GetVec3(1) local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz - --local Frequency = Mhz*1000000 - local Sound = "l10n/DEFAULT/"..Sound - --local name = string.format("%s-%f-%s",Zone:GetName(),Mhz,tostring(Modulation)) - --trigger.action.stopRadioTransmission(name) + local Sound = self.RadioPath..Sound trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, tonumber(Frequency), 1000) -- Beacon in MP only runs for 30secs straight - --local status = string.format("***** Beacon added Freq %s Mod %s", Frequency, UTILS.GetModulationName(Modulation)) - --MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug) elseif Zone then local ZoneCoord = Zone:GetCoordinate(1) local ZoneVec3 = ZoneCoord:GetVec3() local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz - --local Frequency = Mhz*1000000 - local Sound = "l10n/DEFAULT/"..Sound - --local name = string.format("%s-%f-%s",Zone:GetName(),Mhz,tostring(Modulation)) - --trigger.action.stopRadioTransmission(name) + local Sound = self.RadioPath..Sound trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, tonumber(Frequency), 1000) -- Beacon in MP only runs for 30secs straight - --local status = string.format("***** Beacon added Freq %s Mod %s", Frequency, UTILS.GetModulationName(Modulation)) - --MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug) end return self end +--- Set folder path where the CTLD sound files are located **within you mission (miz) file**. +-- The default path is "l10n/DEFAULT/" but sound files simply copied there will be removed by DCS the next time you save the mission. +-- However, if you create a new folder inside the miz file, which contains the sounds, it will not be deleted and can be used. +-- @param #CTLD self +-- @param #string FolderPath The path to the sound files, e.g. "CTLD_Soundfiles/". +-- @return #CTLD self +function CTLD:SetSoundfilesFolder( FolderPath ) + self:T(self.lid .. " SetSoundfilesFolder") + -- Check that it ends with / + if FolderPath then + local lastchar = string.sub( FolderPath, -1 ) + if lastchar ~= "/" then + FolderPath = FolderPath .. "/" + end + end + + -- Folderpath. + self.RadioPath = FolderPath + + -- Info message. + self:I( self.lid .. string.format( "Setting sound files folder to: %s", self.RadioPath ) ) + + return self +end + --- (Internal) Function to refresh radio beacons -- @param #CTLD self function CTLD:_RefreshRadioBeacons() From 849c6ae88b0b7d04445a1eaa672657978c5c15b6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 23 Dec 2022 13:47:35 +0100 Subject: [PATCH 156/603] #AUFTRAG --- Moose Development/Moose/Ops/Auftrag.lua | 6281 +++++++++++++++++++++++ 1 file changed, 6281 insertions(+) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 52904d0ec..4ba024548 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -16,7 +16,6288 @@ -- -- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Auftrag). -- +-- ===--- **Ops** - Auftrag (mission) for Ops. +-- +-- ## Main Features: +-- +-- * Simplifies defining and executing DCS tasks +-- * Additional useful events +-- * Set mission start/stop times +-- * Set mission priority and urgency (can cancel running missions) +-- * Specific mission options for ROE, ROT, formation, etc. +-- * Compatible with OPS classes like FLIGHTGROUP, NAVYGROUP, ARMYGROUP, AIRWING, etc. +-- * FSM events when a mission is done, successful or failed +-- -- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Auftrag). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Ops.Auftrag +-- @image OPS_Auftrag.png + + +--- AUFTRAG class. +-- @type AUFTRAG +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity level. +-- @field #string lid Class id string for output to DCS log file. +-- @field #number auftragsnummer Auftragsnummer. +-- @field #string type Mission type. +-- @field #table categories Mission categories. +-- @field #string status Mission status. +-- @field #table legions Assigned legions. +-- @field #table statusLegion Mission status of all assigned LEGIONs. +-- @field #string statusCommander Mission status of the COMMANDER. +-- @field #string statusChief Mission status of the CHIEF. +-- @field #table groupdata Group specific data. +-- @field #string name Mission name. +-- @field #number prio Mission priority. +-- @field #boolean urgent Mission is urgent. Running missions with lower prio might be cancelled. +-- @field #number importance Importance. +-- @field #number Tstart Mission start time in abs. seconds. +-- @field #number Tstop Mission stop time in abs. seconds. +-- @field #number duration Mission duration in seconds. +-- @field #number durationExe Mission execution time in seconds. +-- @field #number Texecuting Time stamp (abs) when mission is executing. Is `#nil` on start. +-- @field #number Tpush Mission push/execute time in abs. seconds. +-- @field #number Tstarted Time stamp (abs) when mission is started. +-- @field Wrapper.Marker#MARKER marker F10 map marker. +-- @field #boolean markerOn If true, display marker on F10 map with the AUFTRAG status. +-- @field #number markerCoaliton Coalition to which the marker is dispayed. +-- @field #table DCStask DCS task structure. +-- @field #number Ncasualties Number of own casualties during mission. +-- @field #number Nkills Number of (enemy) units killed by assets of this mission. +-- @field #number Nelements Number of elements (units) assigned to mission. +-- @field #number dTevaluate Time interval in seconds before the mission result is evaluated after mission is over. +-- @field #number Tover Mission abs. time stamp, when mission was over. +-- @field #boolean updateDCSTask If `true`, DCS task is updated at every status update of the assigned groups. +-- @field #table conditionStart Condition(s) that have to be true, before the mission will be started. +-- @field #table conditionSuccess If all conditions are true, the mission is cancelled. +-- @field #table conditionFailure If all conditions are true, the mission is cancelled. +-- @field #table conditionPush If all conditions are true, the mission is executed. Before, the group(s) wait at the mission execution waypoint. +-- +-- @field #number orbitSpeed Orbit speed in m/s. +-- @field #number orbitAltitude Orbit altitude in meters. +-- @field #number orbitHeading Orbit heading in degrees. +-- @field #number orbitLeg Length of orbit leg in meters. +-- @field DCS#Vec2 orbitOffsetVec2 2D offset vector. +-- @field DCS#Vec2 orbitVec2 2D orbit vector. +-- @field #number orbitDeltaR Distance threshold in meters for moving orbit targets. +-- +-- @field Ops.Target#TARGET engageTarget Target data to engage. +-- @field #number targetHeading Heading of target in degrees. +-- +-- @field Ops.Operation#OPERATION operation Operation this mission is part of. +-- +-- @field #boolean teleport Groups are teleported to the mission ingress waypoint. +-- +-- @field Core.Zone#ZONE_RADIUS engageZone *Circular* engagement zone. +-- @field #table engageTargetTypes Table of target types that are engaged in the engagement zone. +-- @field #number engageAltitude Engagement altitude in meters. +-- @field #number engageDirection Engagement direction in degrees. +-- @field #number engageQuantity Number of times a target is engaged. +-- @field #number engageWeaponType Weapon type used. +-- @field #number engageWeaponExpend How many weapons are used. +-- @field #boolean engageAsGroup Group attack. +-- @field #number engageMaxDistance Max engage distance. +-- @field #number refuelSystem Refuel type (boom or probe) for TANKER missions. +-- +-- @field Wrapper.Group#GROUP escortGroup The group to be escorted. +-- @field DCS#Vec3 escortVec3 The 3D offset vector from the escorted group to the escort group. +-- +-- @field #number facDesignation FAC designation type. +-- @field #boolean facDatalink FAC datalink enabled. +-- @field #number facFreq FAC radio frequency in MHz. +-- @field #number facModu FAC radio modulation 0=AM 1=FM. +-- +-- @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. +-- @field #number NcarriersMax Max number of required carrier assets. +-- @field Core.Zone#ZONE transportDeployZone Deploy zone of an OPSTRANSPORT. +-- @field Core.Zone#ZONE transportDisembarkZone Disembark zone of an OPSTRANSPORT. +-- +-- @field #number artyRadius Radius in meters. +-- @field #number artyShots Number of shots fired. +-- @field #number artyAltitude Altitude in meters. Can be used for a Barrage. +-- @field #number artyHeading Heading in degrees (for Barrage). +-- @field #number artyAngle Shooting angle in degrees (for Barrage). +-- +-- @field #string alert5MissionType Alert 5 mission type. This is the mission type, the alerted assets will be able to carry out. +-- +-- @field #table attributes Generalized attribute(s) of assets. +-- @field #table properties DCS attribute(s) of assets. +-- +-- @field Ops.Chief#CHIEF chief The CHIEF managing this mission. +-- @field Ops.Commander#COMMANDER commander The COMMANDER managing this mission. +-- @field #table assets Warehouse assets assigned for this mission. +-- @field #number NassetsMin Min. number of required warehouse assets. +-- @field #number NassetsMax Max. number of required warehouse assets. +-- @field #number NescortMin Min. number of required escort assets for each group the mission is assigned to. +-- @field #number NescortMax Max. number of required escort assets for each group the mission is assigned to. +-- @field #string escortMissionType Escort mission type. +-- @field #table escortTargetTypes Target types that will be engaged. +-- @field #number escortEngageRange Engage range in nautical miles (NM). +-- @field #number Nassets Number of requested warehouse assets. +-- @field #table NassetsLegMin Number of required warehouse assets for each assigned legion. +-- @field #table NassetsLegMax Number of required warehouse assets for each assigned legion. +-- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the mission was cancelled before the request is processed. +-- @field #table payloads User specified airwing payloads for this mission. Only these will be considered for the job! +-- @field Ops.AirWing#AIRWING.PatrolData patroldata Patrol data. +-- +-- @field #table specialLegions User specified legions assigned for this mission. Only these will be considered for the job! +-- @field #table specialCohorts User specified cohorts assigned for this mission. Only these will be considered for the job! +-- @field #table transportLegions Legions explicitly requested for providing transport carrier assets. +-- @field #table transportCohorts Cohorts explicitly requested for providing transport carrier assets. +-- @field #table escortLegions Legions explicitly requested for providing escorting assets. +-- @field #table escortCohorts Cohorts explicitly requested for providing escorting assets. +-- +-- @field #string missionTask Mission task. See `ENUMS.MissionTask`. +-- @field #number missionAltitude Mission altitude in meters. +-- @field #number missionSpeed Mission speed in km/h. +-- @field #number missionFraction Mission coordiante fraction. Default is 0.5. +-- @field #number missionRange Mission range in meters. Used by LEGION classes (AIRWING, BRIGADE, ...). +-- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate. +-- @field Core.Point#COORDINATE missionEgressCoord Mission egress waypoint coordinate. +-- @field #number missionWaypointRadius Random radius in meters. +-- +-- @field #table enrouteTasks Mission enroute tasks. +-- +-- @field #number repeated Number of times mission was repeated. +-- @field #number repeatedSuccess Number of times mission was repeated after a success. +-- @field #number repeatedFailure Number of times mission was repeated after a failure. +-- @field #number Nrepeat Number of times the mission is repeated. +-- @field #number NrepeatFailure Number of times mission is repeated if failed. +-- @field #number NrepeatSuccess Number of times mission is repeated if successful. +-- +-- @field Ops.OpsGroup#OPSGROUP.Radio radio Radio freq and modulation. +-- @field Ops.OpsGroup#OPSGROUP.Beacon tacan TACAN setting. +-- @field Ops.OpsGroup#OPSGROUP.Beacon icls ICLS setting. +-- +-- @field #number optionROE ROE. +-- @field #number optionROT ROT. +-- @field #number optionAlarm Alarm state. +-- @field #number optionFormation Formation. +-- @field #boolean optionEPLRS EPLRS datalink. +-- @field #number optionCM Counter measures. +-- @field #number optionRTBammo RTB on out-of-ammo. +-- @field #number optionRTBfuel RTB on out-of-fuel. +-- @field #number optionECM ECM. +-- @field #boolean optionEmission Emission is on or off. +-- @field #boolean optionInvisible Invisible is on/off. +-- @field #boolean optionImmortal Immortal is on/off. +-- +-- @extends Core.Fsm#FSM + +--- *A warrior's mission is to foster the success of others.* -- Morihei Ueshiba +-- +-- === +-- +-- # The AUFTRAG Concept +-- +-- The AUFTRAG class significantly simplifies the workflow of using DCS tasks. +-- +-- You can think of an AUFTRAG as document, which contains the mission briefing, i.e. information about the target location, mission altitude, speed and various other parameters. +-- This document can be handed over directly to a pilot (or multiple pilots) via the @{Ops.FlightGroup#FLIGHTGROUP} class. The pilots will then execute the mission. +-- +-- The AUFTRAG document can also be given to an AIRWING. The airwing will then determine the best assets (pilots and payloads) available for the job. +-- +-- Similarly, an AUFTRAG can be given to ground or navel groups via the @{Ops.ArmyGroup#ARMYGROUP} or @{Ops.NavyGroup#NAVYGROUP} classes, respectively. These classes have also +-- AIRWING analouges, which are called BRIGADE and FLEET. Brigades and fleets will likewise select the best assets they have available and pass on the AUFTRAG to them. +-- +-- +-- One more up the food chain, an AUFTRAG can be passed to a COMMANDER. The commander will recruit the best assets of AIRWINGs, BRIGADEs and/or FLEETs and pass the job over to it. +-- +-- +-- # Airborne Missions +-- +-- Several mission types are supported by this class. +-- +-- ## Anti-Ship +-- +-- An anti-ship mission can be created with the @{#AUFTRAG.NewANTISHIP}() function. +-- +-- ## AWACS +-- +-- An AWACS mission can be created with the @{#AUFTRAG.NewAWACS}() function. +-- +-- ## BAI +-- +-- A BAI mission can be created with the @{#AUFTRAG.NewBAI}() function. +-- +-- ## Bombing +-- +-- A bombing mission can be created with the @{#AUFTRAG.NewBOMBING}() function. +-- +-- ## Bombing Runway +-- +-- A bombing runway mission can be created with the @{#AUFTRAG.NewBOMBRUNWAY}() function. +-- +-- ## Bombing Carpet +-- +-- A carpet bombing mission can be created with the @{#AUFTRAG.NewBOMBCARPET}() function. +-- +-- ## CAP +-- +-- A CAP mission can be created with the @{#AUFTRAG.NewCAP}() function. +-- +-- ## CAS +-- +-- A CAS mission can be created with the @{#AUFTRAG.NewCAS}() function. +-- +-- ## Escort +-- +-- An escort mission can be created with the @{#AUFTRAG.NewESCORT}() function. +-- +-- ## FACA +-- +-- An FACA mission can be created with the @{#AUFTRAG.NewFACA}() function. +-- +-- ## Ferry +-- +-- Not implemented yet. +-- +-- ## Intercept +-- +-- An intercept mission can be created with the @{#AUFTRAG.NewINTERCEPT}() function. +-- +-- ## Orbit +-- +-- An orbit mission can be created with the @{#AUFTRAG.NewORBIT}() function. +-- +-- ## GCICAP +-- +-- An patrol mission can be created with the @{#AUFTRAG.NewGCICAP}() function. +-- +-- ## RECON +-- +-- An reconnaissance mission can be created with the @{#AUFTRAG.NewRECON}() function. +-- +-- ## RESCUE HELO +-- +-- An rescue helo mission can be created with the @{#AUFTRAG.NewRESCUEHELO}() function. +-- +-- ## SEAD +-- +-- An SEAD mission can be created with the @{#AUFTRAG.NewSEAD}() function. +-- +-- ## STRIKE +-- +-- An strike mission can be created with the @{#AUFTRAG.NewSTRIKE}() function. +-- +-- ## Tanker +-- +-- A refueling tanker mission can be created with the @{#AUFTRAG.NewTANKER}() function. +-- +-- ## TROOPTRANSPORT +-- +-- A troop transport mission can be created with the @{#AUFTRAG.NewTROOPTRANSPORT}() function. +-- +-- ## CARGOTRANSPORT +-- +-- A cargo transport mission can be created with the @{#AUFTRAG.NewCARGOTRANSPORT}() function. +-- +-- ## HOVER +-- +-- A mission for a helicoptre or VSTOL plane to Hover at a point for a certain amount of time can be created with the @{#AUFTRAG.NewHOVER}() function. +-- +-- # Ground Missions +-- +-- ## ARTY +-- +-- An arty mission can be created with the @{#AUFTRAG.NewARTY}() function. +-- +-- ## GROUNDATTACK +-- +-- A ground attack mission can be created with the @{#AUFTRAG.NewGROUNDATTACK}() function. +-- +-- # Assigning Missions +-- +-- An AUFTRAG can be assigned to groups (FLIGHTGROUP, ARMYGROUP, NAVYGROUP), legions (AIRWING, BRIGADE, FLEET) or to a COMMANDER. +-- +-- ## Group Level +-- +-- ### Flight Group +-- +-- Assigning an AUFTRAG to a flight group is done via the @{Ops.FlightGroup#FLIGHTGROUP.AddMission} function. See FLIGHTGROUP docs for details. +-- +-- ### Army Group +-- +-- Assigning an AUFTRAG to an army group is done via the @{Ops.ArmyGroup#ARMYGROUP.AddMission} function. See ARMYGROUP docs for details. +-- +-- ### Navy Group +-- +-- Assigning an AUFTRAG to a navy group is done via the @{Ops.NavyGroup#NAVYGROUP.AddMission} function. See NAVYGROUP docs for details. +-- +-- ## Legion Level +-- +-- Adding an AUFTRAG to an airwing is done via the @{Ops.AirWing#AIRWING.AddMission} function. See AIRWING docs for further details. +-- Similarly, an AUFTRAG can be added to a brigade via the @{Ops.Brigade#BRIGADE.AddMission} function. +-- +-- ## Commander Level +-- +-- Assigning an AUFTRAG to a commander is done via the @{Ops.Commander#COMMANDER.AddMission} function. +-- The commander will select the best assets available from all the legions under his command. See COMMANDER docs for details. +-- +-- ## Chief Level +-- +-- Assigning an AUFTRAG to a commander is done via the @{Ops.Chief#CHIEF.AddMission} function. The chief will simply pass on the mission to his/her commander. +-- +-- # Transportation +-- +-- TODO +-- +-- +-- # Events +-- +-- The AUFTRAG class creates many useful (FSM) events, which can be used in the mission designers script. +-- +-- TODO +-- +-- +-- # Examples +-- +-- TODO +-- +-- +-- @field #AUFTRAG +AUFTRAG = { + ClassName = "AUFTRAG", + verbose = 0, + lid = nil, + auftragsnummer = nil, + groupdata = {}, + legions = {}, + statusLegion = {}, + requestID = {}, + assets = {}, + NassetsLegMin = {}, + NassetsLegMax = {}, + missionFraction = 0.5, + enrouteTasks = {}, + marker = nil, + markerOn = nil, + markerCoalition = nil, + conditionStart = {}, + conditionSuccess = {}, + conditionFailure = {}, + conditionPush = {}, +} + +--- Global mission counter. +_AUFTRAGSNR=0 + + +--- Mission types. +-- @type AUFTRAG.Type +-- @field #string ANTISHIP Anti-ship mission. +-- @field #string AWACS AWACS mission. +-- @field #string BAI Battlefield Air Interdiction. +-- @field #string BOMBING Bombing mission. +-- @field #string BOMBRUNWAY Bomb runway of an airbase. +-- @field #string BOMBCARPET Carpet bombing. +-- @field #string CAP Combat Air Patrol. +-- @field #string CAS Close Air Support. +-- @field #string ESCORT Escort mission. +-- @field #string FACA Forward AirController airborne mission. +-- @field #string FERRY Ferry mission. +-- @field #string INTERCEPT Intercept mission. +-- @field #string ORBIT Orbit mission. +-- @field #string GCICAP Similar to CAP but no auto engage targets. +-- @field #string RECON Recon mission. +-- @field #string RECOVERYTANKER Recovery tanker mission. Not implemented yet. +-- @field #string RESCUEHELO Rescue helo. +-- @field #string SEAD Suppression/destruction of enemy air defences. +-- @field #string STRIKE Strike mission. +-- @field #string TANKER Tanker mission. +-- @field #string TROOPTRANSPORT Troop transport mission. +-- @field #string ARTY Fire at point. +-- @field #string PATROLZONE Patrol a zone. +-- @field #string OPSTRANSPORT Ops transport. +-- @field #string AMMOSUPPLY Ammo supply. +-- @field #string FUELSUPPLY Fuel supply. +-- @field #string ALERT5 Alert 5. +-- @field #string ONGUARD On guard. +-- @field #string ARMOREDGUARD On guard - with armored groups. +-- @field #string BARRAGE Barrage. +-- @field #string ARMORATTACK Armor attack. +-- @field #string CASENHANCED Enhanced CAS. +-- @field #string HOVER Hover. +-- @field #string GROUNDATTACK Ground attack. +-- @field #string CARGOTRANSPORT Cargo transport. +-- @field #string RELOCATECOHORT Relocate a cohort from one legion to another. +-- @field #string AIRDEFENSE Air defense. +-- @field #string EWR Early Warning Radar. +-- @field #string RECOVERYTANKER Recovery tanker. +-- @filed #string REARMING Rearming mission. +-- @field #string NOTHING Nothing. +AUFTRAG.Type={ + ANTISHIP="Anti Ship", + AWACS="AWACS", + BAI="BAI", + BOMBING="Bombing", + BOMBRUNWAY="Bomb Runway", + BOMBCARPET="Carpet Bombing", + CAP="CAP", + CAS="CAS", + ESCORT="Escort", + FACA="FAC-A", + FERRY="Ferry Flight", + INTERCEPT="Intercept", + ORBIT="Orbit", + GCICAP="Ground Controlled CAP", + RECON="Recon", + RECOVERYTANKER="Recovery Tanker", + RESCUEHELO="Rescue Helo", + SEAD="SEAD", + STRIKE="Strike", + TANKER="Tanker", + TROOPTRANSPORT="Troop Transport", + ARTY="Fire At Point", + PATROLZONE="Patrol Zone", + OPSTRANSPORT="Ops Transport", + AMMOSUPPLY="Ammo Supply", + FUELSUPPLY="Fuel Supply", + ALERT5="Alert5", + ONGUARD="On Guard", + ARMOREDGUARD="Armored Guard", + BARRAGE="Barrage", + ARMORATTACK="Armor Attack", + CASENHANCED="CAS Enhanced", + HOVER="Hover", + GROUNDATTACK="Ground Attack", + CARGOTRANSPORT="Cargo Transport", + RELOCATECOHORT="Relocate Cohort", + AIRDEFENSE="Air Defence", + EWR="Early Warning Radar", + RECOVERYTANKER="Recovery Tanker", + REARMING="Rearming", + NOTHING="Nothing", +} + +--- Special task description. +-- @type AUFTRAG.SpecialTask +-- @field #string FORMATION AI formation task. +-- @field #string PATROLZONE Patrol zone task. +-- @field #string RECON Recon task. +-- @field #string AMMOSUPPLY Ammo Supply. +-- @field #string FUELSUPPLY Fuel Supply. +-- @field #string ALERT5 Alert 5 task. +-- @field #string ONGUARD On guard. +-- @field #string ARMOREDGUARD On guard with armor. +-- @field #string BARRAGE Barrage. +-- @field #string HOVER Hover. +-- @field #string GROUNDATTACK Ground attack. +-- @field #string FERRY Ferry mission. +-- @field #string RELOCATECOHORT Relocate cohort. +-- @field #string AIRDEFENSE Air defense. +-- @field #string EWR Early Warning Radar. +-- @field #string RECOVERYTANKER Recovery tanker. +-- @field #string REARMING Rearming. +-- @field #string NOTHING Nothing. +AUFTRAG.SpecialTask={ + FORMATION="Formation", + PATROLZONE="PatrolZone", + RECON="ReconMission", + AMMOSUPPLY="Ammo Supply", + FUELSUPPLY="Fuel Supply", + ALERT5="Alert5", + ONGUARD="On Guard", + ARMOREDGUARD="ArmoredGuard", + BARRAGE="Barrage", + ARMORATTACK="AmorAttack", + HOVER="Hover", + GROUNDATTACK="Ground Attack", + FERRY="Ferry", + RELOCATECOHORT="Relocate Cohort", + AIRDEFENSE="Air Defense", + EWR="Early Warning Radar", + RECOVERYTANKER="Recovery Tanker", + REARMING="Rearming", + NOTHING="Nothing", +} + +--- Mission status. +-- @type AUFTRAG.Status +-- @field #string PLANNED Mission is at the early planning stage and has not been added to any queue. +-- @field #string QUEUED Mission is queued at a LEGION. +-- @field #string REQUESTED Mission assets were requested from the warehouse. +-- @field #string SCHEDULED Mission is scheduled in an OPSGROUP queue waiting to be started. +-- @field #string STARTED Mission has started but is not executed yet. +-- @field #string EXECUTING Mission is being executed. +-- @field #string DONE Mission is over. +-- @field #string CANCELLED Mission was cancelled. +-- @field #string SUCCESS Mission was a success. +-- @field #string FAILED Mission failed. +AUFTRAG.Status={ + PLANNED="planned", + QUEUED="queued", + REQUESTED="requested", + SCHEDULED="scheduled", + STARTED="started", + EXECUTING="executing", + DONE="done", + CANCELLED="cancelled", + SUCCESS="success", + FAILED="failed", +} + +--- Mission status of an assigned group. +-- @type AUFTRAG.GroupStatus +-- @field #string SCHEDULED Mission is scheduled in a FLIGHGROUP queue waiting to be started. +-- @field #string STARTED Ops group started this mission but it is not executed yet. +-- @field #string EXECUTING Ops group is executing this mission. +-- @field #string PAUSED Ops group has paused this mission, e.g. for refuelling. +-- @field #string DONE Mission task of the Ops group is done. +-- @field #string CANCELLED Mission was cancelled. +AUFTRAG.GroupStatus={ + SCHEDULED="scheduled", + STARTED="started", + EXECUTING="executing", + PAUSED="paused", + DONE="done", + CANCELLED="cancelled", +} + +--- Target type. +-- @type AUFTRAG.TargetType +-- @field #string GROUP Target is a GROUP object. +-- @field #string UNIT Target is a UNIT object. +-- @field #string STATIC Target is a STATIC object. +-- @field #string COORDINATE Target is a COORDINATE. +-- @field #string AIRBASE Target is an AIRBASE. +-- @field #string SETGROUP Target is a SET of GROUPs. +-- @field #string SETUNIT Target is a SET of UNITs. +AUFTRAG.TargetType={ + GROUP="Group", + UNIT="Unit", + STATIC="Static", + COORDINATE="Coordinate", + AIRBASE="Airbase", + SETGROUP="SetGroup", + SETUNIT="SetUnit", +} + +--- Mission category. +-- @type AUFTRAG.Category +-- @field #string AIRCRAFT Airplanes and helicopters. +-- @field #string AIRPLANE Airplanes. +-- @field #string HELICOPTER Helicopter. +-- @field #string GROUND Ground troops. +-- @field #string NAVAL Naval grous. +AUFTRAG.Category={ + ALL="All", + AIRCRAFT="Aircraft", + AIRPLANE="Airplane", + HELICOPTER="Helicopter", + GROUND="Ground", + NAVAL="Naval", +} + +--- Target data. +-- @type AUFTRAG.TargetData +-- @field Wrapper.Positionable#POSITIONABLE Target Target Object. +-- @field #string Type Target type: "Group", "Unit", "Static", "Coordinate", "Airbase", "SetGroup", "SetUnit". +-- @field #string Name Target name. +-- @field #number Ninital Number of initial targets. +-- @field #number Lifepoints Total life points. +-- @field #number Lifepoints0 Inital life points. + +--- Mission capability. +-- @type AUFTRAG.Capability +-- @field #string MissionType Type of mission. +-- @field #number Performance Number describing the performance level. The higher the better. + +--- Mission success. +-- @type AUFTRAG.Success +-- @field #string SURVIVED Group did survive. +-- @field #string ENGAGED Target was engaged. +-- @field #string DAMAGED Target was damaged. +-- @field #string DESTROYED Target was destroyed. + +--- Generic mission condition. +-- @type AUFTRAG.Condition +-- @field #function func Callback function to check for a condition. Should return a #boolean. +-- @field #table arg Optional arguments passed to the condition callback function. + +--- Group specific data. Each ops group subscribed to this mission has different data for this. +-- @type AUFTRAG.GroupData +-- @field Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @field Core.Point#COORDINATE waypointcoordinate Ingress waypoint coordinate. +-- @field #number waypointindex Mission (ingress) Waypoint UID. +-- @field #number waypointEgressUID Egress Waypoint UID. +-- @field Core.Point#COORDINATE wpegresscoordinate Egress waypoint coordinate. +-- +-- @field Ops.OpsGroup#OPSGROUP.Task waypointtask Waypoint task. +-- @field #string status Group mission status. +-- @field Functional.Warehouse#WAREHOUSE.Assetitem asset The warehouse asset. + + +--- AUFTRAG class version. +-- @field #string version +AUFTRAG.version="0.9.7" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: Replace engageRange by missionRange. Here and in other classes. CTRL+H is your friend! +-- TODO: Mission success options damaged, destroyed. +-- TODO: F10 marker to create new missions. +-- DONE: Add orbit mission for moving anker points. +-- DONE: Add recovery tanker mission for boat ops. +-- DONE: Added auftrag category. +-- DONE: Missions can be assigned to multiple legions. +-- DONE: Option to assign a specific payload for the mission (requires an AIRWING). +-- NOPE: Clone mission. How? Deepcopy? ==> Create a new auftrag. +-- DONE: Recon mission. What input? Set of coordinates? +-- DONE: Option to assign mission to specific squadrons (requires an AIRWING). +-- DONE: Add mission start conditions. +-- DONE: Add rescue helo mission for boat ops. +-- DONE: Mission ROE and ROT. +-- DONE: Mission frequency and TACAN. +-- DONE: Mission formation, etc. +-- DONE: FSM events. +-- DONE: F10 marker functions that are updated on Status event. +-- DONE: Evaluate mission result ==> SUCCESS/FAILURE +-- DONE: NewAUTO() NewA2G NewA2A +-- DONE: Transport mission. +-- DONE: Set mission coalition, e.g. for F10 markers. Could be derived from target if target has a coalition. + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new generic AUFTRAG object. +-- @param #AUFTRAG self +-- @param #string Type Mission type. +-- @return #AUFTRAG self +function AUFTRAG:New(Type) + + -- Inherit everything from FSM class. + local self=BASE:Inherit(self, FSM:New()) -- #AUFTRAG + + -- Increase global counter. + _AUFTRAGSNR=_AUFTRAGSNR+1 + + -- Mission type. + self.type=Type + + -- Auftragsnummer. + self.auftragsnummer=_AUFTRAGSNR + + -- Log ID. + self:_SetLogID() + + -- State is planned. + self.status=AUFTRAG.Status.PLANNED + + -- Defaults . + self:SetName() + self:SetPriority() + self:SetTime() + self:SetRequiredAssets() + self.engageAsGroup=true + self.dTevaluate=5 + + -- Init counters and stuff. + self.repeated=0 + self.repeatedSuccess=0 + self.repeatedFailure=0 + self.Nrepeat=0 + self.NrepeatFailure=0 + self.NrepeatSuccess=0 + self.Ncasualties=0 + self.Nkills=0 + self.Nelements=0 + self.Ngroups=0 + self.Nassigned=nil + self.Ndead=0 + + -- FMS start state is PLANNED. + self:SetStartState(self.status) + + -- PLANNED --> (QUEUED) --> (REQUESTED) --> SCHEDULED --> STARTED --> EXECUTING --> DONE + self:AddTransition("*", "Planned", AUFTRAG.Status.PLANNED) -- Mission is in planning stage. Could be in the queue of a COMMANDER or CHIEF. + self:AddTransition(AUFTRAG.Status.PLANNED, "Queued", AUFTRAG.Status.QUEUED) -- Mission is in queue of a LEGION. + self:AddTransition(AUFTRAG.Status.QUEUED, "Requested", AUFTRAG.Status.REQUESTED) -- Mission assets have been requested from the warehouse. + self:AddTransition(AUFTRAG.Status.REQUESTED, "Scheduled", AUFTRAG.Status.SCHEDULED) -- Mission added to the first ops group queue. + + self:AddTransition(AUFTRAG.Status.PLANNED, "Scheduled", AUFTRAG.Status.SCHEDULED) -- From planned directly to scheduled. + + self:AddTransition(AUFTRAG.Status.SCHEDULED, "Started", AUFTRAG.Status.STARTED) -- First asset has started the mission. + self:AddTransition(AUFTRAG.Status.STARTED, "Executing", AUFTRAG.Status.EXECUTING) -- First asset is executing the mission. + + self:AddTransition("*", "Done", AUFTRAG.Status.DONE) -- All assets have reported that mission is done. + + self:AddTransition("*", "Cancel", AUFTRAG.Status.CANCELLED) -- Command to cancel the mission. + + self:AddTransition("*", "Success", AUFTRAG.Status.SUCCESS) + self:AddTransition("*", "Failed", AUFTRAG.Status.FAILED) + + self:AddTransition("*", "Status", "*") + self:AddTransition("*", "Stop", "*") + + self:AddTransition("*", "Repeat", AUFTRAG.Status.PLANNED) + + self:AddTransition("*", "ElementDestroyed", "*") + self:AddTransition("*", "GroupDead", "*") + self:AddTransition("*", "AssetDead", "*") + + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "Status". + -- @function [parent=#AUFTRAG] Status + -- @param #AUFTRAG self + + --- Triggers the FSM event "Status" after a delay. + -- @function [parent=#AUFTRAG] __Status + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Stop". + -- @function [parent=#AUFTRAG] Stop + -- @param #AUFTRAG self + + --- Triggers the FSM event "Stop" after a delay. + -- @function [parent=#AUFTRAG] __Stop + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Planned". + -- @function [parent=#AUFTRAG] Planned + -- @param #AUFTRAG self + + --- Triggers the FSM event "Planned" after a delay. + -- @function [parent=#AUFTRAG] __Planned + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Planned" event. + -- @function [parent=#AUFTRAG] OnAfterPlanned + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- Triggers the FSM event "Queued". + -- @function [parent=#AUFTRAG] Queued + -- @param #AUFTRAG self + + --- Triggers the FSM event "Queued" after a delay. + -- @function [parent=#AUFTRAG] __Queued + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Queued" event. + -- @function [parent=#AUFTRAG] OnAfterQueued + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- Triggers the FSM event "Requested". + -- @function [parent=#AUFTRAG] Requested + -- @param #AUFTRAG self + + --- Triggers the FSM event "Requested" after a delay. + -- @function [parent=#AUFTRAG] __Requested + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Requested" event. + -- @function [parent=#AUFTRAG] OnAfterRequested + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- Triggers the FSM event "Scheduled". + -- @function [parent=#AUFTRAG] Scheduled + -- @param #AUFTRAG self + + --- Triggers the FSM event "Scheduled" after a delay. + -- @function [parent=#AUFTRAG] __Scheduled + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Scheduled" event. + -- @function [parent=#AUFTRAG] OnAfterScheduled + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- Triggers the FSM event "Started". + -- @function [parent=#AUFTRAG] Started + -- @param #AUFTRAG self + + --- Triggers the FSM event "Started" after a delay. + -- @function [parent=#AUFTRAG] __Started + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Started" event. + -- @function [parent=#AUFTRAG] OnAfterStarted + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- Triggers the FSM event "Executing". + -- @function [parent=#AUFTRAG] Executing + -- @param #AUFTRAG self + + --- Triggers the FSM event "Executing" after a delay. + -- @function [parent=#AUFTRAG] __Executing + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Executing" event. + -- @function [parent=#AUFTRAG] OnAfterExecuting + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- Triggers the FSM event "Cancel". + -- @function [parent=#AUFTRAG] Cancel + -- @param #AUFTRAG self + + --- Triggers the FSM event "Cancel" after a delay. + -- @function [parent=#AUFTRAG] __Cancel + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Cancel" event. + -- @function [parent=#AUFTRAG] OnAfterCancel + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- Triggers the FSM event "Done". + -- @function [parent=#AUFTRAG] Done + -- @param #AUFTRAG self + + --- Triggers the FSM event "Done" after a delay. + -- @function [parent=#AUFTRAG] __Done + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Done" event. + -- @function [parent=#AUFTRAG] OnAfterDone + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- Triggers the FSM event "Success". + -- @function [parent=#AUFTRAG] Success + -- @param #AUFTRAG self + + --- Triggers the FSM event "Success" after a delay. + -- @function [parent=#AUFTRAG] __Success + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Success" event. + -- @function [parent=#AUFTRAG] OnAfterSuccess + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- Triggers the FSM event "Failed". + -- @function [parent=#AUFTRAG] Failed + -- @param #AUFTRAG self + + --- Triggers the FSM event "Failed" after a delay. + -- @function [parent=#AUFTRAG] __Failed + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Failed" event. + -- @function [parent=#AUFTRAG] OnAfterFailed + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- Triggers the FSM event "Repeat". + -- @function [parent=#AUFTRAG] Repeat + -- @param #AUFTRAG self + + --- Triggers the FSM event "Repeat" after a delay. + -- @function [parent=#AUFTRAG] __Repeat + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + + --- On after "Repeat" event. + -- @function [parent=#AUFTRAG] OnAfterRepeat + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + -- Init status update. + self:__Status(-1) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Create Missions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- **[AIR]** Create an ANTI-SHIP mission. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be passed as a @{Wrapper.Group#GROUP} or @{Wrapper.Unit#UNIT} object. +-- @param #number Altitude Engage altitude in feet. Default 2000 ft. +-- @return #AUFTRAG self +function AUFTRAG:NewANTISHIP(Target, Altitude) + + local mission=AUFTRAG:New(AUFTRAG.Type.ANTISHIP) + + mission:_TargetFromObject(Target) + + -- DCS task parameters: + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL + mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000) + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.ANTISHIPSTRIKE + mission.missionAltitude=mission.engageAltitude + mission.missionFraction=0.4 + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.EvadeFire + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR ROTARY]** Create an HOVER mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Where to hover. +-- @param #number Altitude Hover altitude in feet AGL. Default is 50 feet above ground. +-- @param #number Time Time in seconds to hold the hover. Default 300 seconds. +-- @param #number Speed Speed in knots to fly to the target coordinate. Default 150kn. +-- @param #number MissionAlt Altitide to fly towards the mission in feet AGL. Default 1000ft. +-- @return #AUFTRAG self +function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt) + + local mission=AUFTRAG:New(AUFTRAG.Type.HOVER) + + -- Altitude. + if Altitude then + mission.hoverAltitude=Coordinate:GetLandHeight()+UTILS.FeetToMeters(Altitude) + else + mission.hoverAltitude=Coordinate:GetLandHeight()+UTILS.FeetToMeters(50) + end + + mission:_TargetFromObject(Coordinate) + + mission.hoverSpeed = 0.1 -- the DCS Task itself will shortly be build with this so MPS + mission.hoverTime = Time or 300 + self:SetMissionSpeed(Speed or 150) + self:SetMissionAltitude(MissionAlt or 1000) + + -- Mission options: + mission.missionFraction=0.9 + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.HELICOPTER} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create an ORBIT mission, which can be either a circular orbit or a race-track pattern. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Where to orbit. +-- @param #number Altitude Orbit altitude in feet above sea level. Default is y component of `Coordinate`. +-- @param #number Speed Orbit speed in knots. Default 350 KIAS. +-- @param #number Heading Heading of race-track pattern in degrees. If not specified, a circular orbit is performed. +-- @param #number Leg Length of race-track in NM. If not specified, a circular orbit is performed. +-- @return #AUFTRAG self +function AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) + + local mission=AUFTRAG:New(AUFTRAG.Type.ORBIT) + + -- Target. + mission:_TargetFromObject(Coordinate) + + -- Set Altitude. + if Altitude then + mission.orbitAltitude=UTILS.FeetToMeters(Altitude) + else + mission.orbitAltitude=Coordinate.y + end + + -- Orbit speed in m/s. + mission.orbitSpeed = UTILS.KnotsToMps(UTILS.KnotsToAltKIAS(Speed or 350, UTILS.MetersToFeet(mission.orbitAltitude))) + + -- Mission speed in km/h. + mission.missionSpeed = UTILS.KnotsToKmph(Speed or 350) + + if Leg then + mission.orbitLeg=UTILS.NMToMeters(Leg) + + -- Relative heading + if Heading and Heading<0 then + mission.orbitHeadingRel=true + Heading=-Heading + end + + -- Heading if given. + mission.orbitHeading=Heading + end + + -- Mission options: + mission.missionAltitude=mission.orbitAltitude*0.9 + mission.missionFraction=0.9 + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create an ORBIT mission, where the aircraft will go in a circle around the specified coordinate. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Position where to orbit around. +-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. +-- @param #number Speed Orbit speed in knots. Default 350 KIAS. +-- @return #AUFTRAG self +function AUFTRAG:NewORBIT_CIRCLE(Coordinate, Altitude, Speed) + + local mission=AUFTRAG:NewORBIT(Coordinate, Altitude, Speed) + + return mission +end + +--- **[AIR]** Create an ORBIT mission, where the aircraft will fly a race-track pattern. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Where to orbit. +-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. +-- @param #number Speed Orbit speed in knots. Default 350 KIAS. +-- @param #number Heading Heading of race-track pattern in degrees. Default random in [0, 360) degrees. +-- @param #number Leg Length of race-track in NM. Default 10 NM. +-- @return #AUFTRAG self +function AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) + + Heading = Heading or math.random(360) + Leg = Leg or 10 + + local mission=AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) + + return mission +end + +--- **[AIR]** Create an ORBIT mission, where the aircraft will fly a circular or race-track pattern over a given group or unit. +-- @param #AUFTRAG self +-- @param Wrapper.Group#GROUP Group Group where to orbit around. Can also be a UNIT object. +-- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft. +-- @param #number Speed Orbit speed in knots. Default 350 KIAS. +-- @param #number Leg Length of race-track in NM. Default nil. +-- @param #number Heading Heading of race-track pattern in degrees. Default is heading of the group. +-- @param DCS#Vec2 OffsetVec2 Offset 2D-vector {x=0, y=0} in NM with respect to the group. Default directly overhead. Can also be given in polar coordinates `{r=5, phi=45}`. +-- @param #number Distance Threshold distance in NM before orbit pattern is updated. Default 5 NM. +-- @return #AUFTRAG self +function AUFTRAG:NewORBIT_GROUP(Group, Altitude, Speed, Leg, Heading, OffsetVec2, Distance) + + -- Set default altitude. + Altitude = Altitude or 6000 + + -- Create orbit mission. + local mission=AUFTRAG:NewORBIT(Group, Altitude, Speed, Heading, Leg) + + -- DCS tasks needs to be updated from time to time. + mission.updateDCSTask=true + + -- Convert offset vector to meters. + if OffsetVec2 then + if OffsetVec2.x then + OffsetVec2.x=UTILS.NMToMeters(OffsetVec2.x) + end + if OffsetVec2.y then + OffsetVec2.y=UTILS.NMToMeters(OffsetVec2.y) + end + if OffsetVec2.r then + OffsetVec2.r=UTILS.NMToMeters(OffsetVec2.r) + end + end + + -- Offset vector. + mission.orbitOffsetVec2=OffsetVec2 + + -- Pattern update distance. + mission.orbitDeltaR=UTILS.NMToMeters(Distance or 5) + + -- Update task with offset etc. + mission:GetDCSMissionTask() + + return mission +end + + +--- **[AIR]** Create a Ground Controlled CAP (GCICAP) mission. Flights with this task are considered for A2A INTERCEPT missions by the CHIEF class. They will perform a compat air patrol but not engage by +-- themselfs. They wait for the CHIEF to tell them whom to engage. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Where to orbit. +-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. +-- @param #number Speed Orbit speed in knots. Default 350 kts. +-- @param #number Heading Heading of race-track pattern in degrees. Default random in [0, 360) degrees. +-- @param #number Leg Length of race-track in NM. Default 10 NM. +-- @return #AUFTRAG self +function AUFTRAG:NewGCICAP(Coordinate, Altitude, Speed, Heading, Leg) + + -- Create ORBIT first. + local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) + + -- Mission type GCICAP. + mission.type=AUFTRAG.Type.GCICAP + + mission:_SetLogID() + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.INTERCEPT + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + return mission +end + +--- **[AIR]** Create a TANKER mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Where to orbit. +-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. +-- @param #number Speed Orbit speed in knots. Default 350 kts. +-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). +-- @param #number Leg Length of race-track in NM. Default 10 NM. +-- @param #number RefuelSystem Refueling system (0=boom, 1=probe). This info is *only* for AIRWINGs so they launch the right tanker type. +-- @return #AUFTRAG self +function AUFTRAG:NewTANKER(Coordinate, Altitude, Speed, Heading, Leg, RefuelSystem) + + -- Create ORBIT first. + local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) + + -- Mission type TANKER. + mission.type=AUFTRAG.Type.TANKER + + mission:_SetLogID() + + mission.refuelSystem=RefuelSystem + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.REFUELING + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create a AWACS mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Where to orbit. Altitude is also taken from the coordinate. +-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. +-- @param #number Speed Orbit speed in knots. Default 350 kts. +-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). +-- @param #number Leg Length of race-track in NM. Default 10 NM. +-- @return #AUFTRAG self +function AUFTRAG:NewAWACS(Coordinate, Altitude, Speed, Heading, Leg) + + -- Create ORBIT first. + local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) + + -- Mission type AWACS. + mission.type=AUFTRAG.Type.AWACS + + mission:_SetLogID() + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.AWACS + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + + + +--- **[AIR]** Create an INTERCEPT mission. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Target The target to intercept. Can also be passed as simple @{Wrapper.Group#GROUP} or @{Wrapper.Unit#UNIT} object. +-- @return #AUFTRAG self +function AUFTRAG:NewINTERCEPT(Target) + + local mission=AUFTRAG:New(AUFTRAG.Type.INTERCEPT) + + mission:_TargetFromObject(Target) + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.INTERCEPT + mission.missionFraction=0.1 + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.EvadeFire + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create a CAP mission. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE_RADIUS ZoneCAP Circular CAP zone. Detected targets in this zone will be engaged. +-- @param #number Altitude Altitude at which to orbit in feet. Default is 10,000 ft. +-- @param #number Speed Orbit speed in knots. Default 350 kts. +-- @param Core.Point#COORDINATE Coordinate Where to orbit. Default is the center of the CAP zone. +-- @param #number Heading Heading of race-track pattern in degrees. If not specified, a simple circular orbit is performed. +-- @param #number Leg Length of race-track in NM. If not specified, a simple circular orbit is performed. +-- @param #table TargetTypes Table of target types. Default {"Air"}. +-- @return #AUFTRAG self +function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, TargetTypes) + + -- Ensure given TargetTypes parameter is a table. + if TargetTypes then + if type(TargetTypes)~="table" then + TargetTypes={TargetTypes} + end + end + + -- Create ORBIT first. + local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAP:GetCoordinate(), Altitude or 10000, Speed, Heading, Leg) + + -- Mission type CAP. + mission.type=AUFTRAG.Type.CAP + mission:_SetLogID() + + -- DCS task parameters: + mission.engageZone=ZoneCAP + mission.engageTargetTypes=TargetTypes or {"Air"} + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.CAP + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.EvadeFire + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create a CAP mission on a group. +-- @param #AUFTRAG self +-- @param Wrapper.Group#GROUP Grp. +-- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft. +-- @param #number Speed Orbit speed in knots. Default 250 KIAS. +-- @param #number RelHeading Relative heading [0, 360) of race-track pattern in degrees wrt heading of the carrier. Default is heading of the carrier. +-- @param #number Leg Length of race-track in NM. Default 14 NM. +-- @param #number OffsetDist Relative distance of the first race-track point wrt to the carrier. Default 6 NM. +-- @param #number OffsetAngle Relative angle of the first race-track point wrt. to the carrier. Default 180 (behind the boat). +-- @param #number UpdateDistance Threshold distance in NM before orbit pattern is updated. Default 5 NM. +-- @param #table TargetTypes (Optional) Table of target types. Default `{"Helicopters", "Ground Units", "Light armed ships"}`. +-- @param #number EngageRange Max range in nautical miles that the escort group(s) will engage enemies. Default 32 NM (60 km). +-- @return #AUFTRAG self +function AUFTRAG:NewCAPGROUP(Grp, Altitude, Speed, RelHeading, Leg, OffsetDist, OffsetAngle, UpdateDistance, TargetTypes, EngageRange) + + -- Ensure given TargetTypes parameter is a table. + if TargetTypes then + if type(TargetTypes)~="table" then + TargetTypes={TargetTypes} + end + end + -- Six NM astern. + local OffsetVec2={r=OffsetDist or 6, phi=OffsetAngle or 180} + + -- Default leg. + Leg=Leg or 14 + + local Heading=nil + if RelHeading then + Heading=-math.abs(RelHeading) + end + + -- Create orbit mission. + local mission=AUFTRAG:NewORBIT_GROUP(Grp, Altitude, Speed, Leg, Heading, OffsetVec2, UpdateDistance) + -- Mission type CAP. + mission.type=AUFTRAG.Type.CAP + mission:_SetLogID() + + -- DCS task parameters: + local engage = EngageRange or 32 + local zoneCAPGroup = ZONE_GROUP:New("CAPGroup", Grp, UTILS.NMToMeters(engage)) + mission.engageZone=zoneCAPGroup + mission.engageTargetTypes=TargetTypes or {"Air"} + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.CAP + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.EvadeFire + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create a CAS mission. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE_RADIUS ZoneCAS Circular CAS zone. Detected targets in this zone will be engaged. +-- @param #number Altitude Altitude at which to orbit. Default is 10,000 ft. +-- @param #number Speed Orbit speed in knots. Default 350 KIAS. +-- @param Core.Point#COORDINATE Coordinate Where to orbit. Default is the center of the CAS zone. +-- @param #number Heading Heading of race-track pattern in degrees. If not specified, a simple circular orbit is performed. +-- @param #number Leg Length of race-track in NM. If not specified, a simple circular orbit is performed. +-- @param #table TargetTypes (Optional) Table of target types. Default `{"Helicopters", "Ground Units", "Light armed ships"}`. +-- @return #AUFTRAG self +function AUFTRAG:NewCAS(ZoneCAS, Altitude, Speed, Coordinate, Heading, Leg, TargetTypes) + + -- Ensure given TargetTypes parameter is a table. + if TargetTypes then + if type(TargetTypes)~="table" then + TargetTypes={TargetTypes} + end + end + + -- Create ORBIT first. + local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAS:GetCoordinate(), Altitude or 10000, Speed, Heading, Leg) + + -- Mission type CAS. + mission.type=AUFTRAG.Type.CAS + mission:_SetLogID() + + -- DCS Task options: + mission.engageZone=ZoneCAS + mission.engageTargetTypes=TargetTypes or {"Helicopters", "Ground Units", "Light armed ships"} + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.CAS + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.EvadeFire + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + 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 Core.Set#SET_ZONE NoEngageZoneSet Set of zones in which targets are *not* engaged. Default is nowhere. +-- @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"}`. +-- @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. +-- @param #string Designation Designation of target. See `AI.Task.Designation`. Default `AI.Task.Designation.AUTO`. +-- @param #boolean DataLink Enable data link. Default `true`. +-- @param #number Frequency Radio frequency in MHz the FAC uses for communication. Default is 133 MHz. +-- @param #number Modulation Radio modulation band. Default 0=AM. Use 1 for FM. See radio.modulation.AM or radio.modulaton.FM. +-- @return #AUFTRAG self +function AUFTRAG:NewFACA(Target, Designation, DataLink, Frequency, Modulation) + + local mission=AUFTRAG:New(AUFTRAG.Type.FACA) + + mission:_TargetFromObject(Target) + + -- TODO: check that target is really a group object! + + -- DCS Task options: + mission.facDesignation=Designation --or AI.Task.Designation.AUTO + mission.facDatalink=true + mission.facFreq=Frequency or 133 + mission.facModu=Modulation or radio.modulation.AM + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.AFAC + mission.missionAltitude=nil + mission.missionFraction=0.5 + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + + +--- **[AIR]** Create a BAI mission. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. +-- @param #number Altitude Engage altitude in feet. Default 5000 ft. +-- @return #AUFTRAG self +function AUFTRAG:NewBAI(Target, Altitude) + + local mission=AUFTRAG:New(AUFTRAG.Type.BAI) + + mission:_TargetFromObject(Target) + + -- DCS Task options: + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL + mission.engageAltitude=UTILS.FeetToMeters(Altitude or 5000) + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.GROUNDATTACK + mission.missionAltitude=mission.engageAltitude + mission.missionFraction=0.75 + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create a SEAD mission. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP or UNIT object. +-- @param #number Altitude Engage altitude in feet. Default 25000 ft. +-- @return #AUFTRAG self +function AUFTRAG:NewSEAD(Target, Altitude) + + local mission=AUFTRAG:New(AUFTRAG.Type.SEAD) + + mission:_TargetFromObject(Target) + + -- DCS Task options: + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL + mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000) + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.SEAD + mission.missionAltitude=mission.engageAltitude + mission.missionFraction=0.2 + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.EvadeFire + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create a STRIKE mission. Flight will attack the closest map object to the specified coordinate. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC or TARGET object. +-- @param #number Altitude Engage altitude in feet. Default 2000 ft. +-- @return #AUFTRAG self +function AUFTRAG:NewSTRIKE(Target, Altitude) + + local mission=AUFTRAG:New(AUFTRAG.Type.STRIKE) + + mission:_TargetFromObject(Target) + + -- DCS Task options: + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL + mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000) + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.GROUNDATTACK + mission.missionAltitude=mission.engageAltitude + mission.missionFraction=0.75 + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create a BOMBING mission. Flight will drop bombs a specified coordinate. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC or TARGET object. +-- @param #number Altitude Engage altitude in feet. Default 25000 ft. +-- @return #AUFTRAG self +function AUFTRAG:NewBOMBING(Target, Altitude) + + local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING) + + mission:_TargetFromObject(Target) + + -- DCS task options: + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL + mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000) + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.GROUNDATTACK + mission.missionAltitude=mission.engageAltitude*0.8 + mission.missionFraction=0.5 + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.NoReaction -- No reaction is better. + + -- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed. + mission.dTevaluate=5*60 + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + -- Get DCS task. + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create a BOMBRUNWAY mission. +-- @param #AUFTRAG self +-- @param Wrapper.Airbase#AIRBASE Airdrome The airbase to bomb. This must be an airdrome (not a FARP or ship) as these to not have a runway. +-- @param #number Altitude Engage altitude in feet. Default 25000 ft. +-- @return #AUFTRAG self +function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude) + + if type(Airdrome)=="string" then + Airdrome=AIRBASE:FindByName(Airdrome) + end + + local mission=AUFTRAG:New(AUFTRAG.Type.BOMBRUNWAY) + + mission:_TargetFromObject(Airdrome) + + -- DCS task options: + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL + mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000) + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.RUNWAYATTACK + mission.missionAltitude=mission.engageAltitude*0.8 + mission.missionFraction=0.75 + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + -- Evaluate result after 5 min. + mission.dTevaluate=5*60 + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + -- Get DCS task. + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR]** Create a CARPET BOMBING mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT or STATIC object. +-- @param #number Altitude Engage altitude in feet. Default 25000 ft. +-- @param #number CarpetLength Length of bombing carpet in meters. Default 500 m. +-- @return #AUFTRAG self +function AUFTRAG:NewBOMBCARPET(Target, Altitude, CarpetLength) + + local mission=AUFTRAG:New(AUFTRAG.Type.BOMBCARPET) + + mission:_TargetFromObject(Target) + + -- DCS task options: + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL + mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000) + mission.engageCarpetLength=CarpetLength or 500 + mission.engageAsGroup=false -- Looks like this must be false or the task is not executed. It is not available in the ME anyway but in the task of the mission file. + mission.engageDirection=nil -- This is also not available in the ME. + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.GROUNDATTACK + mission.missionAltitude=mission.engageAltitude*0.8 + mission.missionFraction=0.5 + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.NoReaction + + -- Evaluate result after 5 min. + mission.dTevaluate=5*60 + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + -- Get DCS task. + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + + +--- **[AIR]** Create an ESCORT (or FOLLOW) mission. Flight will escort another group and automatically engage certain target types. +-- @param #AUFTRAG self +-- @param Wrapper.Group#GROUP EscortGroup The group to escort. +-- @param DCS#Vec3 OffsetVector A table with x, y and z components specifying the offset of the flight to the escorted group. Default {x=-100, y=0, z=200} for z=200 meters to the right, same alitude (y=0), x=-100 meters behind. +-- @param #number EngageMaxDistance Max engage distance of targets in nautical miles. Default auto 32 NM. +-- @param #table TargetTypes Types of targets to engage automatically. Default is {"Air"}, i.e. all enemy airborne units. Use an empty set {} for a simple "FOLLOW" mission. +-- @return #AUFTRAG self +function AUFTRAG:NewESCORT(EscortGroup, OffsetVector, EngageMaxDistance, TargetTypes) + + local mission=AUFTRAG:New(AUFTRAG.Type.ESCORT) + + -- If only a string is passed we set a variable and check later if the group exists. + if type(EscortGroup)=="string" then + mission.escortGroupName=EscortGroup + mission:_TargetFromObject() + else + mission:_TargetFromObject(EscortGroup) + end + + -- DCS task parameters: + mission.escortVec3=OffsetVector or {x=-100, y=0, z=200} + mission.engageMaxDistance=EngageMaxDistance and UTILS.NMToMeters(EngageMaxDistance) or UTILS.NMToMeters(32) + mission.engageTargetTypes=TargetTypes or {"Air"} + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.ESCORT + mission.missionFraction=0.1 + mission.missionAltitude=1000 + mission.optionROE=ENUMS.ROE.OpenFire -- TODO: what's the best ROE here? Make dependent on ESCORT or FOLLOW! + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR ROTARY]** Create a RESCUE HELO mission. +-- @param #AUFTRAG self +-- @param Wrapper.Unit#UNIT Carrier The carrier unit. +-- @return #AUFTRAG self +function AUFTRAG:NewRESCUEHELO(Carrier) + + local mission=AUFTRAG:New(AUFTRAG.Type.RESCUEHELO) + + mission:_TargetFromObject(Carrier) + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.NOTHING + mission.missionFraction=0.5 + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionROT=ENUMS.ROT.NoReaction + + mission.categories={AUFTRAG.Category.HELICOPTER} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIRPANE]** Create a RECOVERY TANKER mission. +-- @param #AUFTRAG self +-- @param Wrapper.Unit#UNIT Carrier The carrier unit. +-- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft. +-- @param #number Speed Orbit speed in knots. Default 250 KIAS. +-- @param #number Leg Length of race-track in NM. Default 14 NM. +-- @param #number RelHeading Relative heading [0, 360) of race-track pattern in degrees wrt heading of the carrier. Default is heading of the carrier. +-- @param #number OffsetDist Relative distance of the first race-track point wrt to the carrier. Default 6 NM. +-- @param #number OffsetAngle Relative angle of the first race-track point wrt. to the carrier. Default 180 (behind the boat). +-- @param #number UpdateDistance Threshold distance in NM before orbit pattern is updated. Default 5 NM. +-- @return #AUFTRAG self +function AUFTRAG:NewRECOVERYTANKER(Carrier, Altitude, Speed, Leg, RelHeading, OffsetDist, OffsetAngle, UpdateDistance) + + -- Six NM astern. + local OffsetVec2={r=OffsetDist or 6, phi=OffsetAngle or 180} + + -- Default leg. + Leg=Leg or 14 + + -- Default Speed. + Speed=Speed or 250 + + local Heading=nil + if RelHeading then + Heading=-math.abs(RelHeading) + end + + -- Create orbit mission. + local mission=AUFTRAG:NewORBIT_GROUP(Carrier, Altitude, Speed, Leg, Heading, OffsetVec2, UpdateDistance) + + -- Set the type. + mission.type=AUFTRAG.Type.RECOVERYTANKER + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.REFUELING + mission.missionFraction=0.9 + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionROT=ENUMS.ROT.NoReaction + + mission.categories={AUFTRAG.Category.AIRPLANE} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + + +--- **[AIR ROTARY, GROUND]** Create a TROOP TRANSPORT mission. +-- @param #AUFTRAG self +-- @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 first transport group. +-- @param #number PickupRadius Radius around the pickup coordinate in meters. Default 100 m. +-- @return #AUFTRAG self +function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupCoordinate, PickupRadius) + + local mission=AUFTRAG:New(AUFTRAG.Type.TROOPTRANSPORT) + + if TransportGroupSet:IsInstanceOf("GROUP") then + mission.transportGroupSet=SET_GROUP:New() + mission.transportGroupSet:AddGroup(TransportGroupSet) + elseif TransportGroupSet:IsInstanceOf("SET_GROUP") then + mission.transportGroupSet=TransportGroupSet + else + mission:E(mission.lid.."ERROR: TransportGroupSet must be a GROUP or SET_GROUP object!") + return nil + end + + mission:_TargetFromObject(mission.transportGroupSet) + + 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 Transport") + --mission.transportDropoff:MarkToAll("Drop off") + + -- TODO: what's the best ROE here? + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.HELICOPTER, AUFTRAG.Category.GROUND} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR ROTARY]** Create a CARGO TRANSPORT mission. +-- **Important Note:** +-- The dropoff zone has to be a zone defined in the Mission Editor. This is due to a restriction in the used DCS task, which takes the zone ID as input. +-- Only ME zones have an ID that can be referenced. +-- @param #AUFTRAG self +-- @param Wrapper.Static#STATIC StaticCargo Static cargo object. +-- @param Core.Zone#ZONE DropZone Zone where to drop off the cargo. **Has to be a zone defined in the ME!** +-- @return #AUFTRAG self +function AUFTRAG:NewCARGOTRANSPORT(StaticCargo, DropZone) + + local mission=AUFTRAG:New(AUFTRAG.Type.CARGOTRANSPORT) + + mission:_TargetFromObject(StaticCargo) + + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CARGOTRANSPORT) + + -- Set ROE and ROT. + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.HELICOPTER} + + mission.DCStask=mission:GetDCSMissionTask() + + mission.DCStask.params.groupId=StaticCargo:GetID() + mission.DCStask.params.zoneId=DropZone.ZoneID + mission.DCStask.params.zone=DropZone + mission.DCStask.params.cargo=StaticCargo + + return mission +end + +--[[ + +--- **[AIR, GROUND, NAVAL]** Create a OPS TRANSPORT mission. +-- @param #AUFTRAG self +-- @param Core.Set#SET_GROUP CargoGroupSet The set group(s) to be transported. +-- @param Core.Zone#ZONE PickupZone Pick up zone +-- @param Core.Zone#ZONE DeployZone Deploy zone +-- @return #AUFTRAG self +function AUFTRAG:NewOPSTRANSPORT(CargoGroupSet, PickupZone, DeployZone) + + local mission=AUFTRAG:New(AUFTRAG.Type.OPSTRANSPORT) + + mission.transportGroupSet=CargoGroupSet + + mission:_TargetFromObject(mission.transportGroupSet) + + mission.opstransport=OPSTRANSPORT:New(CargoGroupSet, PickupZone, DeployZone) + + function mission.opstransport:OnAfterExecuting(From, Event, To) + mission:Executing() + end + + function mission.opstransport:OnAfterDelivered(From, Event, To) + mission:Done() + end + + -- TODO: what's the best ROE here? + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.ALL} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +]] + +--- **[GROUND, NAVAL]** Create an ARTY mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Target Center of the firing solution. +-- @param #number Nshots Number of shots to be fired. Default `#nil`. +-- @param #number Radius Radius of the shells in meters. Default 100 meters. +-- @param #number Altitude Altitude in meters. Can be used to setup a Barrage. Default `#nil`. +-- @return #AUFTRAG self +function AUFTRAG:NewARTY(Target, Nshots, Radius, Altitude) + + local mission=AUFTRAG:New(AUFTRAG.Type.ARTY) + + mission:_TargetFromObject(Target) + + mission.artyShots=Nshots or nil + mission.artyRadius=Radius or 100 + mission.artyAltitude=Altitude + + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + + mission.optionROE=ENUMS.ROE.OpenFire -- Ground/naval need open fire! + mission.optionAlarm=0 + + mission.missionFraction=0.0 + + -- Evaluate after 8 min. + mission.dTevaluate=8*60 + + mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[GROUND, NAVAL]** Create an BARRAGE mission. Assigned groups will move to a random coordinate within a given zone and start firing into the air. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE Zone The zone where the unit will go. +-- @param #number Heading Heading in degrees. Default random heading [0, 360). +-- @param #number Angle Shooting angle in degrees. Default random [45, 85]. +-- @param #number Radius Radius of the shells in meters. Default 100 meters. +-- @param #number Altitude Altitude in meters. Default 500 m. +-- @param #number Nshots Number of shots to be fired. Default is until ammo is empty (`#nil`). +-- @return #AUFTRAG self +function AUFTRAG:NewBARRAGE(Zone, Heading, Angle, Radius, Altitude, Nshots) + + local mission=AUFTRAG:New(AUFTRAG.Type.BARRAGE) + + mission:_TargetFromObject(Zone) + + mission.artyShots=Nshots + mission.artyRadius=Radius or 100 + mission.artyAltitude=Altitude + mission.artyHeading=Heading + mission.artyAngle=Angle + + mission.engageWeaponType=ENUMS.WeaponFlag.Auto + + mission.optionROE=ENUMS.ROE.OpenFire -- Ground/naval need open fire! + mission.optionAlarm=0 + + mission.missionFraction=0.0 + + -- Evaluate after instantly. + mission.dTevaluate=10 + + mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[AIR, GROUND, NAVAL]** Create a PATROLZONE mission. Group(s) will go to the zone and patrol it randomly. +-- @param #AUFTRAG self +-- @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 by ground units during patrol. Default "Off Road". +-- @return #AUFTRAG self +function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude, Formation) + + local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE) + + -- Ensure we got a ZONE and not just the zone name. + if type(Zone)=="string" then + Zone=ZONE:New(Zone) + end + + mission:_TargetFromObject(Zone) + + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.PATROLZONE) + + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionROT=ENUMS.ROT.PassiveDefense + mission.optionAlarm=ENUMS.AlarmState.Auto + + 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.ALL} + + mission.DCStask=mission:GetDCSMissionTask() + + mission.DCStask.params.formation=Formation or "Off Road" + + return mission +end + + +--- **[OBSOLETE]** Create a ARMORATTACK mission. +-- ** Note that this is actually creating a GROUNDATTACK mission!** +-- @param #AUFTRAG self +-- @param Ops.Target#TARGET Target The target to attack. Can be a GROUP, UNIT or STATIC object. +-- @param #number Speed Speed in knots. +-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. +-- @return #AUFTRAG self +function AUFTRAG:NewARMORATTACK(Target, Speed, Formation) + + local mission=AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation) + + -- Mission type. + mission.type=AUFTRAG.Type.ARMORATTACK + + return mission +end + +--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. +-- @param #number Speed Speed in knots. Default max. +-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`. +-- @return #AUFTRAG self +function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation) + + local mission=AUFTRAG:New(AUFTRAG.Type.GROUNDATTACK) + + mission:_TargetFromObject(Target) + + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.GROUNDATTACK) + + -- Defaults. + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionAlarm=ENUMS.AlarmState.Auto + mission.optionFormation="On Road" + mission.missionFraction=0.70 + mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil + + mission.categories={AUFTRAG.Category.GROUND} + + mission.DCStask=mission:GetDCSMissionTask() + + mission.DCStask.params.speed=Speed + mission.DCStask.params.formation=Formation or ENUMS.Formation.Vehicle.Vee + + return mission +end + +--- **[AIR, GROUND, NAVAL]** Create a RECON mission. +-- @param #AUFTRAG self +-- @param Core.Set#SET_ZONE ZoneSet The recon zones. +-- @param #number Speed Speed in knots. +-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL. +-- @param #boolean Adinfinitum If `true`, the group will start over again after reaching the final zone. +-- @param #boolean Randomly If `true`, the group will select a random zone. +-- @param #string Formation Formation used during recon route. +-- @return #AUFTRAG self +function AUFTRAG:NewRECON(ZoneSet, Speed, Altitude, Adinfinitum, Randomly, Formation) + + local mission=AUFTRAG:New(AUFTRAG.Type.RECON) + + mission:_TargetFromObject(ZoneSet) + + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.RECON) + + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionROT=ENUMS.ROT.PassiveDefense + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=0.5 + mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil + mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or UTILS.FeetToMeters(2000) + + mission.categories={AUFTRAG.Category.ALL} + + mission.DCStask=mission:GetDCSMissionTask() + mission.DCStask.params.adinfinitum=Adinfinitum + mission.DCStask.params.randomly=Randomly + mission.DCStask.params.formation=Formation + + return mission +end + +--- **[GROUND]** Create a AMMO SUPPLY mission. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE Zone The zone, where supply units go. +-- @return #AUFTRAG self +function AUFTRAG:NewAMMOSUPPLY(Zone) + + local mission=AUFTRAG:New(AUFTRAG.Type.AMMOSUPPLY) + + mission:_TargetFromObject(Zone) + + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=1.0 + + mission.missionWaypointRadius=0 + + mission.categories={AUFTRAG.Category.GROUND} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[GROUND]** Create a FUEL SUPPLY mission. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE Zone The zone, where supply units go. +-- @return #AUFTRAG self +function AUFTRAG:NewFUELSUPPLY(Zone) + + local mission=AUFTRAG:New(AUFTRAG.Type.FUELSUPPLY) + + mission:_TargetFromObject(Zone) + + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=1.0 + + mission.categories={AUFTRAG.Category.GROUND} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[GROUND]** Create a REARMING mission. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE Zone The zone, where units go and look for ammo supply. +-- @return #AUFTRAG self +function AUFTRAG:NewREARMING(Zone) + + local mission=AUFTRAG:New(AUFTRAG.Type.REARMING) + + mission:_TargetFromObject(Zone) + + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=1.0 + + mission.missionWaypointRadius=0 + + mission.categories={AUFTRAG.Category.GROUND} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + + +--- **[AIR]** Create an ALERT 5 mission. Aircraft will be spawned uncontrolled and wait for an assignment. You must specify **one** mission type which is performed. +-- This determines the payload and the DCS mission task which are used when the aircraft is spawned. +-- @param #AUFTRAG self +-- @param #string MissionType Mission type `AUFTRAG.Type.XXX`. Determines payload and mission task (intercept, ground attack, etc.). +-- @return #AUFTRAG self +function AUFTRAG:NewALERT5(MissionType) + + local mission=AUFTRAG:New(AUFTRAG.Type.ALERT5) + + mission.missionTask=self:GetMissionTaskforMissionType(MissionType) + + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionROT=ENUMS.ROT.NoReaction + + mission.alert5MissionType=MissionType + + mission.missionFraction=1.0 + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[GROUND, NAVAL]** Create an ON GUARD mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Coordinate, where to stand guard. +-- @return #AUFTRAG self +function AUFTRAG:NewONGUARD(Coordinate) + + local mission=AUFTRAG:New(AUFTRAG.Type.ONGUARD) + + mission:_TargetFromObject(Coordinate) + + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=1.0 + + mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[GROUND, NAVAL]** Create an AIRDEFENSE mission. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE Zone Zone where the air defense group(s) should be stationed. +-- @return #AUFTRAG self +function AUFTRAG:NewAIRDEFENSE(Zone) + + local mission=AUFTRAG:New(AUFTRAG.Type.AIRDEFENSE) + + mission:_TargetFromObject(Zone) + + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=1.0 + + mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[GROUND]** Create an EWR mission. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE Zone Zone where the Early Warning Radar group(s) should be stationed. +-- @return #AUFTRAG self +function AUFTRAG:NewEWR(Zone) + + local mission=AUFTRAG:New(AUFTRAG.Type.EWR) + + mission:_TargetFromObject(Zone) + + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=1.0 + + mission.categories={AUFTRAG.Category.GROUND} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + + +--- **[PRIVATE, AIR, GROUND, NAVAL]** Create a mission to relocate all cohort assets to another LEGION. +-- @param #AUFTRAG self +-- @param Ops.Legion#LEGION Legion The new legion. +-- @param Ops.Cohort#COHORT Cohort The cohort to be relocated. +-- @return #AUFTRAG self +function AUFTRAG:_NewRELOCATECOHORT(Legion, Cohort) + + local mission=AUFTRAG:New(AUFTRAG.Type.RELOCATECOHORT) + + mission:_TargetFromObject(Legion.spawnzone) + + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=0.0 + + mission.categories={AUFTRAG.Category.ALL} + + mission.DCStask=mission:GetDCSMissionTask() + + if Cohort.isGround then + mission.optionFormation=ENUMS.Formation.Vehicle.OnRoad + end + + mission.DCStask.params.legion=Legion + mission.DCStask.params.cohort=Cohort + + return mission +end + +--- **[GROUND, NAVAL]** Create a mission to do NOTHING. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE RelaxZone Zone where the assets are supposed to do nothing. +-- @return #AUFTRAG self +function AUFTRAG:NewNOTHING(RelaxZone) + + local mission=AUFTRAG:New(AUFTRAG.Type.NOTHING) + + mission:_TargetFromObject(RelaxZone) + + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=1.0 + + mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[GROUND]** Create an ARMORED ON GUARD mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Coordinate, where to stand guard. +-- @param #string Formation Formation to take, e.g. "On Road", "Vee" etc. +-- @return #AUFTRAG self +function AUFTRAG:NewARMOREDGUARD(Coordinate,Formation) + + local mission=AUFTRAG:New(AUFTRAG.Type.ARMOREDGUARD) + + mission:_TargetFromObject(Coordinate) + + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionAlarm=ENUMS.AlarmState.Auto + mission.optionFormation=Formation or "On Road" + + mission.missionFraction=1.0 + + mission.categories={AUFTRAG.Category.GROUND} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- Create a mission to attack a TARGET object. +-- @param #AUFTRAG self +-- @param Ops.Target#TARGET Target The target. +-- @param #string MissionType The mission type. +-- @return #AUFTRAG self +function AUFTRAG:NewFromTarget(Target, MissionType) + + local mission=nil --#AUFTRAG + + if MissionType==AUFTRAG.Type.ANTISHIP then + mission=self:NewANTISHIP(Target, Altitude) + elseif MissionType==AUFTRAG.Type.ARTY then + mission=self:NewARTY(Target, Nshots, Radius) + elseif MissionType==AUFTRAG.Type.BAI then + mission=self:NewBAI(Target, Altitude) + elseif MissionType==AUFTRAG.Type.BOMBCARPET then + mission=self:NewBOMBCARPET(Target, Altitude, CarpetLength) + elseif MissionType==AUFTRAG.Type.BOMBING then + mission=self:NewBOMBING(Target, Altitude) + elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then + mission=self:NewBOMBRUNWAY(Target, Altitude) + elseif MissionType==AUFTRAG.Type.CAS then + mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,Target:GetAverageCoordinate(),Heading,Leg,TargetTypes) + elseif MissionType==AUFTRAG.Type.CASENHANCED then + mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,RangeMax,NoEngageZoneSet,TargetTypes) + elseif MissionType==AUFTRAG.Type.INTERCEPT then + mission=self:NewINTERCEPT(Target) + elseif MissionType==AUFTRAG.Type.SEAD then + mission=self:NewSEAD(Target, Altitude) + elseif MissionType==AUFTRAG.Type.STRIKE then + mission=self:NewSTRIKE(Target, Altitude) + elseif MissionType==AUFTRAG.Type.ARMORATTACK then + mission=self:NewARMORATTACK(Target, Speed) + elseif MissionType==AUFTRAG.Type.GROUNDATTACK then + mission=self:NewGROUNDATTACK(Target, Speed, Formation) + else + return nil + end + + return mission +end + + +--- Create a mission to attack a group. Mission type is automatically chosen from the group category. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Target Target object. +-- @return #string Auftrag type, e.g. `AUFTRAG.Type.BAI` (="BAI"). +function AUFTRAG:_DetermineAuftragType(Target) + + local group=nil --Wrapper.Group#GROUP + local airbase=nil --Wrapper.Airbase#AIRBASE + local scenery=nil --Wrapper.Scenery#SCENERY + local coordinate=nil --Core.Point#COORDINATE + local auftrag=nil + + if Target:IsInstanceOf("GROUP") then + group=Target --Target is already a group. + elseif Target:IsInstanceOf("UNIT") then + group=Target:GetGroup() + elseif Target:IsInstanceOf("AIRBASE") then + airbase=Target + elseif Target:IsInstanceOf("SCENERY") then + scenery=Target + end + + if group then + + local category=group:GetCategory() + local attribute=group:GetAttribute() + + if category==Group.Category.AIRPLANE or category==Group.Category.HELICOPTER then + + --- + -- A2A: Intercept + --- + + auftrag=AUFTRAG.Type.INTERCEPT + + elseif category==Group.Category.GROUND or category==Group.Category.TRAIN then + + --- + -- GROUND + --- + + if attribute==GROUP.Attribute.GROUND_SAM then + + -- SEAD/DEAD + + auftrag=AUFTRAG.Type.SEAD + + elseif attribute==GROUP.Attribute.GROUND_AAA then + + auftrag=AUFTRAG.Type.BAI + + elseif attribute==GROUP.Attribute.GROUND_ARTILLERY then + + auftrag=AUFTRAG.Type.BAI + + elseif attribute==GROUP.Attribute.GROUND_INFANTRY then + + auftrag=AUFTRAG.Type.CAS + + elseif attribute==GROUP.Attribute.GROUND_TANK then + + auftrag=AUFTRAG.Type.BAI + + else + + auftrag=AUFTRAG.Type.BAI + + end + + + elseif category==Group.Category.SHIP then + + --- + -- NAVAL + --- + + auftrag=AUFTRAG.Type.ANTISHIP + + else + self:T(self.lid.."ERROR: Unknown Group category!") + end + + elseif airbase then + auftrag=AUFTRAG.Type.BOMBRUNWAY + elseif scenery then + auftrag=AUFTRAG.Type.STRIKE + elseif coordinate then + auftrag=AUFTRAG.Type.BOMBING + end + + return auftrag +end + +--- Create a mission to attack a group. Mission type is automatically chosen from the group category. +-- @param #AUFTRAG self +-- @param Wrapper.Group#GROUP EngageGroup Group to be engaged. +-- @return #AUFTRAG self +function AUFTRAG:NewAUTO(EngageGroup) + + local mission=nil --#AUFTRAG + + local Target=EngageGroup + + local auftrag=self:_DetermineAuftragType(EngageGroup) + + if auftrag==AUFTRAG.Type.ANTISHIP then + mission=AUFTRAG:NewANTISHIP(Target) + elseif auftrag==AUFTRAG.Type.ARTY then + mission=AUFTRAG:NewARTY(Target) + elseif auftrag==AUFTRAG.Type.AWACS then + mission=AUFTRAG:NewAWACS(Coordinate, Altitude,Speed,Heading,Leg) + elseif auftrag==AUFTRAG.Type.BAI then + mission=AUFTRAG:NewBAI(Target,Altitude) + elseif auftrag==AUFTRAG.Type.BOMBING then + mission=AUFTRAG:NewBOMBING(Target,Altitude) + elseif auftrag==AUFTRAG.Type.BOMBRUNWAY then + mission=AUFTRAG:NewBOMBRUNWAY(Airdrome,Altitude) + elseif auftrag==AUFTRAG.Type.BOMBCARPET then + mission=AUFTRAG:NewBOMBCARPET(Target,Altitude,CarpetLength) + elseif auftrag==AUFTRAG.Type.CAP then + mission=AUFTRAG:NewCAP(ZoneCAP,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes) + elseif auftrag==AUFTRAG.Type.CAS then + mission=AUFTRAG:NewCAS(ZoneCAS,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes) + elseif auftrag==AUFTRAG.Type.ESCORT then + mission=AUFTRAG:NewESCORT(EscortGroup,OffsetVector,EngageMaxDistance,TargetTypes) + elseif auftrag==AUFTRAG.Type.FACA then + mission=AUFTRAG:NewFACA(Target,Designation,DataLink,Frequency,Modulation) + elseif auftrag==AUFTRAG.Type.FERRY then + -- Not implemented yet. + elseif auftrag==AUFTRAG.Type.GCICAP then + mission=AUFTRAG:NewGCICAP(Coordinate,Altitude,Speed,Heading,Leg) + elseif auftrag==AUFTRAG.Type.INTERCEPT then + mission=AUFTRAG:NewINTERCEPT(Target) + elseif auftrag==AUFTRAG.Type.ORBIT then + mission=AUFTRAG:NewORBIT(Coordinate,Altitude,Speed,Heading,Leg) + elseif auftrag==AUFTRAG.Type.RECON then + -- Not implemented yet. + elseif auftrag==AUFTRAG.Type.RESCUEHELO then + mission=AUFTRAG:NewRESCUEHELO(Carrier) + elseif auftrag==AUFTRAG.Type.SEAD then + mission=AUFTRAG:NewSEAD(Target,Altitude) + elseif auftrag==AUFTRAG.Type.STRIKE then + mission=AUFTRAG:NewSTRIKE(Target,Altitude) + elseif auftrag==AUFTRAG.Type.TANKER then + mission=AUFTRAG:NewTANKER(Coordinate,Altitude,Speed,Heading,Leg,RefuelSystem) + elseif auftrag==AUFTRAG.Type.TROOPTRANSPORT then + mission=AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet,DropoffCoordinate,PickupCoordinate) + else + + end + + if mission then + mission:SetPriority(10, true) + end + + return mission +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User API Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Set mission start and stop time. +-- @param #AUFTRAG self +-- @param #string ClockStart Time the mission is started, e.g. "05:00" for 5 am. If specified as a #number, it will be relative (in seconds) to the current mission time. Default is 5 seconds after mission was added. +-- @param #string ClockStop (Optional) Time the mission is stopped, e.g. "13:00" for 1 pm. If mission could not be started at that time, it will be removed from the queue. If specified as a #number it will be relative (in seconds) to the current mission time. +-- @return #AUFTRAG self +function AUFTRAG:SetTime(ClockStart, ClockStop) + + -- Current mission time. + local Tnow=timer.getAbsTime() + + -- Set start time. Default in 5 sec. + local Tstart=Tnow+5 + if ClockStart and type(ClockStart)=="number" then + Tstart=Tnow+ClockStart + elseif ClockStart and type(ClockStart)=="string" then + Tstart=UTILS.ClockToSeconds(ClockStart) + end + + -- Set stop time. Default nil. + local Tstop=nil + if ClockStop and type(ClockStop)=="number" then + Tstop=Tnow+ClockStop + elseif ClockStop and type(ClockStop)=="string" then + Tstop=UTILS.ClockToSeconds(ClockStop) + end + + self.Tstart=Tstart + self.Tstop=Tstop + + if Tstop then + self.duration=self.Tstop-self.Tstart + end + + return self +end + +--- Set time how long the mission is executed. Once this time limit has passed, the mission is cancelled. +-- @param #AUFTRAG self +-- @param #number Duration Duration in seconds. +-- @return #AUFTRAG self +function AUFTRAG:SetDuration(Duration) + self.durationExe=Duration + return self +end + +--- Set that mission assets are teleported to the mission execution waypoint. +-- @param #AUFTRAG self +-- @param #boolean Switch If `true` or `nil`, teleporting is on. If `false`, teleporting is off. +-- @return #AUFTRAG self +function AUFTRAG:SetTeleport(Switch) + if Switch==nil then + Switch=true + end + self.teleport=Switch + return self +end + + +--- Set mission push time. This is the time the mission is executed. If the push time is not passed, the group will wait at the mission execution waypoint. +-- @param #AUFTRAG self +-- @param #string ClockPush Time the mission is executed, e.g. "05:00" for 5 am. Can also be given as a `#number`, where it is interpreted as relative push time in seconds. +-- @return #AUFTRAG self +function AUFTRAG:SetPushTime(ClockPush) + + if ClockPush then + if type(ClockPush)=="string" then + self.Tpush=UTILS.ClockToSeconds(ClockPush) + elseif type(ClockPush)=="number" then + self.Tpush=timer.getAbsTime()+ClockPush + end + end + + return self +end + +--- Set mission priority and (optional) urgency. Urgent missions can cancel other running missions. +-- @param #AUFTRAG self +-- @param #number Prio Priority 1=high, 100=low. Default 50. +-- @param #boolean Urgent If *true*, another running mission might be cancelled if it has a lower priority. +-- @param #number Importance Number 1-10. If missions with lower value are in the queue, these have to be finished first. Default is `nil`. +-- @return #AUFTRAG self +function AUFTRAG:SetPriority(Prio, Urgent, Importance) + self.prio=Prio or 50 + self.urgent=Urgent + self.importance=Importance + return self +end + +--- **[LEGION, COMMANDER, CHIEF]** Set how many times the mission is repeated. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level. +-- @param #AUFTRAG self +-- @param #number Nrepeat Number of repeats. Default 0. +-- @return #AUFTRAG self +function AUFTRAG:SetRepeat(Nrepeat) + self.Nrepeat=Nrepeat or 0 + return self +end + +--- **[LEGION, COMMANDER, CHIEF]** Set how many times the mission is repeated if it fails. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level. +-- @param #AUFTRAG self +-- @param #number Nrepeat Number of repeats. Default 0. +-- @return #AUFTRAG self +function AUFTRAG:SetRepeatOnFailure(Nrepeat) + self.NrepeatFailure=Nrepeat or 0 + return self +end + +--- **[LEGION, COMMANDER, CHIEF]** Set how many times the mission is repeated if it was successful. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level. +-- @param #AUFTRAG self +-- @param #number Nrepeat Number of repeats. Default 0. +-- @return #AUFTRAG self +function AUFTRAG:SetRepeatOnSuccess(Nrepeat) + self.NrepeatSuccess=Nrepeat or 0 + return self +end + +--- **[LEGION, COMMANDER, CHIEF]** Define how many assets are required to do the job. Only used if the mission is handled by a **LEGION** (AIRWING, BRIGADE, ...) or higher level. +-- @param #AUFTRAG self +-- @param #number NassetsMin Minimum number of asset groups. Default 1. +-- @param #number NassetsMax Maximum Number of asset groups. Default is same as `NassetsMin`. +-- @return #AUFTRAG self +function AUFTRAG:SetRequiredAssets(NassetsMin, NassetsMax) + + self.NassetsMin=NassetsMin or 1 + + self.NassetsMax=NassetsMax or self.NassetsMin + + -- Ensure that max is at least equal to min. + if self.NassetsMaxself.Tstop then + return false + end + + -- All start conditions true? + local startme=self:EvalConditionsAll(self.conditionStart) + + if not startme then + return false + end + + + -- We're good to go! + return true +end + +--- Check if mission is ready to be started. +-- * Mission stop already passed. +-- * Any stop condition is true. +-- @param #AUFTRAG self +-- @return #boolean If true, mission should be cancelled. +function AUFTRAG:IsReadyToCancel() + + local Tnow=timer.getAbsTime() + + -- Stop time already passed. + if self.Tstop and Tnow>=self.Tstop then + return true + end + + -- Evaluate failure condition. One is enough. + local failure=self:EvalConditionsAny(self.conditionFailure) + + if failure then + self.failurecondition=true + return true + end + + -- Evaluate success consitions. One is enough. + local success=self:EvalConditionsAny(self.conditionSuccess) + + if success then + self.successcondition=true + return true + end + + -- No criterion matched. + return false +end + +--- Check if mission is ready to be pushed. +-- * Mission push time already passed. +-- * **All** push conditions are true. +-- @param #AUFTRAG self +-- @return #boolean If true, mission groups can push. +function AUFTRAG:IsReadyToPush() + + local Tnow=timer.getAbsTime() + + -- Push time passed? + if self.Tpush and Tnow<=self.Tpush then + return false + end + + -- Evaluate push condition(s) if any. All need to be true. + local push=self:EvalConditionsAll(self.conditionPush) + + return push +end + +--- Check if all given condition are true. +-- @param #AUFTRAG self +-- @param #table Conditions Table of conditions. +-- @return #boolean If true, all conditions were true. Returns false if at least one condition returned false. +function AUFTRAG:EvalConditionsAll(Conditions) + + -- Any stop condition must be true. + for _,_condition in pairs(Conditions or {}) do + local condition=_condition --#AUFTRAG.Condition + + -- Call function. + local istrue=condition.func(unpack(condition.arg)) + + -- Any false will return false. + if not istrue then + return false + end + + end + + -- All conditions were true. + return true +end + + +--- Check if any of the given conditions is true. +-- @param #AUFTRAG self +-- @param #table Conditions Table of conditions. +-- @return #boolean If true, at least one condition is true. +function AUFTRAG:EvalConditionsAny(Conditions) + + -- Any stop condition must be true. + for _,_condition in pairs(Conditions or {}) do + local condition=_condition --#AUFTRAG.Condition + + -- Call function. + local istrue=condition.func(unpack(condition.arg)) + + -- Any true will return true. + if istrue then + return true + end + + end + + -- No condition was true. + return false +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Mission Status +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- On after "Status" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterStatus(From, Event, To) + + -- Current abs. mission time. + local Tnow=timer.getAbsTime() + + -- ESCORT: Check if only the group NAME of an escort had been specified. + if self.escortGroupName then + -- Try to find the group. + local group=GROUP:FindByName(self.escortGroupName) + if group and group:IsAlive() then + + -- Debug info. + self:T(self.lid..string.format("ESCORT group %s is now alive. Updating DCS task and adding group to TARGET", tostring(self.escortGroupName))) + + -- Add TARGET object. + self.engageTarget:AddObject(group) + + -- Update DCS task with the known group ID. + self.DCStask=self:GetDCSMissionTask() + + -- Set value to nil so we do not do this again in the next cycle. + self.escortGroupName=nil + end + end + + -- Number of alive mission targets. + local Ntargets=self:CountMissionTargets() + local Ntargets0=self:GetTargetInitialNumber() + + -- Number of alive groups attached to this mission. + local Ngroups=self:CountOpsGroups() + + -- Check if mission is not OVER yet. + if self:IsNotOver() then + + if self:CheckGroupsDone() then + + -- All groups have reported MISSON DONE. + self:Done() + + elseif (self.Tstop and Tnow>self.Tstop+10) then + + -- Cancel mission if stop time passed. + self:Cancel() + + elseif self.durationExe and self.Texecuting and Tnow-self.Texecuting>self.durationExe then + + -- Backup repeat values + local Nrepeat=self.Nrepeat + local NrepeatS=self.NrepeatSuccess + local NrepeatF=self.NrepeatFailure + + -- Cancel mission if stop time passed. + self:Cancel() + + self.Nrepeat=Nrepeat + self.NrepeatSuccess=NrepeatS + self.NrepeatFailure=NrepeatF + + elseif (Ntargets0>0 and Ntargets==0) then + + -- Cancel mission if mission targets are gone (if there were any in the beginning). + -- TODO: I commented this out for some reason but I forgot why... + self:T(self.lid.."No targets left cancelling mission!") + self:Cancel() + + elseif self:IsExecuting() then + + -- Had the case that mission was in state Executing but all assigned groups were dead. + -- TODO: might need to loop over all assigned groups + if Ngroups==0 then + self:Done() + else + local done=true + for groupname,data in pairs(self.groupdata or {}) do + local groupdata=data --#AUFTRAG.GroupData + local opsgroup=groupdata.opsgroup + if opsgroup:IsAlive() then + done=false + end + end + if done then + self:Done() + end + end + + end + + end + + -- Current FSM state. + local fsmstate=self:GetState() + + -- Check for error. + if fsmstate~=self.status then + self:T(self.lid..string.format("ERROR: FSM state %s != %s mission status!", fsmstate, self.status)) + end + + -- General info. + if self.verbose>=1 then + + -- Mission start stop time. + local Cstart=UTILS.SecondsToClock(self.Tstart, true) + local Cstop=self.Tstop and UTILS.SecondsToClock(self.Tstop, true) or "INF" + + local targetname=self:GetTargetName() or "unknown" + + local Nlegions=#self.legions + local commander=self.commander and self.statusCommander or "N/A" + local chief=self.chief and self.statusChief or "N/A" + + -- Info message. + self:T(self.lid..string.format("Status %s: Target=%s, T=%s-%s, assets=%d, groups=%d, targets=%d, legions=%d, commander=%s, chief=%s", + self.status, targetname, Cstart, Cstop, #self.assets, Ngroups, Ntargets, Nlegions, commander, chief)) + end + + -- Group info. + if self.verbose>=2 then + -- Data on assigned groups. + local text="Group data:" + for groupname,_groupdata in pairs(self.groupdata) do + local groupdata=_groupdata --#AUFTRAG.GroupData + text=text..string.format("\n- %s: status mission=%s opsgroup=%s", groupname, groupdata.status, groupdata.opsgroup and groupdata.opsgroup:GetState() or "N/A") + end + self:I(self.lid..text) + end + + -- Ready to evaluate mission outcome? + local ready2evaluate=self.Tover and Tnow-self.Tover>=self.dTevaluate or false + + -- Check if mission is OVER (done or cancelled) and enough time passed to evaluate the result. + if self:IsOver() and ready2evaluate then + -- Evaluate success or failure of the mission. + self:Evaluate() + else + self:__Status(-30) + end + + -- Update F10 marker. + if self.markerOn then + self:UpdateMarker() + end + +end + +--- Evaluate mission outcome - success or failure. +-- @param #AUFTRAG self +-- @return #AUFTRAG self +function AUFTRAG:Evaluate() + + -- Assume success and check if any failed condition applies. + local failed=false + + -- Target damage in %. + local targetdamage=self:GetTargetDamage() + + -- Own damage in %. + local owndamage=self.Ncasualties/self.Nelements*100 + + -- Current number of mission targets. + local Ntargets=self:CountMissionTargets() + local Ntargets0=self:GetTargetInitialNumber() + + local Life=self:GetTargetLife() + local Life0=self:GetTargetInitialLife() + + + if Ntargets0>0 then + + --- + -- Mission had targets + --- + + -- Check if failed. + if self.type==AUFTRAG.Type.TROOPTRANSPORT or self.type==AUFTRAG.Type.ESCORT then + + -- Transported or escorted groups have to survive. + if Ntargets0 then + failed=true + end + + end + + else + + --- + -- Mission had NO targets + --- + + -- No targets and everybody died ==> mission failed. Well, unless success condition is true. + if self.Nelements==self.Ncasualties then + failed=true + end + + end + + + -- Any success condition true? + local successCondition=self:EvalConditionsAny(self.conditionSuccess) + + -- Any failure condition true? + local failureCondition=self:EvalConditionsAny(self.conditionFailure) + + if failureCondition then + failed=true + elseif successCondition then + failed=false + end + + -- Debug text. + if self.verbose > 0 then + local text=string.format("Evaluating mission:\n") + text=text..string.format("Own casualties = %d/%d\n", self.Ncasualties, self.Nelements) + text=text..string.format("Own losses = %.1f %%\n", owndamage) + text=text..string.format("Killed units = %d\n", self.Nkills) + text=text..string.format("--------------------------\n") + text=text..string.format("Targets left = %d/%d\n", Ntargets, Ntargets0) + text=text..string.format("Targets life = %.1f/%.1f\n", Life, Life0) + text=text..string.format("Enemy losses = %.1f %%\n", targetdamage) + text=text..string.format("--------------------------\n") + text=text..string.format("Success Cond = %s\n", tostring(successCondition)) + text=text..string.format("Failure Cond = %s\n", tostring(failureCondition)) + text=text..string.format("--------------------------\n") + text=text..string.format("Final Success = %s\n", tostring(not failed)) + text=text..string.format("=========================") + self:I(self.lid..text) + end + + -- Trigger events. + if failed then + self:I(self.lid..string.format("Mission %d [%s] failed!", self.auftragsnummer, self.type)) + if self.chief then + self.chief.Nfailure=self.chief.Nfailure+1 + end + self:Failed() + else + self:I(self.lid..string.format("Mission %d [%s] success!", self.auftragsnummer, self.type)) + if self.chief then + self.chief.Nsuccess=self.chief.Nsuccess+1 + end + self:Success() + end + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Asset Data +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get all OPS groups. +-- @param #AUFTRAG self +-- @return #table Table of Ops.OpsGroup#OPSGROUP or {}. +function AUFTRAG:GetOpsGroups() + local opsgroups={} + for _,_groupdata in pairs(self.groupdata or {}) do + local groupdata=_groupdata --#AUFTRAG.GroupData + table.insert(opsgroups, groupdata.opsgroup) + end + return opsgroups +end + +--- Get asset data table. +-- @param #AUFTRAG self +-- @param #string AssetName Name of the asset. +-- @return #AUFTRAG.GroupData Group data or *nil* if OPS group does not exist. +function AUFTRAG:GetAssetDataByName(AssetName) + return self.groupdata[tostring(AssetName)] +end + +--- Get flight data table. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The flight group. +-- @return #AUFTRAG.GroupData Flight data or nil if opsgroup does not exist. +function AUFTRAG:GetGroupData(opsgroup) + if opsgroup and self.groupdata then + return self.groupdata[opsgroup.groupname] + end + return nil +end + +--- Set opsgroup mission status. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The flight group. +-- @param #string status New status. +-- @return #AUFTRAG self +function AUFTRAG:SetGroupStatus(opsgroup, status) + + -- Current status. + local oldstatus=self:GetGroupStatus(opsgroup) + + -- Debug info. + self:T(self.lid..string.format("Setting OPSGROUP %s to status %s-->%s", opsgroup and opsgroup.groupname or "nil", tostring(oldstatus), tostring(status))) + + if oldstatus==AUFTRAG.GroupStatus.CANCELLED and status==AUFTRAG.GroupStatus.DONE then + -- Do not overwrite a CANCELLED status with a DONE status. + else + local groupdata=self:GetGroupData(opsgroup) + if groupdata then + groupdata.status=status + else + self:T(self.lid.."WARNING: Could not SET flight data for flight group. Setting status to DONE") + end + end + + -- Check if mission is NOT over. + local isNotOver=self:IsNotOver() + + -- Check if all assigned groups are done. + 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(groupsDone))) + + -- Check if ALL flights are done with their mission. + if isNotOver and groupsDone then + self:T3(self.lid.."All assigned OPSGROUPs done ==> mission DONE!") + self:Done() + else + self:T3(self.lid.."Mission NOT DONE yet!") + end + + return self +end + +--- Get ops group mission status. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @return #string The group status. +function AUFTRAG:GetGroupStatus(opsgroup) + self:T3(self.lid..string.format("Trying to get Flight status for flight group %s", opsgroup and opsgroup.groupname or "nil")) + + local groupdata=self:GetGroupData(opsgroup) + + if groupdata then + return groupdata.status + else + + self:T(self.lid..string.format("WARNING: Could not GET groupdata for opsgroup %s. Returning status DONE.", opsgroup and opsgroup.groupname or "nil")) + return AUFTRAG.GroupStatus.DONE + + end +end + +--- Add LEGION to mission. +-- @param #AUFTRAG self +-- @param Ops.Legion#LEGION Legion The legion. +-- @return #AUFTRAG self +function AUFTRAG:AddLegion(Legion) + + -- Debug info. + self:T(self.lid..string.format("Adding legion %s", Legion.alias)) + + -- Add legion to table. + table.insert(self.legions, Legion) + + return self +end + +--- Remove LEGION from mission. +-- @param #AUFTRAG self +-- @param Ops.Legion#LEGION Legion The legion. +-- @return #AUFTRAG self +function AUFTRAG:RemoveLegion(Legion) + + -- Loop over legions + for i=#self.legions,1,-1 do + local legion=self.legions[i] --Ops.Legion#LEGION + + if legion.alias==Legion.alias then + + -- Debug info. + self:T(self.lid..string.format("Removing legion %s", Legion.alias)) + table.remove(self.legions, i) + + -- Set legion status to nil. + self.statusLegion[Legion.alias]=nil + + return self + end + + end + + self:T(self.lid..string.format("ERROR: Legion %s not found and could not be removed!", Legion.alias)) + return self +end + +--- Set LEGION mission status. +-- @param #AUFTRAG self +-- @param Ops.Legion#LEGION Legion The legion. +-- @param #string Status New status. +-- @return #AUFTRAG self +function AUFTRAG:SetLegionStatus(Legion, Status) + + -- Old status + local status=self:GetLegionStatus(Legion) + + -- Debug info. + self:T(self.lid..string.format("Setting LEGION %s to status %s-->%s", Legion.alias, tostring(status), tostring(Status))) + + -- New status. + self.statusLegion[Legion.alias]=Status + + return self +end + +--- Get LEGION mission status. +-- @param #AUFTRAG self +-- @param Ops.Legion#LEGION Legion The legion. +-- @return #string status Current status. +function AUFTRAG:GetLegionStatus(Legion) + + -- New status. + local status=self.statusLegion[Legion.alias] or "unknown" + + return status +end + + +--- Set mission (ingress) waypoint coordinate for OPS group. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @param Core.Point#COORDINATE coordinate Waypoint Coordinate. +-- @return #AUFTRAG self +function AUFTRAG:SetGroupWaypointCoordinate(opsgroup, coordinate) + local groupdata=self:GetGroupData(opsgroup) + if groupdata then + groupdata.waypointcoordinate=coordinate + end + return self +end + +--- Get mission (ingress) waypoint coordinate of OPS group +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @return Core.Point#COORDINATE Waypoint Coordinate. +function AUFTRAG:GetGroupWaypointCoordinate(opsgroup) + local groupdata=self:GetGroupData(opsgroup) + if groupdata then + return groupdata.waypointcoordinate + end +end + + +--- Set mission waypoint task for OPS group. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @param Ops.OpsGroup#OPSGROUP.Task task Waypoint task. +function AUFTRAG:SetGroupWaypointTask(opsgroup, task) + self:T2(self.lid..string.format("Setting waypoint task %s", task and task.description or "WTF")) + local groupdata=self:GetGroupData(opsgroup) + if groupdata then + groupdata.waypointtask=task + end +end + +--- Get mission waypoint task of OPS group. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @return Ops.OpsGroup#OPSGROUP.Task task Waypoint task. Waypoint task. +function AUFTRAG:GetGroupWaypointTask(opsgroup) + local groupdata=self:GetGroupData(opsgroup) + if groupdata then + return groupdata.waypointtask + end +end + +--- Set mission (ingress) waypoint UID for OPS group. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @param #number waypointindex Waypoint UID. +-- @return #AUFTRAG self +function AUFTRAG:SetGroupWaypointIndex(opsgroup, waypointindex) + self:T2(self.lid..string.format("Setting Mission waypoint UID=%d", waypointindex)) + local groupdata=self:GetGroupData(opsgroup) + if groupdata then + groupdata.waypointindex=waypointindex + end + return self +end + +--- Get mission (ingress) waypoint UID of OPS group. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @return #number Waypoint UID. +function AUFTRAG:GetGroupWaypointIndex(opsgroup) + local groupdata=self:GetGroupData(opsgroup) + if groupdata then + return groupdata.waypointindex + end +end + +--- Set Egress waypoint UID for OPS group. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @param #number waypointindex Waypoint UID. +-- @return #AUFTRAG self +function AUFTRAG:SetGroupEgressWaypointUID(opsgroup, waypointindex) + self:T2(self.lid..string.format("Setting Egress waypoint UID=%d", waypointindex)) + local groupdata=self:GetGroupData(opsgroup) + if groupdata then + groupdata.waypointEgressUID=waypointindex + end + return self +end + +--- Get Egress waypoint UID of OPS group. +-- @param #AUFTRAG self +-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. +-- @return #number Waypoint UID. +function AUFTRAG:GetGroupEgressWaypointUID(opsgroup) + local groupdata=self:GetGroupData(opsgroup) + if groupdata then + return groupdata.waypointEgressUID + end +end + + + +--- Check if all flights are done with their mission (or dead). +-- @param #AUFTRAG self +-- @return #boolean If true, all flights are done with the mission. +function AUFTRAG:CheckGroupsDone() + + -- Check status of all OPS groups. + for groupname,data in pairs(self.groupdata) do + local groupdata=data --#AUFTRAG.GroupData + if groupdata then + if not (groupdata.status==AUFTRAG.GroupStatus.DONE or groupdata.status==AUFTRAG.GroupStatus.CANCELLED) then + -- At least this flight is not DONE or CANCELLED. + self:T2(self.lid..string.format("CheckGroupsDone: OPSGROUP %s is not DONE or CANCELLED but in state %s. Mission NOT DONE!", groupdata.opsgroup.groupname, groupdata.status:upper())) + return false + end + end + end + + -- Check status of all LEGIONs. + for _,_legion in pairs(self.legions) do + local legion=_legion --Ops.Legion#LEGION + local status=self:GetLegionStatus(legion) + if not status==AUFTRAG.Status.CANCELLED then + -- At least one LEGION has not CANCELLED. + self:T2(self.lid..string.format("CheckGroupsDone: LEGION %s is not CANCELLED but in state %s. Mission NOT DONE!", legion.alias, status)) + return false + end + end + + -- Check commander status. + if self.commander then + if not self.statusCommander==AUFTRAG.Status.CANCELLED then + self:T2(self.lid..string.format("CheckGroupsDone: COMMANDER is not CANCELLED but in state %s. Mission NOT DONE!", self.statusCommander)) + return false + end + end + + -- Check chief status. + if self.chief then + if not self.statusChief==AUFTRAG.Status.CANCELLED then + self:T2(self.lid..string.format("CheckGroupsDone: CHIEF is not CANCELLED but in state %s. Mission NOT DONE!", self.statusChief)) + return false + end + end + + -- These are early stages, where we might not even have a opsgroup defined to be checked. If there were any groups, we checked above. + if self:IsPlanned() or self:IsQueued() or self:IsRequested() then + self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] (PLANNED or QUEUED or REQUESTED). Mission NOT DONE!", self.status, self:GetState())) + return false + end + + -- It could be that all flights were destroyed on the way to the mission execution waypoint. + -- TODO: would be better to check if everybody is dead by now. + if self:IsStarted() and self:CountOpsGroups()==0 then + self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!", self.status, self:GetState())) + return true + end + + return true +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- EVENT Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Unit lost event. +-- @param #AUFTRAG self +-- @param Core.Event#EVENTDATA EventData Event data. +function AUFTRAG:OnEventUnitLost(EventData) + + -- Check that this is the right group. + if EventData and EventData.IniGroup and EventData.IniUnit then + local unit=EventData.IniUnit + local group=EventData.IniGroup + local unitname=EventData.IniUnitName + + for _,_groupdata in pairs(self.groupdata) do + local groupdata=_groupdata --#AUFTRAG.GroupData + if groupdata and groupdata.opsgroup and groupdata.opsgroup.groupname==EventData.IniGroupName then + self:T(self.lid..string.format("UNIT LOST event for opsgroup %s unit %s", groupdata.opsgroup.groupname, EventData.IniUnitName)) + end + end + + end + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- FSM Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +--- On after "Planned" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterPlanned(From, Event, To) + self.status=AUFTRAG.Status.PLANNED + self:T(self.lid..string.format("New mission status=%s", self.status)) +end + +--- On after "Queue" event. Mission is added to the mission queue of a LEGION. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterQueued(From, Event, To, Airwing) + self.status=AUFTRAG.Status.QUEUED + self:T(self.lid..string.format("New mission status=%s", self.status)) +end + + +--- On after "Requested" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterRequested(From, Event, To) + self.status=AUFTRAG.Status.REQUESTED + self:T(self.lid..string.format("New mission status=%s", self.status)) +end + +--- On after "Assign" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterAssign(From, Event, To) + self.status=AUFTRAG.Status.ASSIGNED + self:T(self.lid..string.format("New mission status=%s", self.status)) +end + +--- On after "Schedule" event. Mission is added to the mission queue of an OPSGROUP. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterScheduled(From, Event, To) + self.status=AUFTRAG.Status.SCHEDULED + self:T(self.lid..string.format("New mission status=%s", self.status)) +end + +--- On after "Start" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterStarted(From, Event, To) + self.status=AUFTRAG.Status.STARTED + self.Tstarted=timer.getAbsTime() + self:T(self.lid..string.format("New mission status=%s", self.status)) +end + +--- On after "Execute" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterExecuting(From, Event, To) + self.status=AUFTRAG.Status.EXECUTING + self.Texecuting=timer.getAbsTime() + self:T(self.lid..string.format("New mission status=%s", self.status)) +end + +--- On after "ElementDestroyed" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Ops.OpsGroup#OPSGROUP OpsGroup The ops group to which the element belongs. +-- @param Ops.OpsGroup#OPSGROUP.Element Element The element that got destroyed. +function AUFTRAG:onafterElementDestroyed(From, Event, To, OpsGroup, Element) + -- Increase number of own casualties. + self.Ncasualties=self.Ncasualties+1 +end + +--- On after "GroupDead" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Ops.OpsGroup#OPSGROUP OpsGroup The ops group that is dead now. +function AUFTRAG:onafterGroupDead(From, Event, To, OpsGroup) + + local asset=self:GetAssetByName(OpsGroup.groupname) + if asset then + self:AssetDead(asset) + end + + -- Number of dead groups. + self.Ndead=self.Ndead+1 + +end + +--- On after "AssetDead" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset. +function AUFTRAG:onafterAssetDead(From, Event, To, Asset) + + -- Number of groups alive. + local N=self:CountOpsGroups() + + self:T(self.lid..string.format("Asset %s dead! Number of ops groups remaining %d", tostring(Asset.spawngroupname), N)) + + -- All assets dead? + if N==0 then + + if self:IsNotOver() then + + -- Cancel mission. Wait for next mission update to evaluate SUCCESS or FAILURE. + self:Cancel() + + else + + --self:E(self.lid.."ERROR: All assets are dead not but mission was already over... Investigate!") + -- Now this can happen, because when a opsgroup dies (sometimes!), the mission is DONE + + end + end + + -- Delete asset from mission. + self:DelAsset(Asset) + +end + +--- On after "Cancel" event. Cancells the mission. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterCancel(From, Event, To) + + -- Number of OPSGROUPS assigned and alive. + local Ngroups = self:CountOpsGroups() + + -- Debug info. + self:T(self.lid..string.format("CANCELLING mission in status %s. Will wait for %d groups to report mission DONE before evaluation", self.status, Ngroups)) + + -- Time stamp. + self.Tover=timer.getAbsTime() + + -- No more repeats. + self.Nrepeat=self.repeated + self.NrepeatFailure=self.repeatedFailure + self.NrepeatSuccess=self.repeatedSuccess + + -- Not necessary to delay the evaluaton?! + self.dTevaluate=0 + + if self.chief then + + -- Debug info. + self:T(self.lid..string.format("CHIEF will cancel the mission. Will wait for mission DONE before evaluation!")) + + -- CHIEF will cancel the mission. + self.chief:MissionCancel(self) + + elseif self.commander then + + -- Debug info. + self:T(self.lid..string.format("COMMANDER will cancel the mission. Will wait for mission DONE before evaluation!")) + + -- COMMANDER will cancel the mission. + self.commander:MissionCancel(self) + + elseif self.legions and #self.legions>0 then + + -- Loop over all LEGIONs. + for _,_legion in pairs(self.legions or {}) do + local legion=_legion --Ops.Legion#LEGION + + -- Debug info. + self:T(self.lid..string.format("LEGION %s will cancel the mission. Will wait for mission DONE before evaluation!", legion.alias)) + + -- Legion will cancel all flight missions and remove queued request from warehouse queue. + legion:MissionCancel(self) + + end + + else + + -- Debug info. + self:T(self.lid..string.format("No legion, commander or chief. Attached groups will cancel the mission on their own. Will wait for mission DONE before evaluation!")) + + -- Loop over all groups. + for _,_groupdata in pairs(self.groupdata or {}) do + local groupdata=_groupdata --#AUFTRAG.GroupData + groupdata.opsgroup:MissionCancel(self) + end + + end + + -- Special mission states. + if self:IsPlanned() or self:IsQueued() or self:IsRequested() or Ngroups==0 then + self:T(self.lid..string.format("Cancelled mission was in %s stage with %d groups assigned and alive. Call it done!", self.status, Ngroups)) + self:Done() + end + +end + +--- On after "Done" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterDone(From, Event, To) + self.status=AUFTRAG.Status.DONE + self:T(self.lid..string.format("New mission status=%s", self.status)) + + -- Set time stamp. + self.Tover=timer.getAbsTime() + + -- Not executing any more. + self.Texecuting=nil + + -- Set status for CHIEF. + self.statusChief=AUFTRAG.Status.DONE + + -- Set status for COMMANDER. + self.statusCommander=AUFTRAG.Status.DONE + + -- Set status for LEGIONs. + for _,_legion in pairs(self.legions) do + local Legion=_legion --Ops.Legion#LEGION + + self:SetLegionStatus(Legion, AUFTRAG.Status.DONE) + + -- Remove pending request from legion queue. + if self.type==AUFTRAG.Type.RELOCATECOHORT then + + -- Get request ID + local requestid=self.requestID[Legion.alias] + + if requestid then + + -- Debug info. + self:T(self.lid.."Removing request from pending queue") + + -- Remove request from pending queue. + Legion:_DeleteQueueItemByID(requestid, Legion.pending) + + -- Remove cohort from old legion. + local Cohort=self.DCStask.params.cohort --Ops.Cohort#COHORT + Legion:DelCohort(Cohort) + + else + self:E(self.lid.."WARNING: Could NOT remove relocation request from from pending queue (all assets were spawned?)") + end + end + end + + -- Trigger relocated event. + if self.type==AUFTRAG.Type.RELOCATECOHORT then + local cohort=self.DCStask.params.cohort --Ops.Cohort#COHORT + cohort:Relocated() + end +end + +--- On after "Success" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterSuccess(From, Event, To) + + self.status=AUFTRAG.Status.SUCCESS + self:T(self.lid..string.format("New mission status=%s", self.status)) + + -- Set status for CHIEF, COMMANDER and LEGIONs + self.statusChief=self.status + self.statusCommander=self.status + for _,_legion in pairs(self.legions) do + local Legion=_legion --Ops.Legion#LEGION + self:SetLegionStatus(Legion, self.status) + end + + local repeatme=self.repeatedSuccess Repeat mission!", self.repeated+1, N)) + self:Repeat() + + else + + -- Stop mission. + self:T(self.lid..string.format("Mission SUCCESS! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) + self:Stop() + + end + +end + +--- On after "Failed" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterFailed(From, Event, To) + + self.status=AUFTRAG.Status.FAILED + self:T(self.lid..string.format("New mission status=%s", self.status)) + + -- Set status for CHIEF, COMMANDER and LEGIONs + self.statusChief=self.status + self.statusCommander=self.status + for _,_legion in pairs(self.legions) do + local Legion=_legion --Ops.Legion#LEGION + self:SetLegionStatus(Legion, self.status) + end + + local repeatme=self.repeatedFailure Repeat mission!", self.repeated+1, N)) + self:Repeat() + + else + + -- Stop mission. + self:T(self.lid..string.format("Mission FAILED! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) + self:Stop() + + end + +end + +--- On before "Repeat" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onbeforeRepeat(From, Event, To) + + if not (self.chief or self.commander or #self.legions>0) then + self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, COMMANDER or LEGION! Stopping AUFTRAG") + self:Stop() + return false + end + + return true +end + +--- On after "Repeat" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterRepeat(From, Event, To) + + -- Set mission status to PLANNED. + self.status=AUFTRAG.Status.PLANNED + + -- Debug info. + self:T(self.lid..string.format("New mission status=%s (on Repeat)", self.status)) + + -- Set status for CHIEF, COMMANDER and LEGIONs + self.statusChief=self.status + self.statusCommander=self.status + for _,_legion in pairs(self.legions) do + local Legion=_legion --Ops.Legion#LEGION + self:SetLegionStatus(Legion, self.status) + end + + -- Increase repeat counter. + self.repeated=self.repeated+1 + + if self.chief then + + -- Set status for chief. + self.statusChief=AUFTRAG.Status.PLANNED + + -- Remove mission from wingcommander because Chief will assign it again. + if self.commander then + self.statusCommander=AUFTRAG.Status.PLANNED + end + + -- Remove mission from legions because commander will assign it again but maybe to different legion(s). + for _,_legion in pairs(self.legions) do + local legion=_legion --Ops.Legion#LEGION + legion:RemoveMission(self) + end + + elseif self.commander then + + -- Set status for commander. + self.statusCommander=AUFTRAG.Status.PLANNED + + -- Remove mission from legion(s) because commander will assign it again but maybe to different legion(s). + for _,_legion in pairs(self.legions) do + local legion=_legion --Ops.Legion#LEGION + legion:RemoveMission(self) + self:SetLegionStatus(legion, AUFTRAG.Status.PLANNED) + end + + elseif #self.legions>0 then + + -- Remove mission from airwing because WC will assign it again but maybe to a different wing. + for _,_legion in pairs(self.legions) do + local legion=_legion --Ops.Legion#LEGION + legion:RemoveMission(self) + self:SetLegionStatus(legion, AUFTRAG.Status.PLANNED) + legion:AddMission(self) + end + + else + self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, COMMANDER or LEGION! Stopping AUFTRAG") + self:Stop() + return + end + + + -- No mission assets. + self.assets={} + + + -- Remove OPS groups. This also removes the mission from the OPSGROUP mission queue. + for groupname,_groupdata in pairs(self.groupdata) do + local groupdata=_groupdata --#AUFTRAG.GroupData + local opsgroup=groupdata.opsgroup + if opsgroup then + self:DelOpsGroup(opsgroup) + end + + end + -- No flight data. + self.groupdata={} + + -- Reset casualties and units assigned. + self.Ncasualties=0 + self.Nelements=0 + self.Ngroups=0 + self.Nassigned=nil + self.Ndead=0 + + -- Update DCS mission task. Could be that the initial task (e.g. for bombing) was destroyed. Then we need to update the coordinate. + self.DCStask=self:GetDCSMissionTask() + + -- Call status again. + self:__Status(-30) + +end + +--- On after "Stop" event. Remove mission from AIRWING and FLIGHTGROUP mission queues. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterStop(From, Event, To) + + -- Debug info. + self:T(self.lid..string.format("STOPPED mission in status=%s. Removing missions from queues. Stopping CallScheduler!", self.status)) + + -- TODO: Mission should be OVER! we dont want to remove running missions from any queues. + + -- Remove mission from CHIEF queue. + if self.chief then + self.chief:RemoveMission(self) + end + + -- Remove mission from WINGCOMMANDER queue. + if self.commander then + self.commander:RemoveMission(self) + end + + -- Remove mission from LEGION queues. + if #self.legions>0 then + for _,_legion in pairs(self.legions) do + local legion=_legion --Ops.Legion#LEGION + legion:RemoveMission(self) + end + end + + -- Remove mission from OPSGROUP queue + for _,_groupdata in pairs(self.groupdata) do + local groupdata=_groupdata --#AUFTRAG.GroupData + groupdata.opsgroup:RemoveMission(self) + end + + -- No mission assets. + self.assets={} + + -- No flight data. + self.groupdata={} + + -- Clear pending scheduler calls. + self.CallScheduler:Clear() + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Target Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create target data from a given object. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC. +function AUFTRAG:_TargetFromObject(Object) + + if not self.engageTarget then + + if Object and Object:IsInstanceOf("TARGET") then + + self.engageTarget=Object + + else --if Object then + + self.engageTarget=TARGET:New(Object) + + end + + else + + -- Target was already specified elsewhere. + + end + + -- Debug info. + --self:T2(self.lid..string.format("Mission Target %s Type=%s, Ntargets=%d, Lifepoints=%d", self.engageTarget.lid, self.engageTarget.lid, self.engageTarget.N0, self.engageTarget:GetLife())) + + return self +end + + +--- Count alive mission targets. +-- @param #AUFTRAG self +-- @return #number Number of alive target units. +function AUFTRAG:CountMissionTargets() + + local N=0 + + if self.engageTarget then + N=self.engageTarget:CountTargets() + end + + return N +end + +--- Get initial number of targets. +-- @param #AUFTRAG self +-- @return #number Number of initial life points when mission was planned. +function AUFTRAG:GetTargetInitialNumber() + local target=self:GetTargetData() + if target then + return target.N0 + else + return 0 + end +end + + +--- Get target life points. +-- @param #AUFTRAG self +-- @return #number Number of initial life points when mission was planned. +function AUFTRAG:GetTargetInitialLife() + local target=self:GetTargetData() + if target then + return target.life0 + else + return 0 + end +end + +--- Get target damage. +-- @param #AUFTRAG self +-- @return #number Damage in percent. +function AUFTRAG:GetTargetDamage() + local target=self:GetTargetData() + if target then + return target:GetDamage() + else + return 0 + end +end + + +--- Get target life points. +-- @param #AUFTRAG self +-- @return #number Life points of target. +function AUFTRAG:GetTargetLife() + local target=self:GetTargetData() + if target then + return target:GetLife() + else + return 0 + end +end + +--- Get target. +-- @param #AUFTRAG self +-- @return Ops.Target#TARGET The target object. Could be many things. +function AUFTRAG:GetTargetData() + return self.engageTarget +end + +--- Get mission objective object. Could be many things depending on the mission type. +-- @param #AUFTRAG self +-- @return Wrapper.Positionable#POSITIONABLE The target object. Could be many things. +function AUFTRAG:GetObjective() + local objective=self:GetTargetData():GetObject() + return objective +end + +--- Get type of target. +-- @param #AUFTRAG self +-- @return #string The target type. +function AUFTRAG:GetTargetType() + local target=self.engageTarget + if target then + local to=target:GetObjective() + if to then + return to.Type + else + return "Unknown" + end + else + return "Unknown" + end +end + +--- Get 2D vector of target. +-- @param #AUFTRAG self +-- @return DCS#VEC2 The target 2D vector or *nil*. +function AUFTRAG:GetTargetVec2() + local coord=self:GetTargetCoordinate() + if coord then + local vec2=coord:GetVec2() + return vec2 + end + return nil +end + +--- Get coordinate of target. +-- @param #AUFTRAG self +-- @return Core.Point#COORDINATE The target coordinate or *nil*. +function AUFTRAG:GetTargetCoordinate() + + if self.transportPickup then + + -- Special case where we defined a + return self.transportPickup + + elseif self.engageTarget then + + local coord=self.engageTarget:GetCoordinate() + return coord + + elseif self.type==AUFTRAG.Type.ALERT5 then + + -- For example, COMMANDER will not assign a coordiante. This will be done later, when the mission is assigned to an airwing. + return nil + + else + self:T(self.lid.."ERROR: Cannot get target coordinate!") + end + + return nil +end + +--- Get heading of target. +-- @param #AUFTRAG self +-- @return #number Heading of target in degrees. +function AUFTRAG:GetTargetHeading() + if self.engageTarget then + local heading=self.engageTarget:GetHeading() + return heading + end + return nil +end + +--- Get name of the target. +-- @param #AUFTRAG self +-- @return #string Name of the target or "N/A". +function AUFTRAG:GetTargetName() + + if self.engageTarget then + local name=self.engageTarget:GetName() + return name + end + + return "N/A" +end + + +--- Get distance to target. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE FromCoord The coordinate from which the distance is measured. +-- @return #number Distance in meters or 0. +function AUFTRAG:GetTargetDistance(FromCoord) + + local TargetCoord=self:GetTargetCoordinate() + + if TargetCoord and FromCoord then + return TargetCoord:Get2DDistance(FromCoord) + else + self:T(self.lid.."ERROR: TargetCoord or FromCoord does not exist in AUFTRAG:GetTargetDistance() function! Returning 0") + end + + return 0 +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Misc Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Add asset to mission. +-- @param #AUFTRAG self +-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be added to the mission. +-- @return #AUFTRAG self +function AUFTRAG:AddAsset(Asset) + + -- Debug info + self:T(self.lid..string.format("Adding asset \"%s\" to mission", tostring(Asset.spawngroupname))) + + -- Add to table. + self.assets=self.assets or {} + + -- Add to table. + table.insert(self.assets, Asset) + + self.Nassigned=self.Nassigned or 0 + + self.Nassigned=self.Nassigned+1 + + return self +end + +--- Add assets to mission. +-- @param #AUFTRAG self +-- @param #table Assets List of assets. +-- @return #AUFTRAG self +function AUFTRAG:_AddAssets(Assets) + + for _,asset in pairs(Assets) do + self:AddAsset(asset) + end + + return self +end + +--- Delete asset from mission. +-- @param #AUFTRAG self +-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be removed. +-- @return #AUFTRAG self +function AUFTRAG:DelAsset(Asset) + + for i,_asset in pairs(self.assets or {}) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + + if asset.uid==Asset.uid then + self:T(self.lid..string.format("Removing asset \"%s\" from mission", tostring(Asset.spawngroupname))) + table.remove(self.assets, i) + return self + end + + end + + return self +end + +--- Get asset by its spawn group name. +-- @param #AUFTRAG self +-- @param #string Name Asset spawn group name. +-- @return Functional.Warehouse#WAREHOUSE.Assetitem Asset. +function AUFTRAG:GetAssetByName(Name) + + for i,_asset in pairs(self.assets or {}) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + + if asset.spawngroupname==Name then + return asset + end + + end + + return nil +end + +--- Count alive OPS groups assigned for this mission. +-- @param #AUFTRAG self +-- @return #number Number of alive OPS groups. +function AUFTRAG:CountOpsGroups() + local N=0 + for _,_groupdata in pairs(self.groupdata) do + local groupdata=_groupdata --#AUFTRAG.GroupData + if groupdata and groupdata.opsgroup and groupdata.opsgroup:IsAlive() and not groupdata.opsgroup:IsDead() then + N=N+1 + end + end + return N +end + +--- Count OPS groups in a certain status. +-- @param #AUFTRAG self +-- @param #string Status Status of group, e.g. `AUFTRAG.GroupStatus.EXECUTING`. +-- @return #number Number of alive OPS groups. +function AUFTRAG:CountOpsGroupsInStatus(Status) + local N=0 + for _,_groupdata in pairs(self.groupdata) do + local groupdata=_groupdata --#AUFTRAG.GroupData + if groupdata and groupdata.status==Status then + N=N+1 + end + end + return N +end + + + +--- Get coordinate of target. First unit/group of the set is used. +-- @param #AUFTRAG self +-- @param #table MissionTypes A table of mission types. +-- @return #string Comma separated list of mission types. +function AUFTRAG:GetMissionTypesText(MissionTypes) + + local text="" + for _,missiontype in pairs(MissionTypes) do + text=text..string.format("%s, ", missiontype) + end + + return text +end + +--- Set the mission waypoint coordinate where the mission is executed. Note that altitude is set via `:SetMissionAltitude`. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Coordinate where the mission is executed. +-- @return #AUFTRAG self +function AUFTRAG:SetMissionWaypointCoord(Coordinate) + + -- Obviously a zone was passed. We get the coordinate. + if Coordinate:IsInstanceOf("ZONE_BASE") then + Coordinate=Coordinate:GetCoordinate() + end + + self.missionWaypointCoord=Coordinate + return self +end + +--- Set randomization of the mission waypoint coordinate. Each assigned group will get a random ingress coordinate, where the mission is executed. +-- @param #AUFTRAG self +-- @param #number Radius Distance in meters. Default `#nil`. +-- @return #AUFTRAG self +function AUFTRAG:SetMissionWaypointRandomization(Radius) + self.missionWaypointRadius=Radius + return self +end + +--- Set the mission egress coordinate. This is the coordinate where the assigned group will go once the mission is finished. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Egrees coordinate. +-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate. +-- @return #AUFTRAG self +function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude) + + -- Obviously a zone was passed. We get the coordinate. + if Coordinate:IsInstanceOf("ZONE_BASE") then + Coordinate=Coordinate:GetCoordinate() + end + + self.missionEgressCoord=Coordinate + + if Altitude then + self.missionEgressCoord.y=UTILS.FeetToMeters(Altitude) + end +end + +--- Get the mission egress coordinate if this was defined. +-- @param #AUFTRAG self +-- @return Core.Point#COORDINATE Coordinate Coordinate or nil. +function AUFTRAG:GetMissionEgressCoord() + return self.missionEgressCoord +end + +--- Get coordinate which was set as mission waypoint coordinate. +-- @param #AUFTRAG self +-- @return Core.Point#COORDINATE Coordinate where the mission is executed or `#nil`. +function AUFTRAG:_GetMissionWaypointCoordSet() + + -- Check if a coord has been explicitly set. + if self.missionWaypointCoord then + local coord=self.missionWaypointCoord + if self.missionAltitude then + coord.y=self.missionAltitude + end + + + return coord + end + +end + +--- Get coordinate of target. First unit/group of the set is used. +-- @param #AUFTRAG self +-- @param Wrapper.Group#GROUP group Group. +-- @param #number randomradius Random radius in meters. +-- @param #table surfacetypes Surface types of random zone. +-- @return Core.Point#COORDINATE Coordinate where the mission is executed. +function AUFTRAG:GetMissionWaypointCoord(group, randomradius, surfacetypes) + + -- Check if a coord has been explicitly set. + if self.missionWaypointCoord then + local coord=self.missionWaypointCoord + if self.missionAltitude then + coord.y=self.missionAltitude + end + return coord + end + + -- Create waypoint coordinate half way between us and the target. + local waypointcoord=group:GetCoordinate():GetIntermediateCoordinate(self:GetTargetCoordinate(), self.missionFraction) + local alt=waypointcoord.y + + -- Add some randomization. + if randomradius then + waypointcoord=ZONE_RADIUS:New("Temp", waypointcoord:GetVec2(), randomradius):GetRandomCoordinate(nil, nil, surfacetypes):SetAltitude(alt, false) + end + + -- Set altitude of mission waypoint. + if self.missionAltitude then + waypointcoord:SetAltitude(self.missionAltitude, true) + end + + return waypointcoord +end + + +--- Set log ID string. +-- @param #AUFTRAG self +-- @return #AUFTRAG self +function AUFTRAG:_SetLogID() + self.lid=string.format("Auftrag #%d %s | ", self.auftragsnummer, tostring(self.type)) + return self +end + + +--- Update DCS task. +-- @param #AUFTRAG self +-- @return #AUFTRAG self +function AUFTRAG:_UpdateTask() + + + + return self +end + +--- Update mission F10 map marker. +-- @param #AUFTRAG self +-- @return #AUFTRAG self +function AUFTRAG:UpdateMarker() + + -- Marker text. + local text=string.format("%s %s: %s", self.name, self.type:upper(), self.status:upper()) + text=text..string.format("\n%s", self:GetTargetName()) + text=text..string.format("\nTargets %d/%d, Life Points=%d/%d", self:CountMissionTargets(), self:GetTargetInitialNumber(), self:GetTargetLife(), self:GetTargetInitialLife()) + text=text..string.format("\nOpsGroups %d/%d", self:CountOpsGroups(), self:GetNumberOfRequiredAssets()) + + if not self.marker then + + -- Get target coordinates. Can be nil! + local targetcoord=self:GetTargetCoordinate() + + if self.markerCoaliton and self.markerCoaliton>=0 then + self.marker=MARKER:New(targetcoord, text):ReadOnly():ToCoalition(self.markerCoaliton) + else + self.marker=MARKER:New(targetcoord, text):ReadOnly():ToAll() + end + + else + + if self.marker:GetText()~=text then + self.marker:UpdateText(text) + end + + end + + return self +end + +--- Get DCS task table for the given mission. +-- @param #AUFTRAG self +-- @return DCS#Task The DCS task table. If multiple tasks are necessary, this is returned as a combo task. +function AUFTRAG:GetDCSMissionTask() + + local DCStasks={} + + -- Create DCS task based on current self. + if self.type==AUFTRAG.Type.ANTISHIP then + + ---------------------- + -- ANTISHIP Mission -- + ---------------------- + + -- Add enroute anti-ship task. + local DCStask=CONTROLLABLE.EnRouteTaskAntiShip(nil) + table.insert(self.enrouteTasks, DCStask) + + self:_GetDCSAttackTask(self.engageTarget, DCStasks) + + elseif self.type==AUFTRAG.Type.AWACS then + + ------------------- + -- AWACS Mission -- + ------------------- + + local DCStask=CONTROLLABLE.EnRouteTaskAWACS(nil) + + table.insert(self.enrouteTasks, DCStask) + + elseif self.type==AUFTRAG.Type.BAI then + + ----------------- + -- BAI Mission -- + ----------------- + + self:_GetDCSAttackTask(self.engageTarget, DCStasks) + + elseif self.type==AUFTRAG.Type.BOMBING then + + --------------------- + -- BOMBING Mission -- + --------------------- + + local DCStask=CONTROLLABLE.TaskBombing(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, Divebomb) + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.BOMBRUNWAY then + + ------------------------ + -- BOMBRUNWAY Mission -- + ------------------------ + + local DCStask=CONTROLLABLE.TaskBombingRunway(nil, self.engageTarget:GetObject(), self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAsGroup) + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.BOMBCARPET then + + ------------------------ + -- BOMBCARPET Mission -- + ------------------------ + + local DCStask=CONTROLLABLE.TaskCarpetBombing(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, self.engageCarpetLength) + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.CAP then + + ----------------- + -- CAP Mission -- + ----------------- + + local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, self.engageZone:GetVec2(), self.engageZone:GetRadius(), self.engageTargetTypes, Priority) + + table.insert(self.enrouteTasks, DCStask) + + elseif self.type==AUFTRAG.Type.CAS then + + ----------------- + -- CAS Mission -- + ----------------- + + local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, self.engageZone:GetVec2(), self.engageZone:GetRadius(), self.engageTargetTypes, Priority) + + table.insert(self.enrouteTasks, DCStask) + + elseif self.type==AUFTRAG.Type.ESCORT then + + -------------------- + -- ESCORT Mission -- + -------------------- + + local DCStask=CONTROLLABLE.TaskEscort(nil, self.engageTarget:GetObject(), self.escortVec3, LastWaypointIndex, self.engageMaxDistance, self.engageTargetTypes) + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.FACA then + + ----------------- + -- FAC Mission -- + ----------------- + + local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil, self.engageTarget:GetObject(), self.engageWeaponType, self.facDesignation, self.facDatalink, self.facFreq, self.facModu, CallsignName, CallsignNumber) + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.FERRY then + + ------------------- + -- FERRY Mission -- + ------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.FERRY + + -- We create a "fake" DCS task. + local param={} + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.RELOCATECOHORT then + + ---------------------- + -- RELOCATE Mission -- + ---------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.RELOCATECOHORT + + -- We create a "fake" DCS task. + local param={} + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.INTERCEPT then + + ----------------------- + -- INTERCEPT Mission -- + ----------------------- + + self:_GetDCSAttackTask(self.engageTarget, DCStasks) + + elseif self.type==AUFTRAG.Type.ORBIT then + + ------------------- + -- ORBIT Mission -- + ------------------- + + -- Done below as also other mission types use the orbit task. + + elseif self.type==AUFTRAG.Type.GCICAP then + + -------------------- + -- GCICAP Mission -- + -------------------- + + -- Done below as also other mission types use the orbit task. + + elseif self.type==AUFTRAG.Type.RECON then + + ------------------- + -- RECON Mission -- + ------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.RECON + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + param.target=self.engageTarget + param.altitude=self.missionAltitude + param.speed=self.missionSpeed + param.lastindex=nil + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.SEAD then + + ------------------ + -- SEAD Mission -- + ------------------ + + --[[ + local DCStask=CONTROLLABLE.EnRouteTaskEngageTargets(nil, nil ,{"Air Defence"} , 0) + table.insert(self.enrouteTasks, DCStask) + DCStask.key="SEAD" + ]] + + self:_GetDCSAttackTask(self.engageTarget, DCStasks) + + elseif self.type==AUFTRAG.Type.STRIKE then + + -------------------- + -- STRIKE Mission -- + -------------------- + + local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.TANKER or self.type==AUFTRAG.Type.RECOVERYTANKER then + + -------------------- + -- TANKER Mission -- + -------------------- + + local DCStask=CONTROLLABLE.EnRouteTaskTanker(nil) + + table.insert(self.enrouteTasks, DCStask) + + elseif self.type==AUFTRAG.Type.TROOPTRANSPORT then + + ---------------------------- + -- TROOPTRANSPORT Mission -- + ---------------------------- + + -- Task to embark the troops at the pick up point. + local TaskEmbark=CONTROLLABLE.TaskEmbarking(TaskControllable, self.transportPickup, self.transportGroupSet, self.transportWaitForCargo) + + -- Task to disembark the troops at the drop off point. + local TaskDisEmbark=CONTROLLABLE.TaskDisembarking(TaskControllable, self.transportDropoff, self.transportGroupSet) + + table.insert(DCStasks, TaskEmbark) + table.insert(DCStasks, TaskDisEmbark) + + elseif self.type==AUFTRAG.Type.OPSTRANSPORT then + + -------------------------- + -- OPSTRANSPORT Mission -- + -------------------------- + + local DCStask={} + + DCStask.id="OpsTransport" + + -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. + local param={} + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.CARGOTRANSPORT then + + ---------------------------- + -- CARGOTRANSPORT Mission -- + ---------------------------- + + -- Task to transport cargo. + local TaskCargoTransportation={ + id = "CargoTransportation", + params = {} + } + + table.insert(DCStasks, TaskCargoTransportation) + + elseif self.type==AUFTRAG.Type.RESCUEHELO then + + ------------------------- + -- RESCUE HELO Mission -- + ------------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.FORMATION + + -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. + local param={} + param.unitname=self:GetTargetName() + param.offsetX=200 + param.offsetZ=240 + param.altitude=70 + param.dtFollow=1.0 + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.ARTY then + + ------------------ + -- ARTY Mission -- + ------------------ + + + if self.artyShots==1 or self.artyRadius<10 or true then + + local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, self:GetTargetVec2(), self.artyRadius, self.artyShots, self.engageWeaponType, self.artyAltitude) + table.insert(DCStasks, DCStask) + + else + + local Vec2=self:GetTargetVec2() + + local zone=ZONE_RADIUS:New("temp", Vec2, self.artyRadius) + + for i=1,self.artyShots do + + local vec2=zone:GetRandomVec2() + + local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, vec2, 0, 1, self.engageWeaponType, self.artyAltitude) + table.insert(DCStasks, DCStask) + + end + + end + + elseif self.type==AUFTRAG.Type.BARRAGE then + + --------------------- + -- BARRAGE Mission -- + --------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.BARRAGE + + -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. + local param={} + param.zone=self:GetObjective() + param.altitude=self.artyAltitude + param.radius=self.artyRadius + param.heading=self.artyHeading + param.angle=self.artyAngle + param.shots=self.artyShots + param.weaponTypoe=self.engageWeaponType + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.PATROLZONE then + + ------------------------- + -- PATROL ZONE Mission -- + ------------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.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.CASENHANCED then + + ------------------------- + -- CAS ENHANCED Mission -- + ------------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.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.GROUNDATTACK then + + --------------------------- + -- GROUND ATTACK Mission -- + --------------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.GROUNDATTACK + + -- We create a "fake" DCS task and pass the parameters to the ARMYGROUP. + local param={} + param.target=self:GetTargetData() + param.action="Wedge" + param.speed=self.missionSpeed + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.AMMOSUPPLY then + + ------------------------- + -- AMMO SUPPLY Mission -- + ------------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.AMMOSUPPLY + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + param.zone=self:GetObjective() + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.FUELSUPPLY then + + ------------------------- + -- FUEL SUPPLY Mission -- + ------------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.FUELSUPPLY + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + param.zone=self:GetObjective() + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.AMMOSUPPLY then + + ---------------------- + -- REARMING Mission -- + ---------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.REARMING + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + param.zone=self:GetObjective() + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.ALERT5 then + + --------------------- + -- ALERT 5 Mission -- + --------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.ALERT5 + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.NOTHING then + + --------------------- + -- NOTHING Mission -- + --------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.NOTHING + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.HOVER then + + --------------------- + -- HOVER Mission -- + --------------------- + + local DCStask={} + DCStask.id=AUFTRAG.SpecialTask.HOVER + + local param={} + + param.hoverAltitude=self.hoverAltitude + param.hoverTime = self.hoverTime + param.missionSpeed = self.missionSpeed + param.missionAltitude = self.missionAltitude + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.ONGUARD or self.type==AUFTRAG.Type.ARMOREDGUARD then + + ---------------------- + -- ON GUARD Mission -- + ---------------------- + + local DCStask={} + + DCStask.id= self.type==AUFTRAG.Type.ONGUARD and AUFTRAG.SpecialTask.ONGUARD or AUFTRAG.SpecialTask.ARMOREDGUARD + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + param.coordinate=self:GetObjective() + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.AIRDEFENSE then + + ------------------------ + -- AIRDEFENSE Mission -- + ------------------------ + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.AIRDEFENSE + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + param.zone=self:GetObjective() + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.EWR then + + ----------------- + -- EWR Mission -- + ----------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.EWR + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + param.zone=self:GetObjective() + + DCStask.params=param + + table.insert(DCStasks, DCStask) + + -- EWR is an enroute task + local Enroutetask=CONTROLLABLE.EnRouteTaskEWR() + table.insert(self.enrouteTasks, Enroutetask) + + else + self:T(self.lid..string.format("ERROR: Unknown mission task!")) + return nil + end + + + -- Set ORBIT task. Also applies to other missions: AWACS, TANKER, CAP, CAS. + if self.type==AUFTRAG.Type.ORBIT or + self.type==AUFTRAG.Type.CAP or + self.type==AUFTRAG.Type.CAS or + self.type==AUFTRAG.Type.GCICAP or + self.type==AUFTRAG.Type.AWACS or + self.type==AUFTRAG.Type.TANKER or + self.type==AUFTRAG.Type.RECOVERYTANKER then + + ------------------- + -- ORBIT Mission -- + ------------------- + + -- Get/update orbit vector. + self.orbitVec2=self:GetTargetVec2() + + if self.orbitVec2 then + + -- Heading of the target. + self.targetHeading=self:GetTargetHeading() + + local OffsetVec2=nil --DCS#Vec2 + if (self.orbitOffsetVec2~=nil) then + OffsetVec2=UTILS.DeepCopy(self.orbitOffsetVec2) + end + + if OffsetVec2 then + + if self.orbitOffsetVec2.r then + -- Polar coordinates + local r=self.orbitOffsetVec2.r + local phi=(self.orbitOffsetVec2.phi or 0) + self.targetHeading + + OffsetVec2.x=r*math.cos(math.rad(phi)) + OffsetVec2.y=r*math.sin(math.rad(phi)) + else + -- Cartesian coordinates + OffsetVec2.x=self.orbitOffsetVec2.x + OffsetVec2.y=self.orbitOffsetVec2.y + end + + end + + -- Actual orbit position with possible offset. + local orbitVec2=OffsetVec2 and UTILS.Vec2Add(self.orbitVec2, OffsetVec2) or self.orbitVec2 + + -- Check for race-track pattern. + local orbitRaceTrack=nil --DCS#Vec2 + if self.orbitLeg then + + -- Default heading is due North. + local heading=0 + + -- Check if specific heading was specified. + if self.orbitHeading then + + -- Is heading realtive to target? + if self.orbitHeadingRel then + -- Relative heading wrt target. + heading=self.targetHeading+self.orbitHeading + else + -- Take given heading. + heading=self.orbitHeading + end + + else + -- Not specific heading specified ==> Take heading of target. + heading=self.targetHeading or 0 + end + + -- Race-track vector. + orbitRaceTrack=UTILS.Vec2Translate(orbitVec2, self.orbitLeg, heading) + end + + local orbitRaceTrackCoord = nil + if orbitRaceTrack then + orbitRaceTrackCoord = COORDINATE:NewFromVec2(orbitRaceTrack) + end + + -- Create orbit task. + local DCStask=CONTROLLABLE.TaskOrbit(nil, COORDINATE:NewFromVec2(orbitVec2), self.orbitAltitude, self.orbitSpeed, orbitRaceTrackCoord) + + -- Add DCS task. + table.insert(DCStasks, DCStask) + + end + + end + + -- Debug info. + self:T3({missiontask=DCStasks}) + + -- Return the task. + if #DCStasks==1 then + return DCStasks[1] + else + return CONTROLLABLE.TaskCombo(nil, DCStasks) + end + +end + +--- Get DCS task table for an attack group or unit task. +-- @param #AUFTRAG self +-- @param Ops.Target#TARGET Target Target data. +-- @param #table DCStasks DCS DCS tasks table to which the task is added. +-- @return DCS#Task The DCS task table. +function AUFTRAG:_GetDCSAttackTask(Target, DCStasks) + + DCStasks=DCStasks or {} + + for _,_target in pairs(Target.targets) do + local target=_target --Ops.Target#TARGET.Object + + if target.Type==TARGET.ObjectType.GROUP then + + local DCStask=CONTROLLABLE.TaskAttackGroup(nil, target.Object, self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageAsGroup) + + table.insert(DCStasks, DCStask) + + elseif target.Type==TARGET.ObjectType.UNIT or target.Type==TARGET.ObjectType.STATIC then + + local DCStask=CONTROLLABLE.TaskAttackUnit(nil, target.Object, self.engageAsGroup, self.WeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) + + table.insert(DCStasks, DCStask) + + end + + end + + return DCStasks +end + +--- Get DCS task table for an attack group or unit task. +-- @param #AUFTRAG self +-- @param #string MissionType Mission (AUFTAG) type. +-- @return #string DCS mission task for the auftrag type. +function AUFTRAG:GetMissionTaskforMissionType(MissionType) + + local mtask=ENUMS.MissionTask.NOTHING + + if MissionType==AUFTRAG.Type.ANTISHIP then + mtask=ENUMS.MissionTask.ANTISHIPSTRIKE + elseif MissionType==AUFTRAG.Type.AWACS then + mtask=ENUMS.MissionTask.AWACS + elseif MissionType==AUFTRAG.Type.BAI then + mtask=ENUMS.MissionTask.GROUNDATTACK + elseif MissionType==AUFTRAG.Type.BOMBCARPET then + mtask=ENUMS.MissionTask.GROUNDATTACK + elseif MissionType==AUFTRAG.Type.BOMBING then + mtask=ENUMS.MissionTask.GROUNDATTACK + elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then + mtask=ENUMS.MissionTask.RUNWAYATTACK + elseif MissionType==AUFTRAG.Type.CAP then + mtask=ENUMS.MissionTask.CAP + elseif MissionType==AUFTRAG.Type.GCICAP then + mtask=ENUMS.MissionTask.CAP + elseif MissionType==AUFTRAG.Type.CAS then + 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 + mtask=ENUMS.MissionTask.AFAC + elseif MissionType==AUFTRAG.Type.FERRY then + mtask=ENUMS.MissionTask.NOTHING + elseif MissionType==AUFTRAG.Type.INTERCEPT then + mtask=ENUMS.MissionTask.INTERCEPT + elseif MissionType==AUFTRAG.Type.RECON then + mtask=ENUMS.MissionTask.RECONNAISSANCE + elseif MissionType==AUFTRAG.Type.SEAD then + mtask=ENUMS.MissionTask.SEAD + elseif MissionType==AUFTRAG.Type.STRIKE then + mtask=ENUMS.MissionTask.GROUNDATTACK + elseif MissionType==AUFTRAG.Type.TANKER then + mtask=ENUMS.MissionTask.REFUELING + elseif MissionType==AUFTRAG.Type.TROOPTRANSPORT then + mtask=ENUMS.MissionTask.TRANSPORT + elseif MissionType==AUFTRAG.Type.CARGOTRANSPORT then + mtask=ENUMS.MissionTask.TRANSPORT + elseif MissionType==AUFTRAG.Type.ARMORATTACK then + mtask=ENUMS.MissionTask.NOTHING + elseif MissionType==AUFTRAG.Type.HOVER then + mtask=ENUMS.MissionTask.NOTHING + end + + return mtask +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Global Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Checks if a mission type is contained in a table of possible types. +-- @param #string MissionType The requested mission type. +-- @param #table PossibleTypes A table with possible mission types. +-- @return #boolean If true, the requested mission type is part of the possible mission types. +function AUFTRAG.CheckMissionType(MissionType, PossibleTypes) + + if type(PossibleTypes)=="string" then + PossibleTypes={PossibleTypes} + end + + for _,canmission in pairs(PossibleTypes) do + if canmission==MissionType then + return true + end + end + + return false +end + +--- Check if a mission type is contained in a list of possible capabilities. +-- @param #table MissionTypes The requested mission type. Can also be passed as a single mission type `#string`. +-- @param #table Capabilities A table with possible capabilities `Ops.Auftrag#AUFTRAG.Capability`. +-- @param #boolean All If `true`, given mission type must be includedin ALL capabilities. If `false` or `nil`, it must only match one. +-- @return #boolean If true, the requested mission type is part of the possible mission types. +function AUFTRAG.CheckMissionCapability(MissionTypes, Capabilities, All) + + -- Ensure table. + if type(MissionTypes)~="table" then + MissionTypes={MissionTypes} + end + + for _,cap in pairs(Capabilities) do + local capability=cap --Ops.Auftrag#AUFTRAG.Capability + for _,MissionType in pairs(MissionTypes) do + if All==true then + if capability.MissionType~=MissionType then + return false + end + else + if capability.MissionType==MissionType then + return true + end + end + end + end + + if All==true then + return true + else + return false + end +end + + +--- Check if a mission type is contained in a list of possible capabilities. +-- @param #table MissionTypes The requested mission type. Can also be passed as a single mission type `#string`. +-- @param #table Capabilities A table with possible capabilities `Ops.Auftrag#AUFTRAG.Capability`. +-- @return #boolean If true, the requested mission type is part of the possible mission types. +function AUFTRAG.CheckMissionCapabilityAny(MissionTypes, Capabilities) + + local res=AUFTRAG.CheckMissionCapability(MissionTypes, Capabilities, false) + + return res +end + + +--- Check if a mission type is contained in a list of possible capabilities. +-- @param #table MissionTypes The requested mission type. Can also be passed as a single mission type `#string`. +-- @param #table Capabilities A table with possible capabilities `Ops.Auftrag#AUFTRAG.Capability`. +-- @return #boolean If true, the requested mission type is part of the possible mission types. +function AUFTRAG.CheckMissionCapabilityAll(MissionTypes, Capabilities) + + local res=AUFTRAG.CheckMissionCapability(MissionTypes, Capabilities, true) + + return res +end + + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + -- -- ### Author: **funkyfranky** -- From 86ea757a168e8ff16d0e330829b8d2c95909e212 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 25 Dec 2022 14:19:27 +0100 Subject: [PATCH 157/603] #ATIS * FARP support --- Moose Development/Moose/Ops/ATIS.lua | 169 +++++++++++++++------------ 1 file changed, 93 insertions(+), 76 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index d4c20f948..e65a2ac71 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -94,6 +94,7 @@ -- @field #boolean ReportmBar Report mBar/hpa even if not metric, i.e. for Mirage flights -- @field #boolean TransmitOnlyWithPlayers For SRS - If true, only transmit if there are alive Players. -- @field #string SRSText Text of the complete SRS message (if done at least once, else nil) +-- @field #boolean ATISforFARPs Will be set to true if the base given is a FARP/Helipad -- @extends Core.Fsm#FSM --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -309,6 +310,19 @@ -- atis:Start() -- -- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Not that backslashes need to be escaped or simply use slashes (as in linux). +-- +-- ## FARPS +-- +-- ATIS is working with FARPS, but this requires the usage of SRS. The airbase name for the `New()-method` is the UNIT name of the FARP: +-- +-- atis = ATIS:New("FARP Gold",119,radio.modulation.AM) +-- atis:SetMetricUnits() +-- atis:SetTransmitOnlyWithPlayers(true) +-- atis:SetReportmBar(true) +-- atis:SetTowerFrequencies(127.50) +-- atis:SetSRS("D:\\DCS\\_SRS\\", "male", "en-US",nil,5002) +-- atis:SetAdditionalInformation("Welcome to the Jungle!") +-- atis:__Start(3) -- -- @field #ATIS ATIS = { @@ -351,6 +365,7 @@ ATIS = { relHumidity = nil, ReportmBar = false, TransmitOnlyWithPlayers = false, + ATISforFARPs = false, } --- NATO alphabet. @@ -593,7 +608,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.9.12" +ATIS.version = "0.9.14" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1049,7 +1064,7 @@ end -- -- * 186° on the Caucaus map -- * 192° on the Nevada map --- * 170° on the Normany map +-- * 170° on the Normandy map -- * 182° on the Persian Gulf map -- -- Likewise, to convert *true* into *magnetic* heading, one has to substract easterly and add westerly variation. @@ -1257,11 +1272,18 @@ end function ATIS:onafterStart( From, Event, To ) -- Check that this is an airdrome. - if self.airbase:GetAirbaseCategory() ~= Airbase.Category.AIRDROME then - self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT FARPS or SHIPS.", self.airbasename ) ) + if self.airbase:GetAirbaseCategory() == Airbase.Category.SHIP then + self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT SHIPS.", self.airbasename ) ) return end - + + -- Check that if is a Helipad. + if self.airbase:GetAirbaseCategory() == Airbase.Category.HELIPAD then + self:E( self.lid .. string.format( "EXPERIMENTAL: Starting ATIS for Helipad %s! SRS must be ON", self.airbasename ) ) + self.ATISforFARPs = true + self.useSRS = true + end + -- Info. self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) ) @@ -1473,10 +1495,19 @@ function ATIS:onafterBroadcast( From, Event, To ) -------------- --- Runway --- -------------- - - local runwayLanding, rwyLandingLeft=self:GetActiveRunway() - local runwayTakeoff, rwyTakeoffLeft=self:GetActiveRunway(true) - + + + local runwayLanding, rwyLandingLeft + local runwayTakeoff, rwyTakeoffLeft + + if self.airbase:GetAirbaseCategory() == Airbase.Category.HELIPAD then + runwayLanding, rwyLandingLeft="PAD 01",false + runwayTakeoff, rwyTakeoffLeft="PAD 02",false + else + runwayLanding, rwyLandingLeft=self:GetActiveRunway() + runwayTakeoff, rwyTakeoffLeft=self:GetActiveRunway(true) + end + ------------ --- Time --- ------------ @@ -1790,7 +1821,7 @@ function ATIS:onafterBroadcast( From, Event, To ) -- Airbase name subtitle = string.format( "%s", self.airbasename ) - if self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil then + if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil then subtitle = subtitle .. " Airport" end if not self.useSRS then @@ -1865,8 +1896,6 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle - --self:I("Line 1811") - --self:I(alltext) -- Visibility if self.metric then @@ -1884,8 +1913,6 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle - --self:I("Line 1830") - --self:I(alltext) subtitle = "" -- Weather phenomena @@ -1987,10 +2014,8 @@ function ATIS:onafterBroadcast( From, Event, To ) end end end - --self:I("Line 1932") alltext = alltext .. ";\n" .. subtitle - --self:I(alltext) subtitle = "" -- Temperature if self.TDegF then @@ -2019,9 +2044,7 @@ function ATIS:onafterBroadcast( From, Event, To ) self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 ) end end - --self:I("Line 1962") alltext = alltext .. ";\n" .. subtitle - --self:I(alltext) -- Dew point if self.TDegF then @@ -2050,8 +2073,6 @@ function ATIS:onafterBroadcast( From, Event, To ) self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 ) end end - --self:I("Line 1992") - --self:I(alltext) alltext = alltext .. ";\n" .. subtitle -- Altimeter QNH/QFE. @@ -2117,69 +2138,68 @@ function ATIS:onafterBroadcast( From, Event, To ) end end end - --self:I("Line 2049") - --self:I(alltext) alltext = alltext .. ";\n" .. subtitle - -- Active runway. - local subtitle=string.format("Active runway %s", runwayLanding) - if rwyLandingLeft==true then - subtitle=subtitle.." Left" - elseif rwyLandingLeft==false then - subtitle=subtitle.." Right" - end - local _RUNACT = subtitle - if not self.useSRS then - self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle) - self.radioqueue:Number2Transmission(runwayLanding) + if not self.ATISforFARPs then + -- Active runway. + local subtitle=string.format("Active runway %s", runwayLanding) if rwyLandingLeft==true then - self:Transmission(ATIS.Sound.Left, 0.2) + subtitle=subtitle.." Left" elseif rwyLandingLeft==false then - self:Transmission(ATIS.Sound.Right, 0.2) + subtitle=subtitle.." Right" end - end - alltext = alltext .. ";\n" .. subtitle - - -- Runway length. - if self.rwylength then - - local runact = self.airbase:GetActiveRunway( self.runwaym2t ) - local length = runact.length - if not self.metric then - length = UTILS.MetersToFeet( length ) - end - - -- Length in thousands and hundrets of ft/meters. - local L1000, L0100 = self:_GetThousandsAndHundreds( length ) - - -- Subtitle. - local subtitle = string.format( "Runway length %d", length ) - if self.metric then - subtitle = subtitle .. " meters" - else - subtitle = subtitle .. " feet" - end - - -- Transmit. + local _RUNACT = subtitle if not self.useSRS then - self:Transmission( ATIS.Sound.RunwayLength, 1.0, subtitle ) - if tonumber( L1000 ) > 0 then - self.radioqueue:Number2Transmission( L1000 ) - self:Transmission( ATIS.Sound.Thousand, 0.1 ) - end - if tonumber( L0100 ) > 0 then - self.radioqueue:Number2Transmission( L0100 ) - self:Transmission( ATIS.Sound.Hundred, 0.1 ) - end - if self.metric then - self:Transmission( ATIS.Sound.Meters, 0.1 ) - else - self:Transmission( ATIS.Sound.Feet, 0.1 ) + self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle) + self.radioqueue:Number2Transmission(runwayLanding) + if rwyLandingLeft==true then + self:Transmission(ATIS.Sound.Left, 0.2) + elseif rwyLandingLeft==false then + self:Transmission(ATIS.Sound.Right, 0.2) end end alltext = alltext .. ";\n" .. subtitle + + -- Runway length. + if self.rwylength then + + local runact = self.airbase:GetActiveRunway( self.runwaym2t ) + local length = runact.length + if not self.metric then + length = UTILS.MetersToFeet( length ) + end + + -- Length in thousands and hundrets of ft/meters. + local L1000, L0100 = self:_GetThousandsAndHundreds( length ) + + -- Subtitle. + local subtitle = string.format( "Runway length %d", length ) + if self.metric then + subtitle = subtitle .. " meters" + else + subtitle = subtitle .. " feet" + end + + -- Transmit. + if not self.useSRS then + self:Transmission( ATIS.Sound.RunwayLength, 1.0, subtitle ) + if tonumber( L1000 ) > 0 then + self.radioqueue:Number2Transmission( L1000 ) + self:Transmission( ATIS.Sound.Thousand, 0.1 ) + end + if tonumber( L0100 ) > 0 then + self.radioqueue:Number2Transmission( L0100 ) + self:Transmission( ATIS.Sound.Hundred, 0.1 ) + end + if self.metric then + self:Transmission( ATIS.Sound.Meters, 0.1 ) + else + self:Transmission( ATIS.Sound.Feet, 0.1 ) + end + end + alltext = alltext .. ";\n" .. subtitle + end end - -- Airfield elevation if self.elevation then @@ -2246,9 +2266,7 @@ function ATIS:onafterBroadcast( From, Event, To ) end -- ILS - --self:I({ils=self.ils}) local ils=self:GetNavPoint(self.ils, runwayLanding, rwyLandingLeft) - --self:I({ils=ils,runwayLanding=runwayLanding, rwyLandingLeft=rwyLandingLeft}) if ils then subtitle = string.format( "ILS frequency %.2f MHz", ils.frequency ) if not self.useSRS then @@ -2263,7 +2281,6 @@ function ATIS:onafterBroadcast( From, Event, To ) self:Transmission( ATIS.Sound.MegaHertz, 0.2 ) end alltext = alltext .. ";\n" .. subtitle - --self:I(alltext) end -- Outer NDB From 340c5abdb52f74623f017ff3eab766dc267b324d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 28 Dec 2022 15:47:44 +0100 Subject: [PATCH 158/603] #UTILS --- Moose Development/Moose/Core/Point.lua | 1 - Moose Development/Moose/Utilities/Utils.lua | 243 +++++++++++++++++--- 2 files changed, 212 insertions(+), 32 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 58e81c161..e567b8ae1 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1911,7 +1911,6 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #string name (Optional) Name of the fire to stop it, if not using the same COORDINATE object. function COORDINATE:StopBigSmokeAndFire( name ) - self:F2( { name = name } ) name = name or self.firename trigger.action.effectSmokeStop( name ) end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 0e3b3d37d..5889b8dd9 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -2177,10 +2177,29 @@ function UTILS.CheckFileExists(Path,Filename) end end +--- Function to obtain a table of typenames from the group given with the number of units of the same type in the group. +-- @param Wrapper.Group#GROUP Group The group to list +-- @return #table Table of typnames and typename counts, e.g. `{["KAMAZ Truck"]=3,["ATZ-5"]=1}` +function UTILS.GetCountPerTypeName(Group) + local units = Group:GetUnits() + local TypeNameTable = {} + for _,_unt in pairs (units) do + local unit = _unt -- Wrapper.Unit#UNIT + local typen = unit:GetTypeName() + if not TypeNameTable[typen] then + TypeNameTable[typen] = 1 + else + TypeNameTable[typen] = TypeNameTable[typen] + 1 + end + end + return TypeNameTable +end + --- Function to save the state of a list of groups found by name -- @param #table List Table of strings with groupnames -- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems. -- @param #string Filename The name of the file. +-- @param #boolean Structured Append the data with a list of typenames in the group plus their count. -- @return #boolean outcome True if saving is successful, else false. -- @usage -- We will go through the list and find the corresponding group and save the current group size (0 when dead). @@ -2188,7 +2207,7 @@ end -- Position is still saved for your usage. -- The idea is to reduce the number of units when reloading the data again to restart the saved mission. -- The data will be a simple comma separated list of groupname and size, with one header line. -function UTILS.SaveStationaryListOfGroups(List,Path,Filename) +function UTILS.SaveStationaryListOfGroups(List,Path,Filename,Structured) local filename = Filename or "StateListofGroups" local data = "--Save Stationary List of Groups: "..Filename .."\n" for _,_group in pairs (List) do @@ -2196,7 +2215,16 @@ function UTILS.SaveStationaryListOfGroups(List,Path,Filename) if group and group:IsAlive() then local units = group:CountAliveUnits() local position = group:GetVec3() - data = string.format("%s%s,%d,%d,%d,%d\n",data,_group,units,position.x,position.y,position.z) + if Structured then + local structure = UTILS.GetCountPerTypeName(group) + local strucdata = "" + for typen,anzahl in pairs (structure) do + strucdata = strucdata .. typen .. "=="..anzahl..";" + end + data = string.format("%s%s,%d,%d,%d,%d,%s\n",data,_group,units,position.x,position.y,position.z,strucdata) + else + data = string.format("%s%s,%d,%d,%d,%d\n",data,_group,units,position.x,position.y,position.z) + end else data = string.format("%s%s,0,0,0,0\n",data,_group) end @@ -2210,6 +2238,7 @@ end -- @param Core.Set#SET_BASE Set of objects to save -- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems. -- @param #string Filename The name of the file. +-- @param #boolean Structured Append the data with a list of typenames in the group plus their count. -- @return #boolean outcome True if saving is successful, else false. -- @usage -- We will go through the set and find the corresponding group and save the current group size and current position. @@ -2219,7 +2248,7 @@ end -- **Note** Do NOT use dashes or hashes in group template names (-,#)! -- The data will be a simple comma separated list of groupname and size, with one header line. -- The current task/waypoint/etc cannot be restored. -function UTILS.SaveSetOfGroups(Set,Path,Filename) +function UTILS.SaveSetOfGroups(Set,Path,Filename,Structured) local filename = Filename or "SetOfGroups" local data = "--Save SET of groups: "..Filename .."\n" local List = Set:GetSetObjects() @@ -2233,7 +2262,16 @@ function UTILS.SaveSetOfGroups(Set,Path,Filename) end local units = group:CountAliveUnits() local position = group:GetVec3() - data = string.format("%s%s,%s,%d,%d,%d,%d\n",data,name,template,units,position.x,position.y,position.z) + if Structured then + local structure = UTILS.GetCountPerTypeName(group) + local strucdata = "" + for typen,anzahl in pairs (structure) do + strucdata = strucdata .. typen .. "=="..anzahl..";" + end + data = string.format("%s%s,%s,%d,%d,%d,%d,%s\n",data,name,template,units,position.x,position.y,position.z,strucdata) + else + data = string.format("%s%s,%s,%d,%d,%d,%d\n",data,name,template,units,position.x,position.y,position.z) + end end end -- save the data @@ -2297,8 +2335,41 @@ end -- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems. -- @param #string Filename The name of the file. -- @param #boolean Reduce If false, existing loaded groups will not be reduced to fit the saved number. +-- @param #boolean Structured (Optional, needs Reduce = true) If true, and the data has been saved as structure before, remove the correct unit types as per the saved list. +-- @param #boolean Cinematic (Optional, needs Structured = true) If true, place a fire/smoke effect on the dead static position. +-- @param #number Effect (Optional for Cinematic) What effect to use. Defaults to a random effect. Smoke presets are: 1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke. +-- @param #number Density (Optional for Cinematic) What smoke density to use, can be 0 to 1. Defaults to 0.5. -- @return #table Table of data objects (tables) containing groupname, coordinate and group object. Returns nil when file cannot be read. -function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce) +-- @return #table When using Cinematic: table of names of smoke and fire objects, so they can be extinguished with `COORDINATE.StopBigSmokeAndFire( name )` +function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce,Structured,Cinematic,Effect,Density) + + local fires = {} + + local function Smokers(name,coord,effect,density) + local eff = math.random(8) + if type(effect) == "number" then eff = effect end + coord:BigSmokeAndFire(eff,density,name) + table.insert(fires,name) + end + + local function Cruncher(group,typename,anzahl) + local units = group:GetUnits() + local reduced = 0 + for _,_unit in pairs (units) do + local typo = _unit:GetTypeName() + if typename == typo then + if Cinematic then + local coordinate = _unit:GetCoordinate() + local name = _unit:GetName() + Smokers(name,coordinate,Effect,Density) + end + _unit:Destroy(false) + reduced = reduced + 1 + if reduced == anzahl then break end + end + end + end + local reduce = true if Reduce == false then reduce = false end local filename = Filename or "StateListofGroups" @@ -2315,18 +2386,48 @@ function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce) local posx = tonumber(dataset[3]) local posy = tonumber(dataset[4]) local posz = tonumber(dataset[5]) + local structure = dataset[6] + --BASE:I({structure}) local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz}) local data = { groupname=groupname, size=size, coordinate=coordinate, group=GROUP:FindByName(groupname) } if reduce then local actualgroup = GROUP:FindByName(groupname) if actualgroup and actualgroup:IsAlive() and actualgroup:CountAliveUnits() > size then - local reduction = actualgroup:CountAliveUnits() - size - BASE:I("Reducing groupsize by ".. reduction .. " units!") - -- reduce existing group - local units = actualgroup:GetUnits() - local units2 = UTILS.ShuffleTable(units) -- randomize table - for i=1,reduction do - units2[i]:Destroy(false) + if Structured and structure then + --BASE:I("Reducing group structure!") + local loadedstructure = {} + local strcset = UTILS.Split(structure,";") + for _,_data in pairs(strcset) do + local datasplit = UTILS.Split(_data,"==") + loadedstructure[datasplit[1]] = tonumber(datasplit[2]) + end + --BASE:I({loadedstructure}) + local originalstructure = UTILS.GetCountPerTypeName(actualgroup) + --BASE:I({originalstructure}) + for _name,_number in pairs(originalstructure) do + local loadednumber = 0 + if loadedstructure[_name] then + loadednumber = loadedstructure[_name] + end + local reduce = false + if loadednumber < _number then reduce = true end + + --BASE:I(string.format("Looking at: %s | Original number: %d | Loaded number: %d | Reduce: %s",_name,_number,loadednumber,tostring(reduce))) + + if reduce then + Cruncher(actualgroup,_name,_number-loadednumber) + end + + end + else + local reduction = actualgroup:CountAliveUnits() - size + --BASE:I("Reducing groupsize by ".. reduction .. " units!") + -- reduce existing group + local units = actualgroup:GetUnits() + local units2 = UTILS.ShuffleTable(units) -- randomize table + for i=1,reduction do + units2[i]:Destroy(false) + end end end end @@ -2335,19 +2436,52 @@ function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce) else return nil end - return datatable + return datatable,fires end --- Load back a SET of groups from file. -- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems. -- @param #string Filename The name of the file. -- @param #boolean Spawn If set to false, do not re-spawn the groups loaded in location and reduce to size. +-- @param #boolean Structured (Optional, needs Spawn=true)If true, and the data has been saved as structure before, remove the correct unit types as per the saved list. +-- @param #boolean Cinematic (Optional, needs Structured=true) If true, place a fire/smoke effect on the dead static position. +-- @param #number Effect (Optional for Cinematic) What effect to use. Defaults to a random effect. Smoke presets are: 1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke. +-- @param #number Density (Optional for Cinematic) What smoke density to use, can be 0 to 1. Defaults to 0.5. -- @return Core.Set#SET_GROUP Set of GROUP objects. -- Returns nil when file cannot be read. Returns a table of data entries if Spawn is false: `{ groupname=groupname, size=size, coordinate=coordinate, template=template }` -function UTILS.LoadSetOfGroups(Path,Filename,Spawn) +-- @return #table When using Cinematic: table of names of smoke and fire objects, so they can be extinguished with `COORDINATE.StopBigSmokeAndFire( name )` +function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,Density) + + local fires = {} + + local function Smokers(name,coord,effect,density) + local eff = math.random(8) + if type(effect) == "number" then eff = effect end + coord:BigSmokeAndFire(eff,density,name) + table.insert(fires,name) + end + + local function Cruncher(group,typename,anzahl) + local units = group:GetUnits() + local reduced = 0 + for _,_unit in pairs (units) do + local typo = _unit:GetTypeName() + if typename == typo then + if Cinematic then + local coordinate = _unit:GetCoordinate() + local name = _unit:GetName() + Smokers(name,coordinate,Effect,Density) + end + _unit:Destroy(false) + reduced = reduced + 1 + if reduced == anzahl then break end + end + end + end + local spawn = true if Spawn == false then spawn = false end - BASE:I("Spawn = "..tostring(spawn)) + --BASE:I("Spawn = "..tostring(spawn)) local filename = Filename or "SetOfGroups" local setdata = SET_GROUP:New() local datatable = {} @@ -2364,6 +2498,7 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn) local posx = tonumber(dataset[4]) local posy = tonumber(dataset[5]) local posz = tonumber(dataset[6]) + local structure = dataset[7] local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz}) local group=nil local data = { groupname=groupname, size=size, coordinate=coordinate, template=template } @@ -2376,12 +2511,40 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn) setdata:AddObject(spwndgrp) local actualsize = spwndgrp:CountAliveUnits() if actualsize > size then - local reduction = actualsize-size - -- reduce existing group - local units = spwndgrp:GetUnits() - local units2 = UTILS.ShuffleTable(units) -- randomize table - for i=1,reduction do - units2[i]:Destroy(false) + if Structured and structure then + --BASE:I("Reducing group structure!") + local loadedstructure = {} + local strcset = UTILS.Split(structure,";") + for _,_data in pairs(strcset) do + local datasplit = UTILS.Split(_data,"==") + loadedstructure[datasplit[1]] = tonumber(datasplit[2]) + end + --BASE:I({loadedstructure}) + local originalstructure = UTILS.GetCountPerTypeName(spwndgrp) + --BASE:I({originalstructure}) + for _name,_number in pairs(originalstructure) do + local loadednumber = 0 + if loadedstructure[_name] then + loadednumber = loadedstructure[_name] + end + local reduce = false + if loadednumber < _number then reduce = true end + + --BASE:I(string.format("Looking at: %s | Original number: %d | Loaded number: %d | Reduce: %s",_name,_number,loadednumber,tostring(reduce))) + + if reduce then + Cruncher(spwndgrp,_name,_number-loadednumber) + end + + end + else + local reduction = actualsize-size + -- reduce existing group + local units = spwndgrp:GetUnits() + local units2 = UTILS.ShuffleTable(units) -- randomize table + for i=1,reduction do + units2[i]:Destroy(false) + end end end end @@ -2393,7 +2556,7 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn) return nil end if spawn then - return setdata + return setdata,fires else return datatable end @@ -2412,12 +2575,7 @@ function UTILS.LoadSetOfStatics(Path,Filename) table.remove(loadeddata, 1) for _id,_entry in pairs (loadeddata) do local dataset = UTILS.Split(_entry,",") - -- staticname,position.x,position.y,position.z local staticname = dataset[1] - --local posx = tonumber(dataset[2]) - --local posy = tonumber(dataset[3]) - --local posz = tonumber(dataset[4]) - --local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz}) local StaticObject = STATIC:FindByName(staticname,false) if StaticObject then datatable:AddObject(StaticObject) @@ -2433,9 +2591,15 @@ end -- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems. -- @param #string Filename The name of the file. -- @param #boolean Reduce If false, do not destroy the units with size=0. --- @return #table Table of data objects (tables) containing staticname, size (0=dead else 1), coordinate and the static object. +-- @param #boolean Dead (Optional, needs Reduce = true) If Dead is true, re-spawn the dead object as dead and do not just delete it. +-- @param #boolean Cinematic (Optional, needs Dead = true) If true, place a fire/smoke effect on the dead static position. +-- @param #number Effect (Optional for Cinematic) What effect to use. Defaults to a random effect. Smoke presets are: 1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke. +-- @param #number Density (Optional for Cinematic) What smoke density to use, can be 0 to 1. Defaults to 0.5. +-- @return #table Table of data objects (tables) containing staticname, size (0=dead else 1), coordinate and the static object. Dead objects will have coordinate points `{x=0,y=0,z=0}` +-- @return #table When using Cinematic: table of names of smoke and fire objects, so they can be extinguished with `COORDINATE.StopBigSmokeAndFire( name )` -- Returns nil when file cannot be read. -function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce) +function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce,Dead,Cinematic,Effect,Density) + local fires = {} local reduce = true if Reduce == false then reduce = false end local filename = Filename or "StateListofStatics" @@ -2458,14 +2622,31 @@ function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce) if size==0 and reduce then local static = STATIC:FindByName(staticname,false) if static then - static:Destroy(false) + if Dead then + local deadobject = SPAWNSTATIC:NewFromStatic(staticname,static:GetCountry()) + deadobject:InitDead(true) + local heading = static:GetHeading() + local coord = static:GetCoordinate() + static:Destroy(false) + deadobject:SpawnFromCoordinate(coord,heading,staticname) + if Cinematic then + local effect = math.random(8) + if type(Effect) == "number" then + effect = Effect + end + coord:BigSmokeAndFire(effect,Density,staticname) + table.insert(fires,staticname) + end + else + static:Destroy(false) + end end end end else return nil end - return datatable + return datatable,fires end --- Heading Degrees (0-360) to Cardinal From 193f83fcc3e38eb590c91c55b14dd4f381aa3997 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 29 Dec 2022 11:57:18 +0100 Subject: [PATCH 159/603] #AWACS * Added option for helos --- Moose Development/Moose/Ops/Awacs.lua | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index a9fd58387..cf6dfe67f 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -112,6 +112,7 @@ do -- @field #boolean GCI Act as GCI -- @field Wrapper.Group#GROUP GCIGroup EWR group object for GCI ops -- @field #string locale Localization +-- @field #boolean IncludeHelicopters -- @extends Core.Fsm#FSM @@ -365,6 +366,7 @@ do -- testawacs.GoogleTTSPadding = 1 -- seconds -- testawacs.WindowsTTSPadding = 2.5 -- seconds -- testawacs.PikesSpecialSwitch = false -- if set to true, AWACS will omit the "doing xy knots" on the station assignement callout +-- testawacs.IncludeHelicopters = false -- if set to true, Helicopter pilots will also get the AWACS Menu and options -- -- ## 9.2 Bespoke random voices for AI CAP (Google TTS only) -- @@ -497,7 +499,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.50", -- #string + version = "0.2.51", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -584,6 +586,7 @@ AWACS = { GCI = false, GCIGroup = nil, locale = "en", + IncludeHelicopters = false, } --- @@ -914,13 +917,13 @@ AWACS.TaskStatus = { --@field #boolean FromAI ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO-List 0.2.50 +-- TODO-List 0.2.51 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- -- DONE - WIP - Player tasking, VID -- DONE - Localization (sensible?) -- TODO - (LOW) LotATC --- TODO - SW Optimization +-- DONE - SW Optimization -- WONTDO - Maybe check in AI only when airborne -- DONE - remove SSML tag when not on google (currently sometimes spoken) -- DONE - Maybe - Assign specific number of AI CAP to a station @@ -951,6 +954,7 @@ AWACS.TaskStatus = { -- DONE - Shift Length AWACS/AI -- DONE - (WIP) Reporting -- DONE - Do not report non-airborne groups +-- DONE - Added option for helos ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -5534,6 +5538,20 @@ end -- FSM Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- [Internal] onbeforeStart +-- @param #AWACS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #AWACS self +function AWACS:onbeforeStart(From,Event,to) + self:T({From, Event, To}) + if self.IncludeHelicopters then + self.clientset:FilterCategories("helicopter") + end + return self +end + --- [Internal] onafterStart -- @param #AWACS self -- @param #string From From 65f8da77d2b0a7a28d94d15d4b2b45d900c6a02a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 29 Dec 2022 16:32:29 +0100 Subject: [PATCH 160/603] # Make SRS say TACAN not T A C A N --- Moose Development/Moose/Ops/ATIS.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index e65a2ac71..96794d939 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -2416,6 +2416,8 @@ function ATIS:onafterReport( From, Event, To, Text ) local text = string.gsub( text, "mmHg", "millimeters of Mercury" ) local text = string.gsub( text, "hPa", "hectopascals" ) local text = string.gsub( text, "m/s", "meters per second" ) + local text = string.gsub( text, "TACAN", "tackan" ) + local text = string.gsub( text, "FARP", "farp" ) -- Replace ";" by "." local text = string.gsub( text, ";", " . " ) From d12799a892e939e969481209e65cf2c63f352d4c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 30 Dec 2022 17:39:18 +0100 Subject: [PATCH 161/603] OPS --- Moose Development/Moose/Ops/Auftrag.lua | 4 ++-- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 4ba024548..baf6cc8d6 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1064,10 +1064,10 @@ function AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) -- Orbit speed in m/s. mission.orbitSpeed = UTILS.KnotsToMps(UTILS.KnotsToAltKIAS(Speed or 350, UTILS.MetersToFeet(mission.orbitAltitude))) - + -- Mission speed in km/h. mission.missionSpeed = UTILS.KnotsToKmph(Speed or 350) - + if Leg then mission.orbitLeg=UTILS.NMToMeters(Leg) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 3087195f6..6bcfcd5d1 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -712,7 +712,7 @@ end -- @param #FLIGHTGROUP self -- @return #boolean If true, has landed somewhere. function FLIGHTGROUP:IsLandedAt() - is=self:Is("LandedAt") + local is=self:Is("LandedAt") return is end From 56b7364a13cae4183a87d9492fd445e3ee6b67a4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 31 Dec 2022 12:18:12 +0100 Subject: [PATCH 162/603] # Helo --- Moose Development/Moose/Ops/Awacs.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index cf6dfe67f..c77a15c8f 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -499,7 +499,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.51", -- #string + version = "0.2.52", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -917,7 +917,7 @@ AWACS.TaskStatus = { --@field #boolean FromAI ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO-List 0.2.51 +-- TODO-List 0.2.52 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- -- DONE - WIP - Player tasking, VID @@ -2393,7 +2393,7 @@ function AWACS:_GetIdlePilots() self:T("Adding AI with Callsign: "..entry.CallSign) AIPilots[#AIPilots+1] = _entry end - elseif entry.IsPlayer and not entry.Blocked then + elseif entry.IsPlayer and (not entry.Blocked) and (not entry.Group:IsHelicopter()) then if (not entry.HasAssignedTask) or overridetask then -- must be idle, or? -- check last assignment local TNow = timer.getTime() From ecf4f14222286e5f4f7c1971934298a928adbeeb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 31 Dec 2022 15:56:21 +0100 Subject: [PATCH 163/603] #PLAYERTASK -menu issues --- Moose Development/Moose/Ops/PlayerTask.lua | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index e8a415289..6297744d1 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1443,7 +1443,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.54" +PLAYERTASKCONTROLLER.version="0.1.55" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -3301,6 +3301,9 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) joinorabort = true end + local tasktypes = self:_GetAvailableTaskTypes() + local taskpertype = self:_GetTasksPerType() + for _,_client in pairs(clients) do if _client and _client:IsAlive() then local client = _client -- Wrapper.Client#CLIENT @@ -3349,6 +3352,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) --self.PlayerMenu[playername]:RemoveSubMenus() --oldmenu = self.PlayerMenu[playername] --self.PlayerMenu[playername] = nil + self.PlayerMenu[playername]:RemoveSubMenus() self.PlayerMenu[playername] = MENU_GROUP_DELAYED:New(group,menuname,self.MenuParent) self.PlayerMenu[playername]:SetTag(newtag) self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime() @@ -3395,8 +3399,6 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client):SetTag(newtag) if self.activehasinfomenu and self.taskinfomenu then self:T("Building Active-Info Menus for "..playername) - local tasktypes = self:_GetAvailableTaskTypes() - local taskpertype = self:_GetTasksPerType() if self.PlayerInfoMenu[playername] then self.PlayerInfoMenu[playername]:RemoveSubMenus(nil,oldtag) end @@ -3408,10 +3410,14 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) --- -- JOIN TASK MENU --- - local tasktypes = self:_GetAvailableTaskTypes() - local taskpertype = self:_GetTasksPerType() local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale) + + if self.PlayerJoinMenu[playername] then + self.PlayerJoinMenu[playername]:RemoveSubMenus(nil,oldtag) + end + local joinmenu = MENU_GROUP_DELAYED:New(group,menujoin,topmenu):SetTag(newtag) + self.PlayerJoinMenu[playername] = joinmenu local ttypes = {} local taskmenu = {} @@ -3468,6 +3474,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) --- if rebuilddone then self.PlayerMenu[playername]:RemoveSubMenus(nil,oldtag) + self.PlayerMenu[playername]:Set() self.PlayerMenu[playername]:Refresh() end end From b811aa09a881f41597adf2bbf54dc7602361727d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 31 Dec 2022 17:54:04 +0100 Subject: [PATCH 164/603] # FG improvements --- Moose Development/Moose/Ops/Auftrag.lua | 3 ++- Moose Development/Moose/Ops/FlightGroup.lua | 11 +++++++++-- Moose Development/Moose/Ops/OpsGroup.lua | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index ade7a87c6..f4a72b000 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1308,7 +1308,7 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ end -- Create ORBIT first. - local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAP:GetCoordinate(), Altitude or 10000, Speed, Heading, Leg) + local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAP:GetCoordinate(), Altitude or 10000, Speed or 350, Heading, Leg) -- Mission type CAP. mission.type=AUFTRAG.Type.CAP @@ -1322,6 +1322,7 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ mission.missionTask=ENUMS.MissionTask.CAP mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire + mission.missionSpeed = UTILS.KnotsToAltKIAS(Speed,Altitude) mission.categories={AUFTRAG.Category.AIRCRAFT} diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 6bcfcd5d1..f0b6f1bd7 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -3773,9 +3773,16 @@ function FLIGHTGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Altitud -- Set waypoint index. local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID) - + -- Speed in knots. - Speed=Speed or self:GetSpeedCruise() + if not Speed or Speed < 10 then + + local mission = self:GetMissionCurrent() -- Ops.Auftrag#AUFTRAG + local speed = mission.missionSpeed + Speed = speed or self:GetSpeedCruise() + + end + --Speed=Speed or self:GetSpeedCruise() -- Alt type default is barometric (ASL). For helos we use radar (AGL). local alttype=COORDINATE.WaypointAltType.BARO diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 7fddf424d..2cd2f8e12 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -3213,8 +3213,10 @@ end function OPSGROUP:GetExpectedSpeed() if self:IsHolding() or self:Is("Rearming") or self:IsWaiting() or self:IsRetreated() then + --env.info("GetExpectedSpeed - returning ZERO") return 0 else + --env.info("GetExpectedSpeed - returning self.speedWP = "..self.speedWp) return self.speedWp or 0 end From a35d3e180b60e5684bddfa63c1c8194fde23176f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 31 Dec 2022 18:08:20 +0100 Subject: [PATCH 165/603] FG --- Moose Development/Moose/Ops/Auftrag.lua | 2 +- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index f4a72b000..9e14cff1c 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1322,7 +1322,7 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ mission.missionTask=ENUMS.MissionTask.CAP mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire - mission.missionSpeed = UTILS.KnotsToAltKIAS(Speed,Altitude) + mission.missionSpeed = UTILS.KnotsToAltKIAS(Speed or 350,Altitude) mission.categories={AUFTRAG.Category.AIRCRAFT} diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index f0b6f1bd7..12f2f05db 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -943,7 +943,7 @@ function FLIGHTGROUP:Status() local DCSTask=mission:GetDCSMissionTask() --DCS#Task -- Get task. - local Task=self:GetTaskByID(mission.auftragsnummer) + local Task=mission:GetGroupWaypointTask(self) -- Reset current orbit task. self.controller:resetTask() From 6fa3f0a11cb6aa1b119edd092a553f9e14e2a62c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 1 Jan 2023 12:33:35 +0100 Subject: [PATCH 166/603] #TIMER * Added `StartIf()` --- Moose Development/Moose/Core/Timer.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Timer.lua b/Moose Development/Moose/Core/Timer.lua index 88e1e77b3..02e4cee63 100644 --- a/Moose Development/Moose/Core/Timer.lua +++ b/Moose Development/Moose/Core/Timer.lua @@ -155,7 +155,7 @@ function TIMER:New(Function, ...) return self end ---- Create a new TIMER object. +--- Start TIMER object. -- @param #TIMER self -- @param #number Tstart Relative start time in seconds. -- @param #number dT Interval between function calls in seconds. If not specified `nil`, the function is called only once. @@ -192,6 +192,20 @@ function TIMER:Start(Tstart, dT, Duration) return self end +--- Start TIMER object if a condition is met. Useful for e.g. debugging. +-- @param #TIMER self +-- @param #boolean Condition Must be true for the TIMER to start +-- @param #number Tstart Relative start time in seconds. +-- @param #number dT Interval between function calls in seconds. If not specified `nil`, the function is called only once. +-- @param #number Duration Time in seconds for how long the timer is running. If not specified `nil`, the timer runs forever or until stopped manually by the `TIMER:Stop()` function. +-- @return #TIMER self +function TIMER:StartIf(Condition,Tstart, dT, Duration) + if Condition then + self:Start(Tstart, dT, Duration) + end + return self +end + --- Stop the timer by removing the timer function. -- @param #TIMER self -- @param #number Delay (Optional) Delay in seconds, before the timer is stopped. From d36ceb861ac202f1897c1f268895858be0ea4286 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 1 Jan 2023 13:30:46 +0100 Subject: [PATCH 167/603] FG Mission Speed Fix --- Moose Development/Moose/Ops/FlightGroup.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 88ca5ee7a..c390ce0a7 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -3777,10 +3777,15 @@ function FLIGHTGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Altitud -- Set waypoint index. local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID) - + -- Speed in knots. Speed=Speed or self:GetSpeedCruise() + local mission = self:GetMissionCurrent() + if mission and not self.isHelo then + Speed = mission.missionSpeed and mission.missionSpeed or self:GetSpeedCruise() + end + -- Alt type default is barometric (ASL). For helos we use radar (AGL). local alttype=COORDINATE.WaypointAltType.BARO if self.isHelo then From 9680f38dc8aa69c0ea88923af151993e59cc84e4 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 2 Jan 2023 13:28:36 +0100 Subject: [PATCH 168/603] Update Auftrag.lua NewCAP Line 1313: mission.missionSpeed = UTILS.KnotsToKmph(UTILS.KnotsToAltKIAS(Speed or 350, Altitude)) --- Moose Development/Moose/Ops/Auftrag.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index f5557e44a..73f635a03 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1322,7 +1322,7 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ mission.missionTask=ENUMS.MissionTask.CAP mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire - mission.missionSpeed = UTILS.KnotsToAltKIAS(Speed or 350,Altitude) + mission.missionSpeed = UTILS.KnotsToKmph(UTILS.KnotsToAltKIAS(Speed or 350, Altitude)) mission.categories={AUFTRAG.Category.AIRCRAFT} From 671f4a131e4bea522d734a68ca03adae6e43b702 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 2 Jan 2023 13:32:51 +0100 Subject: [PATCH 169/603] Update FlightGroup.lua Output 3775 AddWaypoint Controls --- Moose Development/Moose/Ops/FlightGroup.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index c390ce0a7..3ac45171f 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -3772,19 +3772,19 @@ end -- @return Ops.OpsGroup#OPSGROUP.Waypoint Waypoint table. function FLIGHTGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Altitude, Updateroute) + self:I(self.lid..string.format("AddWaypoint | Speed = %f | After Wpt = %d | Alt = %d | Updateroute = %s",Speed or -1,AfterWaypointWithID or -1 ,Altitude or -1,tostring(Updateroute)) + -- Create coordinate. local coordinate=self:_CoordinateFromObject(Coordinate) -- Set waypoint index. local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID) - + -- Speed in knots. Speed=Speed or self:GetSpeedCruise() - local mission = self:GetMissionCurrent() - if mission and not self.isHelo then - Speed = mission.missionSpeed and mission.missionSpeed or self:GetSpeedCruise() - end + -- Debug info. + self:T3(self.lid..string.format("Waypoint Speed=%.1f knots", Speed)) -- Alt type default is barometric (ASL). For helos we use radar (AGL). local alttype=COORDINATE.WaypointAltType.BARO From 8ed85795c6f8bfd39bbb5b7541d50afa515dc770 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 2 Jan 2023 13:34:32 +0100 Subject: [PATCH 170/603] #TIMER (#1867) * Added `StartIf()` From 4373ee8d13e766c3768f7f140d9053716f5b4ac0 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 2 Jan 2023 13:56:55 +0100 Subject: [PATCH 171/603] Develop (#1869) * #TIMER * Added `StartIf()` * OPS request * AUFTRAG CAP - Fixed mission speed (unit conversion) * ZONE Co-authored-by: Frank --- Moose Development/Moose/Core/Zone.lua | 72 ++++++ .../Moose/Functional/Warehouse.lua | 219 +++++++++--------- Moose Development/Moose/Ops/ArmyGroup.lua | 5 +- Moose Development/Moose/Ops/Auftrag.lua | 65 +++++- Moose Development/Moose/Ops/Brigade.lua | 19 +- Moose Development/Moose/Ops/Legion.lua | 68 +++++- Moose Development/Moose/Ops/OpsGroup.lua | 80 ++++--- Moose Development/Moose/Ops/OpsZone.lua | 144 ++++++++---- Moose Development/Moose/Utilities/Enums.lua | 10 +- 9 files changed, 472 insertions(+), 210 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index d25a6a4bf..588b9f827 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2084,6 +2084,52 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph return self end + +--- Get the smallest circular zone encompassing all points points of the polygon zone. +-- @param #ZONE_POLYGON_BASE self +-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone. +-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered. +-- @return #ZONE_RADIUS The circular zone. +function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone) + + local center=self:GetVec2() + + local radius=0 + + for _,_vec2 in pairs(self._.Polygon) do + local vec2=_vec2 --DCS#Vec2 + + local r=UTILS.VecDist2D(center, vec2) + + if r>radius then + radius=r + end + + end + + local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName, center, radius, DoNotRegisterZone) + + return zone +end + + +--- Get the smallest rectangular zone encompassing all points points of the polygon zone. +-- @param #ZONE_POLYGON_BASE self +-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone. +-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered. +-- @return #ZONE_POLYGON The rectangular zone. +function ZONE_POLYGON_BASE:GetZoneQuad(ZoneName, DoNotRegisterZone) + + local vec1, vec3=self:GetBoundingVec2() + + local vec2={x=vec1.x, y=vec3.y} + local vec4={x=vec3.x, y=vec1.y} + + local zone=ZONE_POLYGON_BASE:New(ZoneName or self.ZoneName, {vec1, vec2, vec3, vec4}) + + return zone +end + --- Smokes the zone boundaries in a color. -- @param #ZONE_POLYGON_BASE self -- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. @@ -2286,6 +2332,32 @@ function ZONE_POLYGON_BASE:GetBoundingSquare() return { x1 = x1, y1 = y1, x2 = x2, y2 = y2 } end +--- Get the bounding 2D vectors of the polygon. +-- @param #ZONE_POLYGON_BASE self +-- @return DCS#Vec2 Coordinates of western-southern-lower vertex of the box. +-- @return DCS#Vec2 Coordinates of eastern-northern-upper vertex of the box. +function ZONE_POLYGON_BASE:GetBoundingVec2() + + local x1 = self._.Polygon[1].x + local y1 = self._.Polygon[1].y + local x2 = self._.Polygon[1].x + local y2 = self._.Polygon[1].y + + for i = 2, #self._.Polygon do + self:T2( { self._.Polygon[i], x1, y1, x2, y2 } ) + x1 = ( x1 > self._.Polygon[i].x ) and self._.Polygon[i].x or x1 + x2 = ( x2 < self._.Polygon[i].x ) and self._.Polygon[i].x or x2 + y1 = ( y1 > self._.Polygon[i].y ) and self._.Polygon[i].y or y1 + y2 = ( y2 < self._.Polygon[i].y ) and self._.Polygon[i].y or y2 + + end + + local vec1={x=x1, y=y1} + local vec2={x=x2, y=y2} + + return vec1, vec2 +end + --- Draw a frontier on the F10 map with small filled circles. -- @param #ZONE_POLYGON_BASE self -- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1= All. diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index cc82caa61..e02ba3008 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -7987,120 +7987,123 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Loop over all assets that need a parking psot. for _,asset in pairs(assets) do local _asset=asset --#WAREHOUSE.Assetitem - - -- Get terminal type of this asset - local terminaltype=asset.terminalType or self:_GetTerminal(asset.attribute, self:GetAirbaseCategory()) - - -- Asset specific parking. - parking[_asset.uid]={} - - -- Loop over all units - each one needs a spot. - for i=1,_asset.nunits do - -- Asset name - local assetname=_asset.spawngroupname.."-"..tostring(i) + if not _asset.spawned then - -- Loop over all parking spots. - local gotit=false - for _,_parkingspot in pairs(parkingdata) do - local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot - - -- Parking valid? - local valid=true - - if asset.parkingIDs then - -- If asset has assigned parking spots, we take these no matter what. - valid=self:_CheckParkingAsset(parkingspot, asset) - else - - -- Valid terminal type depending on attribute. - local validTerminal=AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) + -- Get terminal type of this asset + local terminaltype=asset.terminalType or self:_GetTerminal(asset.attribute, self:GetAirbaseCategory()) + + -- Asset specific parking. + parking[_asset.uid]={} + + -- Loop over all units - each one needs a spot. + for i=1,_asset.nunits do + + -- Asset name + local assetname=_asset.spawngroupname.."-"..tostring(i) + + -- Loop over all parking spots. + local gotit=false + for _,_parkingspot in pairs(parkingdata) do + local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot - -- Valid parking list. - local validParking=self:_CheckParkingValid(parkingspot) + -- Parking valid? + local valid=true - -- Black and white list. - local validBWlist=airbase:_CheckParkingLists(parkingspot.TerminalID) - - -- Debug info. - --env.info(string.format("FF validTerminal = %s", tostring(validTerminal))) - --env.info(string.format("FF validParking = %s", tostring(validParking))) - --env.info(string.format("FF validBWlist = %s", tostring(validBWlist))) - - -- Check if all are true - valid=validTerminal and validParking and validBWlist - end - - - -- Check correct terminal type for asset. We don't want helos in shelters etc. - if valid then - - -- Coordinate of the parking spot. - local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE - local _termid=parkingspot.TerminalID - local free=true - local problem=nil - - -- Loop over all obstacles. - for _,obstacle in pairs(obstacles) do - - -- Check if aircraft overlaps with any obstacle. - local dist=_spot:Get2DDistance(obstacle.coord) - local safe=_overlap(_asset.size, obstacle.size, dist) - - -- Spot is blocked. - if not safe then - self:T3(self.lid..string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is NOT SAFE", assetname, _asset.uid, _termid, dist)) - free=false - problem=obstacle - problem.dist=dist - break - else - --env.info(string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is SAFE", assetname, _asset.uid, _termid, dist)) - end - - end - - -- Check if spot is free - if free then - - -- Add parkingspot for this asset unit. - table.insert(parking[_asset.uid], parkingspot) - - -- Debug - self:T(self.lid..string.format("Parking spot %d is free for asset %s [id=%d]!", _termid, assetname, _asset.uid)) - - -- Add the unit as obstacle so that this spot will not be available for the next unit. - table.insert(obstacles, {coord=_spot, size=_asset.size, name=assetname, type="asset"}) - - gotit=true - break - + if asset.parkingIDs then + -- If asset has assigned parking spots, we take these no matter what. + valid=self:_CheckParkingAsset(parkingspot, asset) else - - -- Debug output for occupied spots. - if self.Debug then - local coord=problem.coord --Core.Point#COORDINATE - local text=string.format("Obstacle %s [type=%s] blocking spot=%d! Size=%.1f m and distance=%.1f m.", problem.name, problem.type, _termid, problem.size, problem.dist) - self:I(self.lid..text) - coord:MarkToAll(string.format(text)) - else - self:T(self.lid..string.format("Parking spot %d is occupied or not big enough!", _termid)) - end - + + -- Valid terminal type depending on attribute. + local validTerminal=AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) + + -- Valid parking list. + local validParking=self:_CheckParkingValid(parkingspot) + + -- Black and white list. + local validBWlist=airbase:_CheckParkingLists(parkingspot.TerminalID) + + -- Debug info. + --env.info(string.format("FF validTerminal = %s", tostring(validTerminal))) + --env.info(string.format("FF validParking = %s", tostring(validParking))) + --env.info(string.format("FF validBWlist = %s", tostring(validBWlist))) + + -- Check if all are true + valid=validTerminal and validParking and validBWlist end - - else - self:T2(self.lid..string.format("Terminal ID=%d: type=%s not supported", parkingspot.TerminalID, parkingspot.TerminalType)) - end -- check terminal type - end -- loop over parking spots - - -- No parking spot for at least one asset :( - if not gotit then - self:I(self.lid..string.format("WARNING: No free parking spot for asset %s [id=%d]", assetname, _asset.uid)) - return nil - end - end -- loop over asset units + + + -- Check correct terminal type for asset. We don't want helos in shelters etc. + if valid then + + -- Coordinate of the parking spot. + local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE + local _termid=parkingspot.TerminalID + local free=true + local problem=nil + + -- Loop over all obstacles. + for _,obstacle in pairs(obstacles) do + + -- Check if aircraft overlaps with any obstacle. + local dist=_spot:Get2DDistance(obstacle.coord) + local safe=_overlap(_asset.size, obstacle.size, dist) + + -- Spot is blocked. + if not safe then + self:T3(self.lid..string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is NOT SAFE", assetname, _asset.uid, _termid, dist)) + free=false + problem=obstacle + problem.dist=dist + break + else + --env.info(string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is SAFE", assetname, _asset.uid, _termid, dist)) + end + + end + + -- Check if spot is free + if free then + + -- Add parkingspot for this asset unit. + table.insert(parking[_asset.uid], parkingspot) + + -- Debug + self:T(self.lid..string.format("Parking spot %d is free for asset %s [id=%d]!", _termid, assetname, _asset.uid)) + + -- Add the unit as obstacle so that this spot will not be available for the next unit. + table.insert(obstacles, {coord=_spot, size=_asset.size, name=assetname, type="asset"}) + + gotit=true + break + + else + + -- Debug output for occupied spots. + if self.Debug then + local coord=problem.coord --Core.Point#COORDINATE + local text=string.format("Obstacle %s [type=%s] blocking spot=%d! Size=%.1f m and distance=%.1f m.", problem.name, problem.type, _termid, problem.size, problem.dist) + self:I(self.lid..text) + coord:MarkToAll(string.format(text)) + else + self:T(self.lid..string.format("Parking spot %d is occupied or not big enough!", _termid)) + end + + end + + else + self:T2(self.lid..string.format("Terminal ID=%d: type=%s not supported", parkingspot.TerminalID, parkingspot.TerminalType)) + end -- check terminal type + end -- loop over parking spots + + -- No parking spot for at least one asset :( + if not gotit then + self:I(self.lid..string.format("WARNING: No free parking spot for asset %s [id=%d]", assetname, _asset.uid)) + return nil + end + end -- loop over asset units + end -- Asset spawned check end -- loop over asset groups return parking diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 4fba93277..edd3fc94a 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -1826,8 +1826,11 @@ function ARMYGROUP:_UpdateEngageTarget() -- Distance to last known position of target. local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3()) + -- Check line of sight to target. + local los=self:HasLoS(vec3) + -- Check if target moved more than 100 meters or we do not have line of sight. - if dist>100 or not self:HasLoS(self.engage.Target:GetCoordinate()) then + if dist>100 or los==false then --env.info("FF Update Engage Target Moved "..self.engage.Target:GetName()) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 73f635a03..c66d59a79 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -7592,7 +7592,7 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ mission.missionTask=ENUMS.MissionTask.CAP mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire - mission.missionSpeed = UTILS.KnotsToAltKIAS(Speed or 350,Altitude) + mission.missionSpeed = UTILS.KnotsToKmph(UTILS.KnotsToAltKIAS(Speed or 350, Altitude)) mission.categories={AUFTRAG.Category.AIRCRAFT} @@ -11749,16 +11749,73 @@ function AUFTRAG:_SetLogID() end ---- Update DCS task. +--- Get request ID from legion this mission requested assets from -- @param #AUFTRAG self --- @return #AUFTRAG self -function AUFTRAG:_UpdateTask() +-- @param Ops.Legion#LEGION Legion The legion from which to get the request ID. +-- @return #number Request ID (if any). +function AUFTRAG:_GetRequestID(Legion) + local requestid=nil + local name=nil + if type(Legion)=="string" then + name=Legion + else + name=Legion.alias + end + + if name then + requestid=self.requestID[name] + end + + return nil +end + + +--- Get request from legion this mission requested assets from. +-- @param #AUFTRAG self +-- @param Ops.Legion#LEGION Legion The legion from which to get the request ID. +-- @return Functional.Warehouse#WAREHOUSE.PendingItem Request. +function AUFTRAG:_GetRequest(Legion) + + local request=nil + + local requestID=self:_GetRequestID(Legion) + + if requestID then + request=Legion:GetRequestByID(requestID) + end + + return request +end + +--- Set request ID from legion this mission requested assets from +-- @param #AUFTRAG self +-- @param Ops.Legion#LEGION Legion The legion from which to get the request ID. +-- @param #number RequestID Request ID. +-- @return #AUFTRAG self +function AUFTRAG:_SetRequestID(Legion, RequestID) + + local requestid=nil + local name=nil + + if type(Legion)=="string" then + name=Legion + else + name=Legion.alias + end + + if name then + if self.requestID[name] then + self:I(self.lid..string.format("WARNING: Mission already has a request ID=%d!", self.requestID[name])) + end + self.requestID[name]=RequestID + end return self end + --- Update mission F10 map marker. -- @param #AUFTRAG self -- @return #AUFTRAG self diff --git a/Moose Development/Moose/Ops/Brigade.lua b/Moose Development/Moose/Ops/Brigade.lua index 3ce1e6be1..73ae5e043 100644 --- a/Moose Development/Moose/Ops/Brigade.lua +++ b/Moose Development/Moose/Ops/Brigade.lua @@ -572,9 +572,9 @@ function BRIGADE:onafterStatus(From, Event, To) self:I(self.lid..text) end - ------------------- + --------------------- -- Refuelling Info -- - ------------------- + --------------------- if self.verbose>=4 then local text="Refuelling Zones:" for i,_refuellingzone in pairs(self.refuellingZones) do @@ -583,7 +583,20 @@ function BRIGADE:onafterStatus(From, Event, To) text=text..string.format("\n* %s: Mission status=%s, suppliers=%d", refuellingzone.zone:GetName(), refuellingzone.mission:GetState(), refuellingzone.mission:CountOpsGroups()) end self:I(self.lid..text) - end + end + + ---------------- + -- Asset Info -- + ---------------- + if self.verbose>=5 then + local text="Assets in stock:" + for i,_asset in pairs(self.stock) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + -- Info text. + text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned)) + end + self:I(self.lid..text) + end end diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index df441a2ec..141c74c31 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -839,6 +839,58 @@ function LEGION:onafterMissionAssign(From, Event, To, Mission, Legions) end +--- Create a request and add it to the warehouse queue. +-- @param #LEGION self +-- @param Functional.Warehouse#WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. +-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param #number nAsset Number of groups requested that match the asset specification. +-- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. +-- @param #string Assignment A keyword or text that can later be used to identify this request and postprocess the assets. +function LEGION:_AddRequest(AssetDescriptor, AssetDescriptorValue, nAsset, Prio, Assignment) + + -- Defaults. + nAsset=nAsset or 1 + Prio=Prio or 50 + + -- Increase id. + self.queueid=self.queueid+1 + + -- Request queue table item. + local request={ + uid=self.queueid, + prio=Prio, + warehouse=self, + assetdesc=AssetDescriptor, + assetdescval=AssetDescriptorValue, + nasset=nAsset, + transporttype=WAREHOUSE.TransportType.SELFPROPELLED, + ntransport=0, + assignment=tostring(Assignment), + airbase=self:GetAirbase(), + category=self:GetAirbaseCategory(), + ndelivered=0, + ntransporthome=0, + assets={}, + toself=true, + } --Functional.Warehouse#WAREHOUSE.Queueitem + + + -- Add request to queue. + table.insert(self.queue, request) + + local descval="assetlist" + if request.assetdesc==WAREHOUSE.Descriptor.ASSETLIST then + + else + descval=tostring(request.assetdescval) + end + + local text=string.format("Warehouse %s: New request from warehouse %s.\nDescriptor %s=%s, #assets=%s; Transport=%s, #transports=%s.", + self.alias, self.alias, request.assetdesc, descval, tostring(request.nasset), request.transporttype, tostring(request.ntransport)) + self:_DebugMessage(text, 5) + +end + --- On after "MissionRequest" event. Performs a self request to the warehouse for the mission assets. Sets mission status to REQUESTED. -- @param #LEGION self @@ -914,11 +966,8 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission) if Mission.type==AUFTRAG.Type.RELOCATECOHORT then cancel=true - -- Get request ID. - local requestID=currM.requestID[self.alias] - -- Get request. - local request=self:GetRequestByID(requestID) + local request=currM:_GetRequest(self) if request then self:T2(self.lid.."Removing group from cargoset") @@ -999,7 +1048,8 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission) local assignment=string.format("Mission-%d", Mission.auftragsnummer) -- Add request to legion warehouse. - self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, assignment) + --self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, assignment) + self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, Mission.prio, assignment) -- The queueid has been increased in the onafterAddRequest function. So we can simply use it here. Mission.requestID[self.alias]=self.queueid @@ -1091,7 +1141,8 @@ function LEGION:onafterTransportRequest(From, Event, To, OpsTransport) local assignment=string.format("Transport-%d", OpsTransport.uid) -- Add request to legion warehouse. - self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, AssetList, #AssetList, nil, nil, OpsTransport.prio, assignment) + --self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, AssetList, #AssetList, nil, nil, OpsTransport.prio, assignment) + self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST, AssetList, #AssetList, OpsTransport.prio, assignment) -- The queueid has been increased in the onafterAddRequest function. So we can simply use it here. OpsTransport.requestID[self.alias]=self.queueid @@ -1198,8 +1249,9 @@ function LEGION:onafterMissionCancel(From, Event, To, Mission) end -- Remove queued request (if any). - if Mission.requestID[self.alias] then - self:_DeleteQueueItemByID(Mission.requestID[self.alias], self.queue) + local requestID=Mission:_GetRequestID(self) + if requestID then + self:_DeleteQueueItemByID(requestID, self.queue) end end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 09277e67e..d7ce1a101 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -1539,49 +1539,67 @@ function OPSGROUP:SetReturnOnOutOfAmmo() return self end - --- Check if an element of the group has line of sight to a coordinate. -- @param #OPSGROUP self --- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. +-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. Can also be a DCS#Vec3. -- @param #OPSGROUP.Element Element The (optinal) element. If not given, all elements are checked. -- @param DCS#Vec3 OffsetElement Offset vector of the element. -- @param DCS#Vec3 OffsetCoordinate Offset vector of the coordinate. -- @return #boolean If `true`, there is line of sight to the specified coordinate. function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate) - -- Target vector. - local Vec3=Coordinate:GetVec3() + if Coordinate then - -- Optional offset. - if OffsetCoordinate then - Vec3=UTILS.VecAdd(Vec3, OffsetCoordinate) - end - - --- Function to check LoS for an element of the group. - local function checklos(element) - local vec3=element.unit:GetVec3() - if OffsetElement then - vec3=UTILS.VecAdd(vec3, OffsetElement) + -- Target vector. + local Vec3={x=Coordinate.x, y=Coordinate.y, z=Coordinate.z} --Coordinate:GetVec3() + + -- Optional offset. + if OffsetCoordinate then + Vec3=UTILS.VecAdd(Vec3, OffsetCoordinate) end - local _los=land.isVisible(vec3, Vec3) - --self:I({los=_los, source=vec3, target=Vec3}) - return _los - end - - if Element then - local los=checklos(Element) - return los - else - - for _,element in pairs(self.elements) do - -- Get LoS of this element. - local los=checklos(element) - if los then - return true + + --- Function to check LoS for an element of the group. + local function checklos(vec3) + if vec3 then + if OffsetElement then + vec3=UTILS.VecAdd(vec3, OffsetElement) + end + local _los=land.isVisible(vec3, Vec3) + --self:I({los=_los, source=vec3, target=Vec3}) + return _los + end + return nil + end + + if Element then + -- Check los for the given element. + if Element.unit and Element.unit:IsAlive() then + local vec3=Element.unit:GetVec3() + local los=checklos(Element) + return los + end + else + + -- Check if any element has los. + local gotit=false + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + if element and element.unit and element.unit:IsAlive() then + gotit=true + local vec3=element.unit:GetVec3() + -- Get LoS of this element. + local los=checklos(vec3) + if los then + return true + end + end + end + + if gotit then + return false end end - - return false + end return nil diff --git a/Moose Development/Moose/Ops/OpsZone.lua b/Moose Development/Moose/Ops/OpsZone.lua index 831f2a1b8..92d7d2c9b 100644 --- a/Moose Development/Moose/Ops/OpsZone.lua +++ b/Moose Development/Moose/Ops/OpsZone.lua @@ -5,6 +5,7 @@ -- * Monitor if a zone is captured -- * Monitor if an airbase is captured -- * Define conditions under which zones are captured/held +-- * Supports circular and polygon zone shapes -- -- === -- @@ -20,6 +21,7 @@ -- @field #string lid DCS log ID string. -- @field #number verbose Verbosity of output. -- @field Core.Zone#ZONE zone The zone. +-- @field Core.Zone#ZONE_RADIUS zoneCircular The circular zone. -- @field Wrapper.Airbase#AIRBASE airbase The airbase that is monitored. -- @field #string airbaseName Name of the airbase that is monitored. -- @field #string zoneName Name of the zone. @@ -60,9 +62,6 @@ -- -- An OPSZONE is a strategically important area. -- --- **Restrictions** --- --- * Since we are using a DCS routine that scans a zone for units or other objects present in the zone and this DCS routine is limited to cicular zones, only those can be used. -- -- @field #OPSZONE OPSZONE = { @@ -84,9 +83,19 @@ OPSZONE = { -- @field #string Type Type of mission -- @field Ops.Auftrag#AUFTRAG Mission The actual attached mission + +--- Type of zone we are dealing with. +-- @type OPSZONE.ZoneType +-- @field #string Circular Zone is circular. +-- @field #string Polygon Zone is a polygon. +OPSZONE.ZoneType={ + Circular="Circular", + Polygon="Polygon", +} + --- OPSZONE class version. -- @field #string version -OPSZONE.version="0.4.0" +OPSZONE.version="0.5.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -94,6 +103,7 @@ OPSZONE.version="0.4.0" -- TODO: Pause/unpause evaluations. -- TODO: Differentiate between ground attack and boming by air or arty. +-- DONE: Polygon zones. -- DONE: Capture time, i.e. time how long a single coalition has to be inside the zone to capture it. -- DONE: Capturing based on (total) threat level threshold. Unarmed units do not pose a threat and should not be able to hold a zone. -- DONE: Can neutrals capture? No, since they are _neutral_! @@ -125,7 +135,7 @@ function OPSZONE:New(Zone, CoalitionOwner) if type(Zone)=="string" then -- Convert string into a ZONE or ZONE_AIRBASE local Name=Zone - Zone=ZONE:New(Name) + Zone=ZONE:FindByName(Name) if not Zone then local airbase=AIRBASE:FindByName(Name) if airbase then @@ -146,8 +156,17 @@ function OPSZONE:New(Zone, CoalitionOwner) if Zone:IsInstanceOf("ZONE_AIRBASE") then self.airbase=Zone._.ZoneAirbase self.airbaseName=self.airbase:GetName() + self.zoneType=OPSZONE.ZoneType.Circular + self.zoneCircular=Zone elseif Zone:IsInstanceOf("ZONE_RADIUS") then -- Nothing to do. + self.zoneType=OPSZONE.ZoneType.Circular + self.zoneCircular=Zone + elseif Zone:IsInstanceOf("ZONE_POLYGON_BASE") then + -- Nothing to do. + self.zoneType=OPSZONE.ZoneType.Polygon + local zone=Zone --Core.Zone#ZONE_POLYGON + self.zoneCircular=zone:GetZoneRadius(nil, true) else self:E("ERROR: OPSZONE must be a SPHERICAL zone due to DCS restrictions!") return nil @@ -156,10 +175,10 @@ function OPSZONE:New(Zone, CoalitionOwner) -- Set some string id for output to DCS.log file. self.lid=string.format("OPSZONE %s | ", Zone:GetName()) - -- Set some values. + -- Set some values. self.zone=Zone self.zoneName=Zone:GetName() - self.zoneRadius=Zone:GetRadius() + self.zoneRadius=self.zoneCircular:GetRadius() self.Missions = {} self.ScanUnitSet=SET_UNIT:New():FilterZones({Zone}) self.ScanGroupSet=SET_GROUP:New():FilterZones({Zone}) @@ -820,8 +839,6 @@ function OPSZONE:onafterEmpty(From, Event, To) -- Debug info. self:T(self.lid..string.format("Zone is empty EVENT")) - - end --- On after "Attacked" event. @@ -1034,42 +1051,55 @@ function OPSZONE:Scan() local tl=0 local unit=UNIT:Find(DCSUnit) if unit then - - -- Threat level of unit. - tl=unit:GetThreatLevel() + + -- Inside zone. + local inzone=true + if self.zoneType==OPSZONE.ZoneType.Polygon then - -- Add unit to set. - self.ScanUnitSet:AddUnit(unit) + -- Check if unit is really inside the zone. + inzone=unit:IsInZone(self.zone) + + -- Debug marker. + -- Debug: Had cases where a (red) unit was clearly not inside the zone but the scan did find it! + unit:GetCoordinate():MarkToAll(string.format("Unit %s inzone=%s", unit:GetName(), tostring(inzone))) + end - -- Debug: Had cases where a (red) unit was clearly not inside the zone but the scan did find it! - --local inzone=unit:IsInZone(self.zone) - --unit:GetCoordinate():MarkToAll(string.format("Unit %s inzone=%s", unit:GetName(), tostring(inzone))) + if inzone then + + -- Threat level of unit. + tl=unit:GetThreatLevel() + + -- Add unit to set. + self.ScanUnitSet:AddUnit(unit) + + -- Get group of unit. + local group=unit:GetGroup() - -- Get group of unit. - local group=unit:GetGroup() + -- Add group to scanned set. + if group then + self.ScanGroupSet:AddGroup(group, true) + end + + -- Increase counter. + if Coalition==coalition.side.RED then + Nred=Nred+1 + Tred=Tred+tl + elseif Coalition==coalition.side.BLUE then + Nblu=Nblu+1 + Tblu=Tblu+tl + elseif Coalition==coalition.side.NEUTRAL then + Nnut=Nnut+1 + Tnut=Tnut+tl + end + + -- Debug info. + if self.verbose>=4 then + self:I(self.lid..string.format("Found unit %s (coalition=%d)", DCSUnit:getName(), Coalition)) + end - if group then - self.ScanGroupSet:AddGroup(group, true) end end - - -- Increase counter. - if Coalition==coalition.side.RED then - Nred=Nred+1 - Tred=Tred+tl - elseif Coalition==coalition.side.BLUE then - Nblu=Nblu+1 - Tblu=Tblu+tl - elseif Coalition==coalition.side.NEUTRAL then - Nnut=Nnut+1 - Tnut=Tnut+tl - end - - -- Debug info. - if self.verbose>=4 then - self:I(self.lid..string.format("Found unit %s (coalition=%d)", DCSUnit:getName(), Coalition)) - end end elseif ObjectCategory==Object.Category.STATIC and ZoneObject:isExist() then @@ -1079,26 +1109,40 @@ function OPSZONE:Scan() --- -- This is a DCS static object. - local DCSStatic=ZoneObject --DCS#Static + local DCSStatic=ZoneObject --DCS#StaticObject -- Get coalition. local Coalition=DCSStatic:getCoalition() -- CAREFUL! Downed pilots break routine here without any error thrown. --local unit=STATIC:Find(DCSStatic) - - -- Increase counter. - if Coalition==coalition.side.RED then - Nred=Nred+1 - elseif Coalition==coalition.side.BLUE then - Nblu=Nblu+1 - elseif Coalition==coalition.side.NEUTRAL then - Nnut=Nnut+1 + + -- Inside zone. + local inzone=true + if self.zoneType==OPSZONE.ZoneType.Polygon then + + local Vec3=DCSStatic:getPoint() + + inzone=self.zone:IsVec3InZone(Vec3) + end - -- Debug info - if self.verbose>=4 then - self:I(self.lid..string.format("Found static %s (coalition=%d)", DCSStatic:getName(), Coalition)) + if inzone then + + -- Increase counter. + if Coalition==coalition.side.RED then + Nred=Nred+1 + elseif Coalition==coalition.side.BLUE then + Nblu=Nblu+1 + elseif Coalition==coalition.side.NEUTRAL then + Nnut=Nnut+1 + end + + -- Debug info + if self.verbose>=4 then + self:I(self.lid..string.format("Found static %s (coalition=%d)", DCSStatic:getName(), Coalition)) + end + end elseif ObjectCategory==Object.Category.SCENERY then diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index 63f3d6187..678510025 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -28,11 +28,11 @@ ENUMS = {} --- Rules of Engagement. -- @type ENUMS.ROE --- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target. --- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking. --- @field #number OpenFire AI will engage only targets specified in its taskings. --- @field #number ReturnFire AI will only engage threats that shoot first. --- @field #number WeaponHold AI will hold fire under all circumstances. +-- @field #number WeaponFree [AIR] AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target. +-- @field #number OpenFireWeaponFree [AIR] AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking. +-- @field #number OpenFire [AIR, GROUND, NAVAL] AI will engage only targets specified in its taskings. +-- @field #number ReturnFire [AIR, GROUND, NAVAL] AI will only engage threats that shoot first. +-- @field #number WeaponHold [AIR, GROUND, NAVAL] AI will hold fire under all circumstances. ENUMS.ROE = { WeaponFree=0, OpenFireWeaponFree=1, From dcd9915fe0e83a3a29e33f5a3e62b94f8814b89d Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 2 Jan 2023 14:34:12 +0100 Subject: [PATCH 172/603] Merge master in Apple-Dev (#1871) * #TIMER * Added `StartIf()` * PseudoATC - Option to display playername (#1870) Use `myatc:SetReportPlayername()` to switch this on #1864 --- .../Moose/Functional/PseudoATC.lua | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 0cf76281f..67b86c5fe 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -45,6 +45,7 @@ -- @field #number talt Interval in seconds between reporting altitude until touchdown. Default 3 sec. -- @field #boolean chatty Display some messages on events like take-off and touchdown. -- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. +-- @field #boolean reportplayername If true, use playername not callsign on callouts -- @extends Core.Base#BASE --- Adds some rudimentary ATC functionality via the radio menu. @@ -88,6 +89,7 @@ PSEUDOATC={ talt=3, chatty=true, eventsmoose=true, + reportplayername = false, } ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -98,7 +100,7 @@ PSEUDOATC.id="PseudoATC | " --- PSEUDOATC version. -- @field #number version -PSEUDOATC.version="0.9.2" +PSEUDOATC.version="0.9.3" ----------------------------------------------------------------------------------------------------------------------------------------- @@ -183,6 +185,13 @@ function PSEUDOATC:SetMessageDuration(duration) self.mdur=duration or 30 end +--- Use player name, not call sign, in callouts +-- @param #PSEUDOATC self +function PSEUDOATC:SetReportPlayername() + self.reportplayername = true + return self +end + --- Set time interval after which the F10 radio menu is refreshed. -- @param #PSEUDOATC self -- @param #number interval Interval in seconds. Default is every 120 sec. @@ -485,6 +494,9 @@ function PSEUDOATC:PlayerTakeOff(unit, place) -- Bye-Bye message. if place and self.chatty then local text=string.format("%s, %s, you are airborne. Have a safe trip!", place, CallSign) + if self.reportplayername then + text=string.format("%s, %s, you are airborne. Have a safe trip!", place, PlayerName) + end MESSAGE:New(text, self.mdur):ToGroup(group) end @@ -844,7 +856,8 @@ function PSEUDOATC:ReportHeight(GID, UID, dt, _clear) local position=unit:GetCoordinate() local height=get_AGL(position) local callsign=unit:GetCallsign() - + local PlayerName=self.group[GID].player[UID].playername + -- Settings. local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS @@ -856,7 +869,9 @@ function PSEUDOATC:ReportHeight(GID, UID, dt, _clear) -- Message text. local _text=string.format("%s, your altitude is %s AGL.", callsign, Hs) - + if self.reportplayername then + _text=string.format("%s, your altitude is %s AGL.", PlayerName, Hs) + end -- Append flight level. if _clear==false then _text=_text..string.format(" FL%03d.", position.y/30.48) From 9657976f630dc8da72bf6251d413e81136f94a1e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 2 Jan 2023 17:27:20 +0100 Subject: [PATCH 173/603] FG --- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 3ac45171f..d8ee64592 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -3772,7 +3772,7 @@ end -- @return Ops.OpsGroup#OPSGROUP.Waypoint Waypoint table. function FLIGHTGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Altitude, Updateroute) - self:I(self.lid..string.format("AddWaypoint | Speed = %f | After Wpt = %d | Alt = %d | Updateroute = %s",Speed or -1,AfterWaypointWithID or -1 ,Altitude or -1,tostring(Updateroute)) + self:I(self.lid..string.format("AddWaypoint | Speed = %f | After Wpt = %d | Alt = %d | Updateroute = %s",Speed or -1,AfterWaypointWithID or -1 ,Altitude or -1,tostring(Updateroute))) -- Create coordinate. local coordinate=self:_CoordinateFromObject(Coordinate) From 659188e3cb3a0c7157f722a562fb0a27f22dd531 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 2 Jan 2023 17:27:27 +0100 Subject: [PATCH 174/603] UNIT --- Moose Development/Moose/Wrapper/Unit.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 0b6814029..0393d55e0 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -229,7 +229,7 @@ function UNIT:ReSpawnAt( Coordinate, Heading ) SpawnGroupTemplate.y = Coordinate.z self:F( #SpawnGroupTemplate.units ) - for UnitID, UnitData in pairs( SpawnGroup:GetUnits() ) do + for UnitID, UnitData in pairs( SpawnGroup:GetUnits() or {} ) do local GroupUnit = UnitData -- #UNIT self:F( GroupUnit:GetName() ) if GroupUnit:IsAlive() then From f401179c57c28ea5f21a1933f83bb8588814f519 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 3 Jan 2023 10:14:06 +0100 Subject: [PATCH 175/603] #FlightGroup * Added options to allow afterburner, jettison of empty tanks and jettison of weapons --- Moose Development/Moose/Ops/FlightGroup.lua | 63 +++++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index d8ee64592..ec1189049 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -56,6 +56,9 @@ -- @field #number RTBRecallCount Number that counts RTB calls. -- @field Ops.FlightControl#FLIGHTCONTROL.HoldingStack stack Holding stack. -- @field #boolean isReadyTO Flight is ready for takeoff. This is for FLIGHTCONTROL. +-- @field #boolean prohibitAB Disallow (true) or allow (false) AI to use the afterburner. +-- @field #boolean jettisonEmptyTanks Allow (true) or disallow (false) AI to jettison empty fuel tanks. +-- @field #boolean jettisonWeapons Allow (true) or disallow (false) AI to jettison weapons if in danger. -- -- @extends Ops.OpsGroup#OPSGROUP @@ -145,6 +148,9 @@ FLIGHTGROUP = { RTBRecallCount = 0, playerSettings = {}, playerWarnings = {}, + prohibitAB = true, + jettisonEmptyTanks = true, + jettisonWeapons = true, -- that's actually a negative option like prohibitAB } @@ -210,7 +216,7 @@ FLIGHTGROUP.Players={} --- FLIGHTGROUP class version. -- @field #string version -FLIGHTGROUP.version="0.8.2" +FLIGHTGROUP.version="0.8.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -410,6 +416,52 @@ function FLIGHTGROUP:SetVTOL() return self end +--- Set if aircraft is **not** allowed to use afterburner. +-- @param #FLIGHTGROUP self +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetProhibitAfterburner() + self.prohibitAB = true + if self:GetGroup():IsAlive() then + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB, true) + end + return self +end + +--- Set if aircraft is allowed to use afterburner. +-- @param #FLIGHTGROUP self +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetAllowAfterburner() + self.prohibitAB = false + if self:GetGroup():IsAlive() then + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB, false) + end + return self +end + +--- Set if aircraft is allowed to drop empty fuel tanks - set to true to allow, and false to forbid it. +-- @param #FLIGHTGROUP self +-- @param #boolean Switch true or false +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetJettisonEmptyTanks(Switch) + self.jettisonEmptyTanks = Switch + if self:GetGroup():IsAlive() then + self:GetGroup():SetOption(AI.Option.Air.id.JETT_TANKS_IF_EMPTY, Switch) + end + return self +end + +--- Set if aircraft is allowed to drop weapons to escape danger - set to true to allow, and false to forbid it. +-- @param #FLIGHTGROUP self +-- @param #boolean Switch true or false +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetJettisonWeapons(Switch) + self.jettisonWeapons = not Switch + if self:GetGroup():IsAlive() then + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT, not Switch) + end + return self +end + --- Set if group is ready for taxi/takeoff if controlled by a `FLIGHTCONTROL`. -- @param #FLIGHTGROUP self -- @param #boolean ReadyTO If `true`, flight is ready for takeoff. @@ -1759,9 +1811,10 @@ function FLIGHTGROUP:onafterSpawned(From, Event, To) end -- TODO: make this input. - self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT, true) - self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB, true) -- Does not seem to work. AI still used the after burner. - self:GetGroup():SetOption(AI.Option.Air.id.RTB_ON_BINGO, false) + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT, self.jettisonWeapons) + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB, self.prohibitAB) -- Does not seem to work. AI still used the after burner. + self:GetGroup():SetOption(AI.Option.Air.id.RTB_ON_BINGO, false) + self:GetGroup():SetOption(AI.Option.Air.id.JETT_TANKS_IF_EMPTY, self.jettisonEmptyTanks) --self.group:SetOption(AI.Option.Air.id.RADAR_USING, AI.Option.Air.val.RADAR_USING.FOR_CONTINUOUS_SEARCH) -- Update route. @@ -3772,8 +3825,6 @@ end -- @return Ops.OpsGroup#OPSGROUP.Waypoint Waypoint table. function FLIGHTGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Altitude, Updateroute) - self:I(self.lid..string.format("AddWaypoint | Speed = %f | After Wpt = %d | Alt = %d | Updateroute = %s",Speed or -1,AfterWaypointWithID or -1 ,Altitude or -1,tostring(Updateroute))) - -- Create coordinate. local coordinate=self:_CoordinateFromObject(Coordinate) From 812aff7f2ef4a40d0ea5212cbf96655b1c821706 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 3 Jan 2023 10:34:07 +0100 Subject: [PATCH 176/603] AB useage --- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index ec1189049..b46109e29 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -148,7 +148,7 @@ FLIGHTGROUP = { RTBRecallCount = 0, playerSettings = {}, playerWarnings = {}, - prohibitAB = true, + prohibitAB = false, jettisonEmptyTanks = true, jettisonWeapons = true, -- that's actually a negative option like prohibitAB } From d34d902413c10e512fd01ef5bc7d31727cda5ed4 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 4 Jan 2023 09:24:57 +0100 Subject: [PATCH 177/603] Develop (#1874) * #FG * make us AB the default * OPS **CHIEF** - Fixed bugs of polygon opszones - Added option to use CAPTUREZONE auftrag for opszones **OPSZONE** - Fixed bug when marker off **ZONE_POLYGON_BASE** - Added `GetRadius()` function **LEGION** - Improved Reinforcement * AWACS -correct error in SetAwacsDetails (#1873) Corrected setting of Angels Co-authored-by: Frank --- Moose Development/Moose/Core/Zone.lua | 23 +++- .../Moose/Functional/Warehouse.lua | 105 +++++++++--------- Moose Development/Moose/Ops/Auftrag.lua | 59 ++++++---- Moose Development/Moose/Ops/Awacs.lua | 4 +- Moose Development/Moose/Ops/Chief.lua | 26 +++-- Moose Development/Moose/Ops/Legion.lua | 44 ++++++-- Moose Development/Moose/Ops/OpsGroup.lua | 18 +++ Moose Development/Moose/Ops/OpsZone.lua | 2 +- 8 files changed, 182 insertions(+), 99 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 588b9f827..7475c20e3 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2084,13 +2084,10 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph return self end - ---- Get the smallest circular zone encompassing all points points of the polygon zone. +--- Get the smallest radius encompassing all points of the polygon zone. -- @param #ZONE_POLYGON_BASE self --- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone. --- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered. --- @return #ZONE_RADIUS The circular zone. -function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone) +-- @return #number Radius of the zone in meters. +function ZONE_POLYGON_BASE:GetRadius() local center=self:GetVec2() @@ -2106,6 +2103,20 @@ function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone) end end + + return radius +end + +--- Get the smallest circular zone encompassing all points of the polygon zone. +-- @param #ZONE_POLYGON_BASE self +-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone. +-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered. +-- @return #ZONE_RADIUS The circular zone. +function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone) + + local center=self:GetVec2() + + local radius=self:GetRadius() local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName, center, radius, DoNotRegisterZone) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 8a1dfcd95..4e0c0552b 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -5829,62 +5829,65 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Get stock item. local asset=cargoassets[i] --#WAREHOUSE.Assetitem + + if not asset.spawned then - -- Set asset status to not spawned until we capture its birth event. - asset.spawned=false - asset.iscargo=true - - -- Set request ID. - asset.rid=Request.uid - - -- Spawn group name. - local _alias=asset.spawngroupname - - --Request add asset by id. - Request.assets[asset.uid]=asset - - -- Spawn an asset group. - local _group=nil --Wrapper.Group#GROUP - if asset.category==Group.Category.GROUND then - - -- Spawn ground troops. - _group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation) - - elseif asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then - - -- Spawn air units. - if Parking[asset.uid] then - _group=self:_SpawnAssetAircraft(_alias, asset, Request, Parking[asset.uid], UnControlled, Request.lateActivation) - else - _group=self:_SpawnAssetAircraft(_alias, asset, Request, nil, UnControlled, Request.lateActivation) - end - - elseif asset.category==Group.Category.TRAIN then - - -- Spawn train. - if self.rail then - --TODO: Rail should only get one asset because they would spawn on top! - - -- Spawn naval assets. + -- Set asset status to not spawned until we capture its birth event. + asset.iscargo=true + + -- Set request ID. + asset.rid=Request.uid + + -- Spawn group name. + local _alias=asset.spawngroupname + + --Request add asset by id. + Request.assets[asset.uid]=asset + + -- Spawn an asset group. + local _group=nil --Wrapper.Group#GROUP + if asset.category==Group.Category.GROUND then + + -- Spawn ground troops. _group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation) + + elseif asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then + + -- Spawn air units. + if Parking[asset.uid] then + _group=self:_SpawnAssetAircraft(_alias, asset, Request, Parking[asset.uid], UnControlled, Request.lateActivation) + else + _group=self:_SpawnAssetAircraft(_alias, asset, Request, nil, UnControlled, Request.lateActivation) + end + + elseif asset.category==Group.Category.TRAIN then + + -- Spawn train. + if self.rail then + --TODO: Rail should only get one asset because they would spawn on top! + + -- Spawn naval assets. + _group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation) + end + + --self:E(self.lid.."ERROR: Spawning of TRAIN assets not possible yet!") + + elseif asset.category==Group.Category.SHIP then + + -- Spawn naval assets. + _group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.portzone, Request.lateActivation) + + else + self:E(self.lid.."ERROR: Unknown asset category!") end - - --self:E(self.lid.."ERROR: Spawning of TRAIN assets not possible yet!") - - elseif asset.category==Group.Category.SHIP then - - -- Spawn naval assets. - _group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.portzone, Request.lateActivation) - - else - self:E(self.lid.."ERROR: Unknown asset category!") + + -- Trigger event. + if _group then + self:__AssetSpawned(0.01, _group, asset, Request) + end + end - -- Trigger event. - if _group then - self:__AssetSpawned(0.01, _group, asset, Request) - end - end end diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index c66d59a79..d8e3cd0b4 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -6338,6 +6338,8 @@ end -- @field #table DCStask DCS task structure. -- @field #number Ncasualties Number of own casualties during mission. -- @field #number Nkills Number of (enemy) units killed by assets of this mission. +-- @field #number Ndead Number of assigned groups that are dead. +-- @field #number Nassigned Number of assigned groups. -- @field #number Nelements Number of elements (units) assigned to mission. -- @field #number dTevaluate Time interval in seconds before the mission result is evaluated after mission is over. -- @field #number Tover Mission abs. time stamp, when mission was over. @@ -6374,6 +6376,7 @@ end -- @field #number refuelSystem Refuel type (boom or probe) for TANKER missions. -- -- @field Wrapper.Group#GROUP escortGroup The group to be escorted. +-- @field #string escortGroupName Name of the escorted group. -- @field DCS#Vec3 escortVec3 The 3D offset vector from the escorted group to the escort group. -- -- @field #number facDesignation FAC designation type. @@ -9879,7 +9882,7 @@ function AUFTRAG:IsExecuting(AllGroups) local N if self.Nassigned then - N=self.Nassigned-self.Ndead + N=self.Nassigned-self.Ndead else N=self:CountOpsGroups() end @@ -10167,6 +10170,8 @@ function AUFTRAG:onafterStatus(From, Event, To) -- Number of alive groups attached to this mission. local Ngroups=self:CountOpsGroups() + + local Nassigned=self.Nassigned and self.Nassigned-self.Ndead or 0 -- Check if mission is not OVER yet. if self:IsNotOver() then @@ -10202,7 +10207,13 @@ function AUFTRAG:onafterStatus(From, Event, To) self:T(self.lid.."No targets left cancelling mission!") self:Cancel() - elseif self:IsExecuting() and ((not self.reinforce) or self.reinforce==0) then + elseif self:IsExecuting() and ((not self.reinforce) or (self.reinforce==0 and Nassigned<=0)) then + +-- env.info("Mission Done:") +-- env.info(string.format("Nreinforce= %d", self.reinforce or 0)) +-- env.info(string.format("Nassigned = %d", self.Nassigned)) +-- env.info(string.format("Ndead = %d", self.Ndead)) +-- env.info(string.format("Nass-Ndead= %d", Nassigned)) -- Had the case that mission was in state Executing but all assigned groups were dead. -- TODO: might need to loop over all assigned groups @@ -10262,6 +10273,17 @@ function AUFTRAG:onafterStatus(From, Event, To) end self:I(self.lid..text) end + + -- Group info. + if self.verbose>=3 then + -- Data on assigned groups. + local text=string.format("Assets [N=%d,Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0) + for i,_asset in pairs(self.assets or {}) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + text=text..string.format("\n[%d] %s: spawned=%s, requested=%s, reserved=%s", i, asset.spawngroupname, tostring(asset.spawned), tostring(asset.requested), tostring(asset.reserved)) + end + self:I(self.lid..text) + end -- Ready to evaluate mission outcome? local ready2evaluate=self.Tover and Tnow-self.Tover>=self.dTevaluate or false @@ -10680,11 +10702,9 @@ function AUFTRAG:GetGroupEgressWaypointUID(opsgroup) end end - - ---- Check if all flights are done with their mission (or dead). +--- Check if all groups are done with their mission (or dead). -- @param #AUFTRAG self --- @return #boolean If true, all flights are done with the mission. +-- @return #boolean If `true`, all groups are done with the mission. function AUFTRAG:CheckGroupsDone() -- Check status of all OPS groups. @@ -10692,7 +10712,7 @@ function AUFTRAG:CheckGroupsDone() local groupdata=data --#AUFTRAG.GroupData if groupdata then if not (groupdata.status==AUFTRAG.GroupStatus.DONE or groupdata.status==AUFTRAG.GroupStatus.CANCELLED) then - -- At least this flight is not DONE or CANCELLED. + -- At least this group is not DONE or CANCELLED. self:T2(self.lid..string.format("CheckGroupsDone: OPSGROUP %s is not DONE or CANCELLED but in state %s. Mission NOT DONE!", groupdata.opsgroup.groupname, groupdata.status:upper())) return false end @@ -10733,12 +10753,12 @@ function AUFTRAG:CheckGroupsDone() end -- Check if there is still reinforcement to be expected. - if self:IsExecuting() and self.reinforce and self.reinforce>0 then + if self:IsExecuting() and self.reinforce and (self.reinforce>0 or self.Nassigned-self.Ndead>0) then self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] and reinfoce=%d. Mission NOT DONE!", self.status, self:GetState(), self.reinforce)) return false end - -- It could be that all flights were destroyed on the way to the mission execution waypoint. + -- It could be that all groups were destroyed on the way to the mission execution waypoint. -- TODO: would be better to check if everybody is dead by now. if self:IsStarted() and self:CountOpsGroups()==0 then self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!", self.status, self:GetState())) @@ -10873,6 +10893,7 @@ end function AUFTRAG:onafterGroupDead(From, Event, To, OpsGroup) local asset=self:GetAssetByName(OpsGroup.groupname) + if asset then self:AssetDead(asset) end @@ -10965,7 +10986,7 @@ function AUFTRAG:onafterCancel(From, Event, To) -- Debug info. self:T(self.lid..string.format("LEGION %s will cancel the mission. Will wait for mission DONE before evaluation!", legion.alias)) - -- Legion will cancel all flight missions and remove queued request from warehouse queue. + -- Legion will cancel all group's missions and remove queued request from warehouse queue. legion:MissionCancel(self) end @@ -11231,7 +11252,7 @@ function AUFTRAG:onafterRepeat(From, Event, To) end end - -- No flight data. + -- No group data. self.groupdata={} -- Reset casualties and units assigned. @@ -11249,7 +11270,7 @@ function AUFTRAG:onafterRepeat(From, Event, To) end ---- On after "Stop" event. Remove mission from AIRWING and FLIGHTGROUP mission queues. +--- On after "Stop" event. Remove mission from LEGION and OPSGROUP mission queues. -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. @@ -11288,7 +11309,7 @@ function AUFTRAG:onafterStop(From, Event, To) -- No mission assets. self.assets={} - -- No flight data. + -- No group data. self.groupdata={} -- Clear pending scheduler calls. @@ -12093,7 +12114,7 @@ function AUFTRAG:GetDCSMissionTask() DCStask.id="OpsTransport" - -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} DCStask.params=param @@ -12123,7 +12144,7 @@ function AUFTRAG:GetDCSMissionTask() DCStask.id=AUFTRAG.SpecialTask.FORMATION - -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} param.unitname=self:GetTargetName() param.offsetX=200 @@ -12174,7 +12195,7 @@ function AUFTRAG:GetDCSMissionTask() DCStask.id=AUFTRAG.SpecialTask.BARRAGE - -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} param.zone=self:GetObjective() param.altitude=self.artyAltitude @@ -12198,7 +12219,7 @@ function AUFTRAG:GetDCSMissionTask() DCStask.id=AUFTRAG.SpecialTask.PATROLZONE - -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} param.zone=self:GetObjective() param.altitude=self.missionAltitude @@ -12218,7 +12239,7 @@ function AUFTRAG:GetDCSMissionTask() DCStask.id=AUFTRAG.SpecialTask.CAPTUREZONE - -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} DCStask.params=param @@ -12234,7 +12255,7 @@ function AUFTRAG:GetDCSMissionTask() DCStask.id=AUFTRAG.SpecialTask.PATROLZONE - -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} param.zone=self:GetObjective() param.altitude=self.missionAltitude diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index c77a15c8f..59f1ed663 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1840,10 +1840,10 @@ function AWACS:SetAwacsDetails(CallSign,CallSignNo,Angels,Speed,Heading,Leg) self:T(self.lid.."SetAwacsDetails") self.CallSign = CallSign or CALLSIGN.AWACS.Magic self.CallSignNo = CallSignNo or 1 - self.Angels = Angels or 25 + self.AwacsAngels = Angels or 25 local speed = Speed or 250 self.SpeedBase = speed - self.Speed = UTILS.KnotsToAltKIAS(speed,self.Angels*1000) + self.Speed = UTILS.KnotsToAltKIAS(speed,self.AwacsAngels*1000) self.Heading = Heading or 0 self.Leg = Leg or 25 return self diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 35028306a..6acc20fca 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -218,6 +218,7 @@ -- * `AUFTRAG.Type.ARTY` -- * `AUFTRAG.Type.PATROLZONE` -- * `AUFTRAG.Type.ONGUARD` +-- * `AUFTRAG.Type.CAPTUREZONE` -- * `AUFTRAG.Type.RECON` -- * `AUFTRAG.Type.AMMOSUPPLY` -- * `AUFTRAG.Type.BOMBING` @@ -330,7 +331,7 @@ CHIEF.Strategy = { --- CHIEF class version. -- @field #string version -CHIEF.version="0.5.3" +CHIEF.version="0.6.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -3033,6 +3034,9 @@ function CHIEF:RecruitAssetsForZone(StratZone, Resource) LEGION.UnRecruitAssets(assets) return false end + + -- Debug messgage. + self:T2(self.lid..string.format("Recruited %d assets for mission %s", #assets, MissionType)) if MissionType==AUFTRAG.Type.PATROLZONE or MissionType==AUFTRAG.Type.ONGUARD then @@ -3040,10 +3044,7 @@ function CHIEF:RecruitAssetsForZone(StratZone, Resource) --- -- PATROLZONE or ONGUARD --- - - -- Debug messgage. - self:T2(self.lid..string.format("Recruited %d assets for PATROL mission", #assets)) - + if MissionType==AUFTRAG.Type.PATROLZONE then mission=AUFTRAG:NewPATROLZONE(TargetZone) @@ -3054,6 +3055,14 @@ function CHIEF:RecruitAssetsForZone(StratZone, Resource) -- Engage detected targets. mission:SetEngageDetected(25, {"Ground Units", "Light armed ships", "Helicopters"}) + elseif MissionType==AUFTRAG.Type.CAPTUREZONE then + + --- + -- CAPTUREZONE + --- + + mission=AUFTRAG:NewCAPTUREZONE(StratZone.opszone, self.coalition) + elseif MissionType==AUFTRAG.Type.CASENHANCED then --- @@ -3089,6 +3098,9 @@ function CHIEF:RecruitAssetsForZone(StratZone, Resource) end end + -- Here we need a circular zone. + TargetZone=StratZone.opszone.zoneCircular + -- Leg length. local Leg = TargetZone:GetRadius() <= 10000 and 5 or UTILS.MetersToNM(TargetZone:GetRadius()) @@ -3163,9 +3175,7 @@ function CHIEF:RecruitAssetsForZone(StratZone, Resource) if mission then -- Add assets to mission. - for _,asset in pairs(assets) do - mission:AddAsset(asset) - end + mission:_AddAssets(assets) -- Assign mission to legions. self:MissionAssign(mission, legions) diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 141c74c31..811392d49 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -158,12 +158,14 @@ function LEGION:New(WarehouseName, LegionName) -- @function [parent=#LEGION] MissionRequest -- @param #LEGION self -- @param Ops.Auftrag#AUFTRAG Mission The mission. + -- @param #table Assets (Optional) Assets to add. --- Triggers the FSM event "MissionRequest" after a delay. -- @function [parent=#LEGION] __MissionRequest -- @param #LEGION self -- @param #number delay Delay in seconds. -- @param Ops.Auftrag#AUFTRAG Mission The mission. + -- @param #table Assets (Optional) Assets to add. --- On after "MissionRequest" event. -- @function [parent=#LEGION] OnAfterMissionRequest @@ -172,6 +174,7 @@ function LEGION:New(WarehouseName, LegionName) -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Auftrag#AUFTRAG Mission The mission. + -- @param #table Assets (Optional) Assets to add. --- Triggers the FSM event "MissionCancel" after a delay. @@ -671,13 +674,15 @@ function LEGION:CheckMissionQueue() if mission:IsExecuting() and mission.reinforce and mission.reinforce>0 then -- Number of current opsgroups. - local N=mission:CountOpsGroups() + local N=mission.Nassigned-mission.Ndead if N Reinforce=%s", N, mission.NassetsMin, tostring(reinforce))) + -- Debug info. + self:T(self.lid..string.format("Checking Reinforcement Nreinf=%d, Nops=%d, Nassigned=%d, Ndead=%d, Nmin=%d ==> Reinforce=%s", + mission.reinforce, N, mission.Nassigned, mission.Ndead, mission.NassetsMin, tostring(reinforce))) end -- Firstly, check if mission is due? @@ -689,8 +694,8 @@ function LEGION:CheckMissionQueue() -- Did we find enough assets? if recruited then - -- Reserve assets and add to mission. - mission:_AddAssets(assets) + -- Add to mission. + --mission:_AddAssets(assets) -- Recruit asset for escorting recruited mission assets. local EscortAvail=self:RecruitAssetsForEscort(mission, assets) @@ -722,7 +727,7 @@ function LEGION:CheckMissionQueue() if EscortAvail and TransportAvail then -- Got a mission. - self:MissionRequest(mission) + self:MissionRequest(mission, assets) -- Reduce number of reinforcements. if reinforce then @@ -846,6 +851,7 @@ end -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. -- @param #string Assignment A keyword or text that can later be used to identify this request and postprocess the assets. +-- @return Functional.Warehouse#WAREHOUSE.Queueitem The request. function LEGION:_AddRequest(AssetDescriptor, AssetDescriptorValue, nAsset, Prio, Assignment) -- Defaults. @@ -889,6 +895,7 @@ function LEGION:_AddRequest(AssetDescriptor, AssetDescriptorValue, nAsset, Prio, self.alias, self.alias, request.assetdesc, descval, tostring(request.nasset), request.transporttype, tostring(request.ntransport)) self:_DebugMessage(text, 5) + return request end @@ -898,10 +905,14 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Auftrag#AUFTRAG Mission The requested mission. -function LEGION:onafterMissionRequest(From, Event, To, Mission) +-- @param #table Assets (Optional) Assets to add. +function LEGION:onafterMissionRequest(From, Event, To, Mission, Assets) -- Debug info. self:T(self.lid..string.format("MissionRequest for mission %s [%s]", Mission:GetName(), Mission:GetType())) + + -- Take provided assets or that of the mission. + Assets=Assets or Mission.assets -- Set mission status from QUEUED to REQUESTED. Mission:Requested() @@ -917,7 +928,7 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission) -- Assets to be requested. local Assetlist={} - for _,_asset in pairs(Mission.assets) do + for _,_asset in pairs(Assets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- Check that this asset belongs to this Legion warehouse. @@ -929,7 +940,7 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission) -- Spawned Assets --- - if asset.flightgroup then + if asset.flightgroup and not asset.flightgroup:IsMissionInQueue(Mission) then -- Add new mission. asset.flightgroup:AddMission(Mission) @@ -992,11 +1003,13 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission) end + Mission:AddAsset(asset) + -- Trigger event. self:__OpsOnMission(2, asset.flightgroup, Mission) else - self:E(self.lid.."ERROR: OPSGROUP for asset does NOT exist but it seems to be SPAWNED (asset.spawned=true)!") + self:T(self.lid.."ERROR: OPSGROUP for asset does NOT exist but it seems to be SPAWNED (asset.spawned=true)!") end else @@ -1040,22 +1053,29 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission) if Mission.type==AUFTRAG.Type.ALERT5 then asset.takeoffType=COORDINATE.WaypointType.TakeOffParking end + + Mission:AddAsset(asset) end -- Set assignment. -- TODO: Get/set functions for assignment string. local assignment=string.format("Mission-%d", Mission.auftragsnummer) + + --local request=Mission:_GetRequest(self) -- Add request to legion warehouse. --self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, assignment) - self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, Mission.prio, assignment) + local request=self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, Mission.prio, assignment) + + env.info(string.format("FF Added request=%d for Nasssets=%d", request.uid, #Assetlist)) -- The queueid has been increased in the onafterAddRequest function. So we can simply use it here. - Mission.requestID[self.alias]=self.queueid + --Mission.requestID[self.alias]=self.queueid + Mission:_SetRequestID(self, self.queueid) -- Get request. - local request=self:GetRequestByID(self.queueid) + --local request=self:GetRequestByID(self.queueid) -- Debug info. self:T(self.lid..string.format("Mission %s [%s] got Request ID=%d", Mission:GetName(), Mission:GetType(), self.queueid)) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index d7ce1a101..5dcdad6be 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -5067,6 +5067,24 @@ function OPSGROUP:GetMissionByID(id) return nil end +--- Check if a given mission is already in the queue. +-- @param #OPSGROUP self +-- @param Ops.Auftrag#AUFTRAG Mission the mission to check +-- @return #boolean If `true`, the mission is in the queue. +function OPSGROUP:IsMissionInQueue(Mission) + + for _,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + + if mission.auftragsnummer==Mission.auftragsnummer then + return true + end + + end + + return false +end + --- Get mission by its task id. -- @param #OPSGROUP self -- @param #number taskid The id of the (waypoint) task of the mission. diff --git a/Moose Development/Moose/Ops/OpsZone.lua b/Moose Development/Moose/Ops/OpsZone.lua index 92d7d2c9b..7fcabeb77 100644 --- a/Moose Development/Moose/Ops/OpsZone.lua +++ b/Moose Development/Moose/Ops/OpsZone.lua @@ -506,7 +506,7 @@ function OPSZONE:SetMarkZone(Switch, ReadOnly) self.marker:Remove() end self.marker=nil - --self.marker=false + self.markZone=false end return self end From dde458aea042515d2eeb71225993dfbb71c9c761 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:42:25 +0100 Subject: [PATCH 178/603] Update CSAR.lua --- Moose Development/Moose/Ops/CSAR.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index da3853223..165339793 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -2107,10 +2107,11 @@ function CSAR:_AddBeaconToGroup(_group, _freq) if _group:IsAlive() then local _radioUnit = _group:GetUnit(1) if _radioUnit then + local name = _radioUnit:GetName() local Frequency = _freq -- Freq in Hertz local Sound = "l10n/DEFAULT/"..self.radioSound local vec3 = _radioUnit:GetVec3() or _radioUnit:GetPositionVec3() or {x=0,y=0,z=0} - trigger.action.radioTransmission(Sound, vec3, 0, false, Frequency, self.ADFRadioPwr or 1000) -- Beacon in MP only runs for exactly 30secs straight + trigger.action.radioTransmission(Sound, vec3, 0, false, Frequency, self.ADFRadioPwr or 1000,name..math.random(1,10000)) -- Beacon in MP only runs for exactly 30secs straight end end return self From 47f6f7e8cc96d29e52a3397c6d0300860ef5dcf8 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:42:56 +0100 Subject: [PATCH 179/603] Update CTLD.lua --- Moose Development/Moose/Ops/CTLD.lua | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 677c810e8..04ceca24f 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3754,18 +3754,22 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip, IsDropped) end end local Sound = Sound or "beacon.ogg" - if IsDropped and Zone then - local ZoneCoord = Zone - local ZoneVec3 = ZoneCoord:GetVec3(1) - local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz - local Sound = self.RadioPath..Sound - trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, tonumber(Frequency), 1000) -- Beacon in MP only runs for 30secs straight - elseif Zone then - local ZoneCoord = Zone:GetCoordinate(1) - local ZoneVec3 = ZoneCoord:GetVec3() - local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz - local Sound = self.RadioPath..Sound - trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, tonumber(Frequency), 1000) -- Beacon in MP only runs for 30secs straight + if Zone then + if IsDropped then + local ZoneCoord = Zone + local ZoneVec3 = ZoneCoord:GetVec3() or {x=0,y=0,z=0} + -- local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz + local Frequency = Mhz * 1000000 -- Freq in Hertz + local Sound = self.RadioPath..Sound + trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000, name..math.random(1,10000)) -- Beacon in MP only runs for 30secs straight + else + local ZoneCoord = Zone:GetCoordinate() + local ZoneVec3 = ZoneCoord:GetVec3() or {x=0,y=0,z=0} + --local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz + local Frequency = Mhz * 1000000 -- Freq in Hert + local Sound = self.RadioPath..Sound + trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000, name ..math.random(1,10000)) -- Beacon in MP only runs for 30secs straight + end end return self end @@ -3817,11 +3821,7 @@ function CTLD:_RefreshRadioBeacons() local Name = czone.name local FM = FMbeacon.frequency -- MHz local VHF = VHFbeacon.frequency -- KHz - local UHF = UHFbeacon.frequency -- MHz - -- local co = coroutine.create(self._AddRadioBeacon) - --coroutine.resume(co, self, Name,Sound,FM,CTLD.RadioModulation.FM, IsShip, IsDropped) - --coroutine.resume(co, self, Name,Sound,VHF,CTLD.RadioModulation.FM, IsShip, IsDropped) - --coroutine.resume(co, self, Name,Sound,UHF,CTLD.RadioModulation.AM, IsShip, IsDropped) + local UHF = UHFbeacon.frequency -- MHz self:_AddRadioBeacon(Name,Sound,FM, CTLD.RadioModulation.FM, IsShip, IsDropped) self:_AddRadioBeacon(Name,Sound,VHF,CTLD.RadioModulation.FM, IsShip, IsDropped) self:_AddRadioBeacon(Name,Sound,UHF,CTLD.RadioModulation.AM, IsShip, IsDropped) From 24cc5ca32dcde1c08223913fa3746d34be108a21 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:53:59 +0100 Subject: [PATCH 180/603] Update CTLD.lua --- Moose Development/Moose/Ops/CTLD.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 04ceca24f..27e009cea 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3761,15 +3761,19 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip, IsDropped) -- local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz local Frequency = Mhz * 1000000 -- Freq in Hertz local Sound = self.RadioPath..Sound - trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000, name..math.random(1,10000)) -- Beacon in MP only runs for 30secs straight + trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000, Name..math.random(1,10000)) -- Beacon in MP only runs for 30secs straight + self:I(string.format("Beacon added | Name = %s | Sound = %s | Vec3 = %d %d %d | Freq = %f | Modulation = %d (0=AM/1=FM)",Name,Sound,ZoneVec3.x,ZoneVec3.y,ZoneVec3.z,MhZ,Modulation)) else local ZoneCoord = Zone:GetCoordinate() local ZoneVec3 = ZoneCoord:GetVec3() or {x=0,y=0,z=0} --local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz local Frequency = Mhz * 1000000 -- Freq in Hert local Sound = self.RadioPath..Sound - trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000, name ..math.random(1,10000)) -- Beacon in MP only runs for 30secs straight + trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000, Name..math.random(1,10000)) -- Beacon in MP only runs for 30secs straight + self:I(string.format("Beacon added | Name = %s | Sound = %s | Vec3 = {x=%d, y=%d, z=%d} | Freq = %f | Modulation = %d (0=AM/1=FM)",Name,Sound,ZoneVec3.x,ZoneVec3.y,ZoneVec3.z,MhZ,Modulation)) end + else + self:E(self.lid.."***** _AddRadioBeacon: Zone does not exist: "..Name) end return self end From 16af086cc51e74dfcde885679b2c27a91ffb6763 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 5 Jan 2023 10:46:06 +0100 Subject: [PATCH 181/603] #CTLD --- Moose Development/Moose/Ops/CTLD.lua | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 27e009cea..70aecad2a 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1088,7 +1088,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.24" +CTLD.version="1.0.25" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3758,19 +3758,17 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip, IsDropped) if IsDropped then local ZoneCoord = Zone local ZoneVec3 = ZoneCoord:GetVec3() or {x=0,y=0,z=0} - -- local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz local Frequency = Mhz * 1000000 -- Freq in Hertz local Sound = self.RadioPath..Sound trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000, Name..math.random(1,10000)) -- Beacon in MP only runs for 30secs straight - self:I(string.format("Beacon added | Name = %s | Sound = %s | Vec3 = %d %d %d | Freq = %f | Modulation = %d (0=AM/1=FM)",Name,Sound,ZoneVec3.x,ZoneVec3.y,ZoneVec3.z,MhZ,Modulation)) + self:T2(string.format("Beacon added | Name = %s | Sound = %s | Vec3 = %d %d %d | Freq = %f | Modulation = %d (0=AM/1=FM)",Name,Sound,ZoneVec3.x,ZoneVec3.y,ZoneVec3.z,Mhz,Modulation)) else local ZoneCoord = Zone:GetCoordinate() local ZoneVec3 = ZoneCoord:GetVec3() or {x=0,y=0,z=0} - --local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz local Frequency = Mhz * 1000000 -- Freq in Hert local Sound = self.RadioPath..Sound - trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000, Name..math.random(1,10000)) -- Beacon in MP only runs for 30secs straight - self:I(string.format("Beacon added | Name = %s | Sound = %s | Vec3 = {x=%d, y=%d, z=%d} | Freq = %f | Modulation = %d (0=AM/1=FM)",Name,Sound,ZoneVec3.x,ZoneVec3.y,ZoneVec3.z,MhZ,Modulation)) + trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000, Name..math.random(1,10000)) -- Beacon in MP only runs for 30secs straightt + self:T2(string.format("Beacon added | Name = %s | Sound = %s | Vec3 = {x=%d, y=%d, z=%d} | Freq = %f | Modulation = %d (0=AM/1=FM)",Name,Sound,ZoneVec3.x,ZoneVec3.y,ZoneVec3.z,Mhz,Modulation)) end else self:E(self.lid.."***** _AddRadioBeacon: Zone does not exist: "..Name) From 4b19cbcf67cda5de276f745766a7745483fec3c0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 8 Jan 2023 18:08:53 +0100 Subject: [PATCH 182/603] #PLAYERTASK * Added FSM events PlayerJoinedTask and PlayerAbortedTask --- Moose Development/Moose/Ops/PlayerTask.lua | 28 ++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 6297744d1..08b1e2d38 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -160,7 +160,7 @@ function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType) self:AddTransition("*", "Planned", "Planned") -- Task is in planning stage. self:AddTransition("*", "Requested", "Requested") -- Task clients have been requested to join. self:AddTransition("*", "ClientAdded", "*") -- Client has been added to the task - self:AddTransition("*", "ClientRemoved", "*") -- Client has been added to the task + self:AddTransition("*", "ClientRemoved", "*") -- Client has been removed from the task self:AddTransition("*", "Executing", "Executing") -- First client is executing the Task. self:AddTransition("*", "Done", "Done") -- All clients have reported that Task is done. self:AddTransition("*", "Cancel", "Done") -- Command to cancel the Task. @@ -1443,7 +1443,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.55" +PLAYERTASKCONTROLLER.version="0.1.56" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -1536,6 +1536,8 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self:AddTransition("*", "TaskTargetFlared", "*") self:AddTransition("*", "TaskTargetIlluminated", "*") self:AddTransition("*", "TaskRepeatOnFailed", "*") + self:AddTransition("*", "PlayerJoinedTask", "*") + self:AddTransition("*", "PlayerAbortedTask", "*") self:AddTransition("*", "Stop", "Stopped") self:__Start(2) @@ -1629,6 +1631,26 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) -- @param #string To To state. -- @param Ops.PlayerTask#PLAYERTASK Task + --- On After "PlayerJoinedTask" event. Player joined a task. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterPlayerJoinedTask + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Group#GROUP Group The player group object + -- @param Wrapper.Client#CLIENT Client The player client object + -- @param Ops.PlayerTask#PLAYERTASK Task + + --- On After "PlayerAbortedTask" event. Player aborted a task. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterPlayerAbortedTask + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Group#GROUP Group The player group object + -- @param Wrapper.Client#CLIENT Client The player client object + -- @param Ops.PlayerTask#PLAYERTASK Task + end --- [Internal] Init localization @@ -2833,6 +2855,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force) self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) end self.TasksPerPlayer:Push(Task,playername) + self:__PlayerJoinedTask(1, Group, Client, Task) -- clear menu self:_BuildMenus(Client,true) end @@ -3210,6 +3233,7 @@ function PLAYERTASKCONTROLLER:_AbortTask(Group, Client) if self.UseSRS then self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2) end + self:__PlayerAbortedTask(1,Group, Client,task) else text = self.gettext:GetEntry("NOACTIVETASK",self.locale) end From 581b5b3de9b8aa20a261098c4282c5f6e6a57cbc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 10 Jan 2023 07:48:13 +0100 Subject: [PATCH 183/603] fix --- .../Moose/Functional/PseudoATC.lua | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 67b86c5fe..38803afb0 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -100,7 +100,7 @@ PSEUDOATC.id="PseudoATC | " --- PSEUDOATC version. -- @field #number version -PSEUDOATC.version="0.9.3" +PSEUDOATC.version="0.9.4" ----------------------------------------------------------------------------------------------------------------------------------------- @@ -450,14 +450,18 @@ function PSEUDOATC:PlayerLanded(unit, place) local group=unit:GetGroup() local GID=group:GetID() local UID=unit:GetDCSObject():getID() - local PlayerName=self.group[GID].player[UID].playername - local UnitName=self.group[GID].player[UID].unitname - local GroupName=self.group[GID].player[UID].groupname - - -- Debug message. - local text=string.format("Player %s in unit %s of group %s (id=%d) landed at %s.", PlayerName, UnitName, GroupName, GID, place) - self:T(PSEUDOATC.id..text) - MESSAGE:New(text, 30):ToAllIf(self.Debug) + --local PlayerName=self.group[GID].player[UID].playername + --local UnitName=self.group[GID].player[UID].unitname + --local GroupName=self.group[GID].player[UID].groupname + local PlayerName = unit:GetPlayerName() or "Ghost" + local UnitName = unit:GetName() or "Ghostplane" + local GroupName = group:GetName() or "Ghostgroup" + if self.Debug then + -- Debug message. + local text=string.format("Player %s in unit %s of group %s landed at %s.", PlayerName, UnitName, GroupName, place) + self:T(PSEUDOATC.id..text) + MESSAGE:New(text, 30):ToAllIf(self.Debug) + end -- Stop altitude reporting timer if its activated. self:AltitudeTimerStop(GID,UID) @@ -479,18 +483,22 @@ function PSEUDOATC:PlayerTakeOff(unit, place) -- Gather some information. local group=unit:GetGroup() - local GID=group:GetID() - local UID=unit:GetDCSObject():getID() - local PlayerName=self.group[GID].player[UID].playername - local CallSign=self.group[GID].player[UID].callsign - local UnitName=self.group[GID].player[UID].unitname - local GroupName=self.group[GID].player[UID].groupname - - -- Debug message. - local text=string.format("Player %s in unit %s of group %s (id=%d) took off at %s.", PlayerName, UnitName, GroupName, GID, place) - self:T(PSEUDOATC.id..text) - MESSAGE:New(text, 30):ToAllIf(self.Debug) - + --local GID=group:GetID() + --local UID=unit:GetDCSObject():getID() + --local PlayerName=self.group[GID].player[UID].playername + --local CallSign=self.group[GID].player[UID].callsign + --local UnitName=self.group[GID].player[UID].unitname + --local GroupName=self.group[GID].player[UID].groupname + local PlayerName = unit:GetPlayerName() or "Ghost" + local UnitName = unit:GetName() or "Ghostplane" + local GroupName = group:GetName() or "Ghostgroup" + local CallSign = unit:GetCallsign() or "Ghost11" + if self.Debug then + -- Debug message. + local text=string.format("Player %s in unit %s of group %s took off at %s.", PlayerName, UnitName, GroupName, place) + self:T(PSEUDOATC.id..text) + MESSAGE:New(text, 30):ToAllIf(self.Debug) + end -- Bye-Bye message. if place and self.chatty then local text=string.format("%s, %s, you are airborne. Have a safe trip!", place, CallSign) From 43123ec93eb2bfff6a1d8a1185303f35db93177b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 10 Jan 2023 12:40:28 +0100 Subject: [PATCH 184/603] #AWACS * Fixed initial screen text on bogey dope just being x group(s) #PLAYERTASK * Improved logic to declare task succes even if not player is assigned --- Moose Development/Moose/Ops/Awacs.lua | 3 ++- Moose Development/Moose/Ops/PlayerTask.lua | 17 ++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 59f1ed663..e087ae1c4 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -499,7 +499,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.52", -- #string + version = "0.2.53", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -2901,6 +2901,7 @@ function AWACS:_BogeyDope(Group) if contactsAO > 0 then local dope = self.gettext:GetEntry("DOPE",self.locale) text = string.format(dope,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + textScreen = string.format(dope,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) local onetxt = self.gettext:GetEntry("ONE",self.locale) local grptxt = self.gettext:GetEntry("GROUP",self.locale) local groupstxt = self.gettext:GetEntry("GROUPMULTI",self.locale) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 08b1e2d38..f63d051ee 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -96,7 +96,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.11" +PLAYERTASK.version="0.1.12" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -661,6 +661,8 @@ function PLAYERTASK:onafterStatus(From, Event, To) local status = self:GetState() + if status == "Stopped" then return self end + -- Check Target status local targetdead = false @@ -672,10 +674,11 @@ function PLAYERTASK:onafterStatus(From, Event, To) return self end end + + local clientsalive = false if status == "Executing" then -- Check Clients alive - local clientsalive = false local ClientTable = self.Clients:GetDataTable() for _,_client in pairs(ClientTable) do local client = _client -- Wrapper.Client#CLIENT @@ -689,7 +692,10 @@ function PLAYERTASK:onafterStatus(From, Event, To) self:__Failed(-2) status = "Failed" end - + end + + -- Continue if we are not done + if status ~= "Done" and status ~= "Stopped" then -- Any success condition true? local successCondition=self:_EvalConditionsAny(self.conditionSuccess) @@ -707,12 +713,9 @@ function PLAYERTASK:onafterStatus(From, Event, To) if self.verbose then self:I(self.lid.."Target dead: "..tostring(targetdead).." | Clients alive: " .. tostring(clientsalive)) end - end - -- Continue if we are not done - if status ~= "Done" then self:__Status(-20) - else + elseif status ~= "Stopped" then self:__Stop(-1) end From 44f6f0fd402a706c10d4caa1f053563d4b9fc695 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 10 Jan 2023 17:08:08 +0100 Subject: [PATCH 185/603] #PLAYERTASK * Even nicer MGRS readouts --- Moose Development/Moose/Ops/PlayerTask.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index f63d051ee..bc8bd2bb8 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -3062,10 +3062,12 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) text=string.gsub(text,"°M|","° magnetic; ") end if string.find(CoordText,"MGRS") then - local Text = string.gsub(CoordText,"%d","%1;") -- "0 5 1 " - Text = string.gsub(Text," $","") -- "0 5 1" - CoordText = string.gsub(Text,"0","zero") - CoordText = string.gsub(Text,"MGRS","MGRS;") + local Text = string.gsub(CoordText,"MGRS ","") + Text = string.gsub(Text,"%s+","") + Text = string.gsub(Text,"([%a%d])","%1;") -- "0 5 1 " + Text = string.gsub(Text,"0","zero") + Text = string.gsub(Text,"9","niner") + CoordText = "MGRS;"..Text if self.PathToGoogleKey then CoordText = string.format("%s",CoordText) end From b04624bd6093a698a9d88a5e3c7b6b06b56f5c93 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 17 Jan 2023 09:25:24 +0100 Subject: [PATCH 186/603] #ATIS docu fix --- Moose Development/Moose/Ops/ATIS.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 96794d939..3780ba573 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -634,7 +634,7 @@ ATIS.version = "0.9.14" -- Constructor ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Create a new ATIS class object for a specific aircraft carrier unit. +--- Create a new ATIS class object for a specific airbase. -- @param #ATIS self -- @param #string AirbaseName Name of the airbase. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. From 0b1c287f683b38d394cc96c4cbc426ba2997c433 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 17 Jan 2023 12:08:57 +0100 Subject: [PATCH 187/603] #Controllable - docu changes --- Moose Development/Moose/Wrapper/Controllable.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 62fc20bda..c3243f650 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -67,7 +67,6 @@ -- * @{#CONTROLLABLE.TaskRouteToVec2}: (AIR + GROUND) Make the Controllable move to a given point. -- * @{#CONTROLLABLE.TaskRouteToVec3}: (AIR + GROUND) Make the Controllable move to a given point. -- * @{#CONTROLLABLE.TaskRouteToZone}: (AIR + GROUND) Route the controllable to a given zone. --- * @{#CONTROLLABLE.TaskReturnToBase}: (AIR) Route the controllable to an airbase. -- -- ## 2.2) EnRoute assignment -- From a51176967c852eb63efdf8106d34b7235fd4ff1e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 19 Jan 2023 15:00:18 +0100 Subject: [PATCH 188/603] #UTILS - make LoadSetOfGroups save(r) for groups spawned with SpawnScheduled --- Moose Development/Moose/Utilities/Utils.lua | 146 ++++++++++++-------- 1 file changed, 92 insertions(+), 54 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 280fdeaad..5896f21c6 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -2477,6 +2477,12 @@ end function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,Density) local fires = {} + local usedtemplates = {} + local spawn = true + if Spawn == false then spawn = false end + local filename = Filename or "SetOfGroups" + local setdata = SET_GROUP:New() + local datatable = {} local function Smokers(name,coord,effect,density) local eff = math.random(8) @@ -2502,13 +2508,73 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,D end end end + + local function PostSpawn(args) + local spwndgrp = args[1] + local size = args[2] + local structure = args[3] - local spawn = true - if Spawn == false then spawn = false end + setdata:AddObject(spwndgrp) + local actualsize = spwndgrp:CountAliveUnits() + if actualsize > size then + if Structured and structure then + + local loadedstructure = {} + local strcset = UTILS.Split(structure,";") + for _,_data in pairs(strcset) do + local datasplit = UTILS.Split(_data,"==") + loadedstructure[datasplit[1]] = tonumber(datasplit[2]) + end + + local originalstructure = UTILS.GetCountPerTypeName(spwndgrp) + + for _name,_number in pairs(originalstructure) do + local loadednumber = 0 + if loadedstructure[_name] then + loadednumber = loadedstructure[_name] + end + local reduce = false + if loadednumber < _number then reduce = true end + + if reduce then + Cruncher(spwndgrp,_name,_number-loadednumber) + end + + end + else + local reduction = actualsize-size + -- reduce existing group + local units = spwndgrp:GetUnits() + local units2 = UTILS.ShuffleTable(units) -- randomize table + for i=1,reduction do + units2[i]:Destroy(false) + end + end + end + end + + local function MultiUse(Data) + local template = Data.template + if template and usedtemplates[template] and usedtemplates[template].used and usedtemplates[template].used > 1 then + -- multispawn + if not usedtemplates[template].done then + local spwnd = 0 + local spawngrp = SPAWN:New(template) + spawngrp:InitLimit(0,usedtemplates[template].used) + for _,_entry in pairs(usedtemplates[template].data) do + spwnd = spwnd + 1 + local sgrp=spawngrp:SpawnFromCoordinate(_entry.coordinate,spwnd) + BASE:ScheduleOnce(0.5,PostSpawn,{sgrp,_entry.size,_entry.structure}) + end + usedtemplates[template].done = true + end + return true + else + return false + end + end + --BASE:I("Spawn = "..tostring(spawn)) - local filename = Filename or "SetOfGroups" - local setdata = SET_GROUP:New() - local datatable = {} if UTILS.CheckFileExists(Path,filename) then local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename) -- remove header @@ -2525,55 +2591,27 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,D local structure = dataset[7] local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz}) local group=nil - local data = { groupname=groupname, size=size, coordinate=coordinate, template=template } - table.insert(datatable,data) - if spawn then - local group = SPAWN:New(template) - :InitDelayOff() - :OnSpawnGroup( - function(spwndgrp) - setdata:AddObject(spwndgrp) - local actualsize = spwndgrp:CountAliveUnits() - if actualsize > size then - if Structured and structure then - --BASE:I("Reducing group structure!") - local loadedstructure = {} - local strcset = UTILS.Split(structure,";") - for _,_data in pairs(strcset) do - local datasplit = UTILS.Split(_data,"==") - loadedstructure[datasplit[1]] = tonumber(datasplit[2]) - end - --BASE:I({loadedstructure}) - local originalstructure = UTILS.GetCountPerTypeName(spwndgrp) - --BASE:I({originalstructure}) - for _name,_number in pairs(originalstructure) do - local loadednumber = 0 - if loadedstructure[_name] then - loadednumber = loadedstructure[_name] - end - local reduce = false - if loadednumber < _number then reduce = true end - - --BASE:I(string.format("Looking at: %s | Original number: %d | Loaded number: %d | Reduce: %s",_name,_number,loadednumber,tostring(reduce))) - - if reduce then - Cruncher(spwndgrp,_name,_number-loadednumber) - end - - end - else - local reduction = actualsize-size - -- reduce existing group - local units = spwndgrp:GetUnits() - local units2 = UTILS.ShuffleTable(units) -- randomize table - for i=1,reduction do - units2[i]:Destroy(false) - end - end - end - end - ) - :SpawnFromCoordinate(coordinate) + if size > 0 then + local data = { groupname=groupname, size=size, coordinate=coordinate, template=template, structure=structure } + table.insert(datatable,data) + if usedtemplates[template] then + usedtemplates[template].used = usedtemplates[template].used + 1 + table.insert(usedtemplates[template].data,data) + else + usedtemplates[template] = { + data = {}, + used = 1, + done = false, + } + table.insert(usedtemplates[template].data,data) + end + end + end + for _id,_entry in pairs (datatable) do + if spawn and not MultiUse(_entry) and _entry.size > 0 then + local group = SPAWN:New(_entry.template) + local sgrp=group:SpawnFromCoordinate(_entry.coordinate) + BASE:ScheduleOnce(0.5,PostSpawn,{sgrp,_entry.size,_entry.structure}) end end else From df9a572656406c57679be5efd2fec47d148756ff Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 19 Jan 2023 16:58:31 +0100 Subject: [PATCH 189/603] #FG - nil check on GetClosestAirbase() --- Moose Development/Moose/Ops/FlightGroup.lua | 24 ++++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index a84d7c124..81cc0f89a 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -216,7 +216,7 @@ FLIGHTGROUP.Players={} --- FLIGHTGROUP class version. -- @field #string version -FLIGHTGROUP.version="0.8.4" +FLIGHTGROUP.version="0.8.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -4152,13 +4152,21 @@ end function FLIGHTGROUP:GetClosestAirbase() local group=self.group --Wrapper.Group#GROUP - - local coord=group:GetCoordinate() - local coalition=self:GetCoalition() - - local airbase=coord:GetClosestAirbase() --(nil, coalition) - - return airbase + + if group and group:IsAlive() then + + local coord=group:GetCoordinate() + local coalition=self:GetCoalition() + + local airbase=coord:GetClosestAirbase() --(nil, coalition) + + return airbase + + else + + return nil + + end end --- Search unoccupied parking spots at the airbase for all flight elements. From dcacba6803c602979805de3f8123e6b72e0c387b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 19 Jan 2023 17:03:07 +0100 Subject: [PATCH 190/603] F --- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 81cc0f89a..f89d142c7 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -216,7 +216,7 @@ FLIGHTGROUP.Players={} --- FLIGHTGROUP class version. -- @field #string version -FLIGHTGROUP.version="0.8.5" +FLIGHTGROUP.version="0.8.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list From 23553f0f4c98d03dd8e3ecd4e8f193d120b71200 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 21 Jan 2023 15:40:18 +0100 Subject: [PATCH 191/603] #AUFTRAG * Added checking of bespoke conditions within the status call of AUFTRAG and not only when AUFTRAG is done already --- Moose Development/Moose/Ops/Auftrag.lua | 6317 +---------------------- 1 file changed, 29 insertions(+), 6288 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 5fec50121..b85fa6182 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -16,6289 +16,7 @@ -- -- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Auftrag). -- --- ===--- **Ops** - Auftrag (mission) for Ops. --- --- ## Main Features: --- --- * Simplifies defining and executing DCS tasks --- * Additional useful events --- * Set mission start/stop times --- * Set mission priority and urgency (can cancel running missions) --- * Specific mission options for ROE, ROT, formation, etc. --- * Compatible with OPS classes like FLIGHTGROUP, NAVYGROUP, ARMYGROUP, AIRWING, etc. --- * FSM events when a mission is done, successful or failed --- -- === --- --- ## Example Missions: --- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Auftrag). --- --- === --- --- ### Author: **funkyfranky** --- --- === --- @module Ops.Auftrag --- @image OPS_Auftrag.png - - ---- AUFTRAG class. --- @type AUFTRAG --- @field #string ClassName Name of the class. --- @field #number verbose Verbosity level. --- @field #string lid Class id string for output to DCS log file. --- @field #number auftragsnummer Auftragsnummer. --- @field #string type Mission type. --- @field #table categories Mission categories. --- @field #string status Mission status. --- @field #table legions Assigned legions. --- @field #table statusLegion Mission status of all assigned LEGIONs. --- @field #string statusCommander Mission status of the COMMANDER. --- @field #string statusChief Mission status of the CHIEF. --- @field #table groupdata Group specific data. --- @field #string name Mission name. --- @field #number prio Mission priority. --- @field #boolean urgent Mission is urgent. Running missions with lower prio might be cancelled. --- @field #number importance Importance. --- @field #number Tstart Mission start time in abs. seconds. --- @field #number Tstop Mission stop time in abs. seconds. --- @field #number duration Mission duration in seconds. --- @field #number durationExe Mission execution time in seconds. --- @field #number Texecuting Time stamp (abs) when mission is executing. Is `#nil` on start. --- @field #number Tpush Mission push/execute time in abs. seconds. --- @field #number Tstarted Time stamp (abs) when mission is started. --- @field Wrapper.Marker#MARKER marker F10 map marker. --- @field #boolean markerOn If true, display marker on F10 map with the AUFTRAG status. --- @field #number markerCoaliton Coalition to which the marker is dispayed. --- @field #table DCStask DCS task structure. --- @field #number Ncasualties Number of own casualties during mission. --- @field #number Nkills Number of (enemy) units killed by assets of this mission. --- @field #number Nelements Number of elements (units) assigned to mission. --- @field #number dTevaluate Time interval in seconds before the mission result is evaluated after mission is over. --- @field #number Tover Mission abs. time stamp, when mission was over. --- @field #boolean updateDCSTask If `true`, DCS task is updated at every status update of the assigned groups. --- @field #table conditionStart Condition(s) that have to be true, before the mission will be started. --- @field #table conditionSuccess If all conditions are true, the mission is cancelled. --- @field #table conditionFailure If all conditions are true, the mission is cancelled. --- @field #table conditionPush If all conditions are true, the mission is executed. Before, the group(s) wait at the mission execution waypoint. --- --- @field #number orbitSpeed Orbit speed in m/s. --- @field #number orbitAltitude Orbit altitude in meters. --- @field #number orbitHeading Orbit heading in degrees. --- @field #number orbitLeg Length of orbit leg in meters. --- @field DCS#Vec2 orbitOffsetVec2 2D offset vector. --- @field DCS#Vec2 orbitVec2 2D orbit vector. --- @field #number orbitDeltaR Distance threshold in meters for moving orbit targets. --- --- @field Ops.Target#TARGET engageTarget Target data to engage. --- @field #number targetHeading Heading of target in degrees. --- --- @field Ops.Operation#OPERATION operation Operation this mission is part of. --- --- @field #boolean teleport Groups are teleported to the mission ingress waypoint. --- --- @field Core.Zone#ZONE_RADIUS engageZone *Circular* engagement zone. --- @field #table engageTargetTypes Table of target types that are engaged in the engagement zone. --- @field #number engageAltitude Engagement altitude in meters. --- @field #number engageDirection Engagement direction in degrees. --- @field #number engageQuantity Number of times a target is engaged. --- @field #number engageWeaponType Weapon type used. --- @field #number engageWeaponExpend How many weapons are used. --- @field #boolean engageAsGroup Group attack. --- @field #number engageMaxDistance Max engage distance. --- @field #number refuelSystem Refuel type (boom or probe) for TANKER missions. --- --- @field Wrapper.Group#GROUP escortGroup The group to be escorted. --- @field DCS#Vec3 escortVec3 The 3D offset vector from the escorted group to the escort group. --- --- @field #number facDesignation FAC designation type. --- @field #boolean facDatalink FAC datalink enabled. --- @field #number facFreq FAC radio frequency in MHz. --- @field #number facModu FAC radio modulation 0=AM 1=FM. --- --- @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. --- @field #number NcarriersMax Max number of required carrier assets. --- @field Core.Zone#ZONE transportDeployZone Deploy zone of an OPSTRANSPORT. --- @field Core.Zone#ZONE transportDisembarkZone Disembark zone of an OPSTRANSPORT. --- --- @field #number artyRadius Radius in meters. --- @field #number artyShots Number of shots fired. --- @field #number artyAltitude Altitude in meters. Can be used for a Barrage. --- @field #number artyHeading Heading in degrees (for Barrage). --- @field #number artyAngle Shooting angle in degrees (for Barrage). --- --- @field #string alert5MissionType Alert 5 mission type. This is the mission type, the alerted assets will be able to carry out. --- --- @field #table attributes Generalized attribute(s) of assets. --- @field #table properties DCS attribute(s) of assets. --- --- @field Ops.Chief#CHIEF chief The CHIEF managing this mission. --- @field Ops.Commander#COMMANDER commander The COMMANDER managing this mission. --- @field #table assets Warehouse assets assigned for this mission. --- @field #number NassetsMin Min. number of required warehouse assets. --- @field #number NassetsMax Max. number of required warehouse assets. --- @field #number NescortMin Min. number of required escort assets for each group the mission is assigned to. --- @field #number NescortMax Max. number of required escort assets for each group the mission is assigned to. --- @field #string escortMissionType Escort mission type. --- @field #table escortTargetTypes Target types that will be engaged. --- @field #number escortEngageRange Engage range in nautical miles (NM). --- @field #number Nassets Number of requested warehouse assets. --- @field #table NassetsLegMin Number of required warehouse assets for each assigned legion. --- @field #table NassetsLegMax Number of required warehouse assets for each assigned legion. --- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the mission was cancelled before the request is processed. --- @field #table payloads User specified airwing payloads for this mission. Only these will be considered for the job! --- @field Ops.AirWing#AIRWING.PatrolData patroldata Patrol data. --- --- @field #table specialLegions User specified legions assigned for this mission. Only these will be considered for the job! --- @field #table specialCohorts User specified cohorts assigned for this mission. Only these will be considered for the job! --- @field #table transportLegions Legions explicitly requested for providing transport carrier assets. --- @field #table transportCohorts Cohorts explicitly requested for providing transport carrier assets. --- @field #table escortLegions Legions explicitly requested for providing escorting assets. --- @field #table escortCohorts Cohorts explicitly requested for providing escorting assets. --- --- @field #string missionTask Mission task. See `ENUMS.MissionTask`. --- @field #number missionAltitude Mission altitude in meters. --- @field #number missionSpeed Mission speed in km/h. --- @field #number missionFraction Mission coordiante fraction. Default is 0.5. --- @field #number missionRange Mission range in meters. Used by LEGION classes (AIRWING, BRIGADE, ...). --- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate. --- @field Core.Point#COORDINATE missionEgressCoord Mission egress waypoint coordinate. --- @field #number missionWaypointRadius Random radius in meters. --- --- @field #table enrouteTasks Mission enroute tasks. --- --- @field #number repeated Number of times mission was repeated. --- @field #number repeatedSuccess Number of times mission was repeated after a success. --- @field #number repeatedFailure Number of times mission was repeated after a failure. --- @field #number Nrepeat Number of times the mission is repeated. --- @field #number NrepeatFailure Number of times mission is repeated if failed. --- @field #number NrepeatSuccess Number of times mission is repeated if successful. --- --- @field Ops.OpsGroup#OPSGROUP.Radio radio Radio freq and modulation. --- @field Ops.OpsGroup#OPSGROUP.Beacon tacan TACAN setting. --- @field Ops.OpsGroup#OPSGROUP.Beacon icls ICLS setting. --- --- @field #number optionROE ROE. --- @field #number optionROT ROT. --- @field #number optionAlarm Alarm state. --- @field #number optionFormation Formation. --- @field #boolean optionEPLRS EPLRS datalink. --- @field #number optionCM Counter measures. --- @field #number optionRTBammo RTB on out-of-ammo. --- @field #number optionRTBfuel RTB on out-of-fuel. --- @field #number optionECM ECM. --- @field #boolean optionEmission Emission is on or off. --- @field #boolean optionInvisible Invisible is on/off. --- @field #boolean optionImmortal Immortal is on/off. --- --- @extends Core.Fsm#FSM - ---- *A warrior's mission is to foster the success of others.* -- Morihei Ueshiba --- --- === --- --- # The AUFTRAG Concept --- --- The AUFTRAG class significantly simplifies the workflow of using DCS tasks. --- --- You can think of an AUFTRAG as document, which contains the mission briefing, i.e. information about the target location, mission altitude, speed and various other parameters. --- This document can be handed over directly to a pilot (or multiple pilots) via the @{Ops.FlightGroup#FLIGHTGROUP} class. The pilots will then execute the mission. --- --- The AUFTRAG document can also be given to an AIRWING. The airwing will then determine the best assets (pilots and payloads) available for the job. --- --- Similarly, an AUFTRAG can be given to ground or navel groups via the @{Ops.ArmyGroup#ARMYGROUP} or @{Ops.NavyGroup#NAVYGROUP} classes, respectively. These classes have also --- AIRWING analouges, which are called BRIGADE and FLEET. Brigades and fleets will likewise select the best assets they have available and pass on the AUFTRAG to them. --- --- --- One more up the food chain, an AUFTRAG can be passed to a COMMANDER. The commander will recruit the best assets of AIRWINGs, BRIGADEs and/or FLEETs and pass the job over to it. --- --- --- # Airborne Missions --- --- Several mission types are supported by this class. --- --- ## Anti-Ship --- --- An anti-ship mission can be created with the @{#AUFTRAG.NewANTISHIP}() function. --- --- ## AWACS --- --- An AWACS mission can be created with the @{#AUFTRAG.NewAWACS}() function. --- --- ## BAI --- --- A BAI mission can be created with the @{#AUFTRAG.NewBAI}() function. --- --- ## Bombing --- --- A bombing mission can be created with the @{#AUFTRAG.NewBOMBING}() function. --- --- ## Bombing Runway --- --- A bombing runway mission can be created with the @{#AUFTRAG.NewBOMBRUNWAY}() function. --- --- ## Bombing Carpet --- --- A carpet bombing mission can be created with the @{#AUFTRAG.NewBOMBCARPET}() function. --- --- ## CAP --- --- A CAP mission can be created with the @{#AUFTRAG.NewCAP}() function. --- --- ## CAS --- --- A CAS mission can be created with the @{#AUFTRAG.NewCAS}() function. --- --- ## Escort --- --- An escort mission can be created with the @{#AUFTRAG.NewESCORT}() function. --- --- ## FACA --- --- An FACA mission can be created with the @{#AUFTRAG.NewFACA}() function. --- --- ## Ferry --- --- Not implemented yet. --- --- ## Intercept --- --- An intercept mission can be created with the @{#AUFTRAG.NewINTERCEPT}() function. --- --- ## Orbit --- --- An orbit mission can be created with the @{#AUFTRAG.NewORBIT}() function. --- --- ## GCICAP --- --- An patrol mission can be created with the @{#AUFTRAG.NewGCICAP}() function. --- --- ## RECON --- --- An reconnaissance mission can be created with the @{#AUFTRAG.NewRECON}() function. --- --- ## RESCUE HELO --- --- An rescue helo mission can be created with the @{#AUFTRAG.NewRESCUEHELO}() function. --- --- ## SEAD --- --- An SEAD mission can be created with the @{#AUFTRAG.NewSEAD}() function. --- --- ## STRIKE --- --- An strike mission can be created with the @{#AUFTRAG.NewSTRIKE}() function. --- --- ## Tanker --- --- A refueling tanker mission can be created with the @{#AUFTRAG.NewTANKER}() function. --- --- ## TROOPTRANSPORT --- --- A troop transport mission can be created with the @{#AUFTRAG.NewTROOPTRANSPORT}() function. --- --- ## CARGOTRANSPORT --- --- A cargo transport mission can be created with the @{#AUFTRAG.NewCARGOTRANSPORT}() function. --- --- ## HOVER --- --- A mission for a helicoptre or VSTOL plane to Hover at a point for a certain amount of time can be created with the @{#AUFTRAG.NewHOVER}() function. --- --- # Ground Missions --- --- ## ARTY --- --- An arty mission can be created with the @{#AUFTRAG.NewARTY}() function. --- --- ## GROUNDATTACK --- --- A ground attack mission can be created with the @{#AUFTRAG.NewGROUNDATTACK}() function. --- --- # Assigning Missions --- --- An AUFTRAG can be assigned to groups (FLIGHTGROUP, ARMYGROUP, NAVYGROUP), legions (AIRWING, BRIGADE, FLEET) or to a COMMANDER. --- --- ## Group Level --- --- ### Flight Group --- --- Assigning an AUFTRAG to a flight group is done via the @{Ops.FlightGroup#FLIGHTGROUP.AddMission} function. See FLIGHTGROUP docs for details. --- --- ### Army Group --- --- Assigning an AUFTRAG to an army group is done via the @{Ops.ArmyGroup#ARMYGROUP.AddMission} function. See ARMYGROUP docs for details. --- --- ### Navy Group --- --- Assigning an AUFTRAG to a navy group is done via the @{Ops.NavyGroup#NAVYGROUP.AddMission} function. See NAVYGROUP docs for details. --- --- ## Legion Level --- --- Adding an AUFTRAG to an airwing is done via the @{Ops.AirWing#AIRWING.AddMission} function. See AIRWING docs for further details. --- Similarly, an AUFTRAG can be added to a brigade via the @{Ops.Brigade#BRIGADE.AddMission} function. --- --- ## Commander Level --- --- Assigning an AUFTRAG to a commander is done via the @{Ops.Commander#COMMANDER.AddMission} function. --- The commander will select the best assets available from all the legions under his command. See COMMANDER docs for details. --- --- ## Chief Level --- --- Assigning an AUFTRAG to a commander is done via the @{Ops.Chief#CHIEF.AddMission} function. The chief will simply pass on the mission to his/her commander. --- --- # Transportation --- --- TODO --- --- --- # Events --- --- The AUFTRAG class creates many useful (FSM) events, which can be used in the mission designers script. --- --- TODO --- --- --- # Examples --- --- TODO --- --- --- @field #AUFTRAG -AUFTRAG = { - ClassName = "AUFTRAG", - verbose = 0, - lid = nil, - auftragsnummer = nil, - groupdata = {}, - legions = {}, - statusLegion = {}, - requestID = {}, - assets = {}, - NassetsLegMin = {}, - NassetsLegMax = {}, - missionFraction = 0.5, - enrouteTasks = {}, - marker = nil, - markerOn = nil, - markerCoalition = nil, - conditionStart = {}, - conditionSuccess = {}, - conditionFailure = {}, - conditionPush = {}, -} - ---- Global mission counter. -_AUFTRAGSNR=0 - - ---- Mission types. --- @type AUFTRAG.Type --- @field #string ANTISHIP Anti-ship mission. --- @field #string AWACS AWACS mission. --- @field #string BAI Battlefield Air Interdiction. --- @field #string BOMBING Bombing mission. --- @field #string BOMBRUNWAY Bomb runway of an airbase. --- @field #string BOMBCARPET Carpet bombing. --- @field #string CAP Combat Air Patrol. --- @field #string CAS Close Air Support. --- @field #string ESCORT Escort mission. --- @field #string FACA Forward AirController airborne mission. --- @field #string FERRY Ferry mission. --- @field #string INTERCEPT Intercept mission. --- @field #string ORBIT Orbit mission. --- @field #string GCICAP Similar to CAP but no auto engage targets. --- @field #string RECON Recon mission. --- @field #string RECOVERYTANKER Recovery tanker mission. Not implemented yet. --- @field #string RESCUEHELO Rescue helo. --- @field #string SEAD Suppression/destruction of enemy air defences. --- @field #string STRIKE Strike mission. --- @field #string TANKER Tanker mission. --- @field #string TROOPTRANSPORT Troop transport mission. --- @field #string ARTY Fire at point. --- @field #string PATROLZONE Patrol a zone. --- @field #string OPSTRANSPORT Ops transport. --- @field #string AMMOSUPPLY Ammo supply. --- @field #string FUELSUPPLY Fuel supply. --- @field #string ALERT5 Alert 5. --- @field #string ONGUARD On guard. --- @field #string ARMOREDGUARD On guard - with armored groups. --- @field #string BARRAGE Barrage. --- @field #string ARMORATTACK Armor attack. --- @field #string CASENHANCED Enhanced CAS. --- @field #string HOVER Hover. --- @field #string GROUNDATTACK Ground attack. --- @field #string CARGOTRANSPORT Cargo transport. --- @field #string RELOCATECOHORT Relocate a cohort from one legion to another. --- @field #string AIRDEFENSE Air defense. --- @field #string EWR Early Warning Radar. --- @field #string RECOVERYTANKER Recovery tanker. --- @filed #string REARMING Rearming mission. --- @field #string NOTHING Nothing. -AUFTRAG.Type={ - ANTISHIP="Anti Ship", - AWACS="AWACS", - BAI="BAI", - BOMBING="Bombing", - BOMBRUNWAY="Bomb Runway", - BOMBCARPET="Carpet Bombing", - CAP="CAP", - CAS="CAS", - ESCORT="Escort", - FACA="FAC-A", - FERRY="Ferry Flight", - INTERCEPT="Intercept", - ORBIT="Orbit", - GCICAP="Ground Controlled CAP", - RECON="Recon", - RECOVERYTANKER="Recovery Tanker", - RESCUEHELO="Rescue Helo", - SEAD="SEAD", - STRIKE="Strike", - TANKER="Tanker", - TROOPTRANSPORT="Troop Transport", - ARTY="Fire At Point", - PATROLZONE="Patrol Zone", - OPSTRANSPORT="Ops Transport", - AMMOSUPPLY="Ammo Supply", - FUELSUPPLY="Fuel Supply", - ALERT5="Alert5", - ONGUARD="On Guard", - ARMOREDGUARD="Armored Guard", - BARRAGE="Barrage", - ARMORATTACK="Armor Attack", - CASENHANCED="CAS Enhanced", - HOVER="Hover", - GROUNDATTACK="Ground Attack", - CARGOTRANSPORT="Cargo Transport", - RELOCATECOHORT="Relocate Cohort", - AIRDEFENSE="Air Defence", - EWR="Early Warning Radar", - RECOVERYTANKER="Recovery Tanker", - REARMING="Rearming", - NOTHING="Nothing", -} - ---- Special task description. --- @type AUFTRAG.SpecialTask --- @field #string FORMATION AI formation task. --- @field #string PATROLZONE Patrol zone task. --- @field #string RECON Recon task. --- @field #string AMMOSUPPLY Ammo Supply. --- @field #string FUELSUPPLY Fuel Supply. --- @field #string ALERT5 Alert 5 task. --- @field #string ONGUARD On guard. --- @field #string ARMOREDGUARD On guard with armor. --- @field #string BARRAGE Barrage. --- @field #string HOVER Hover. --- @field #string GROUNDATTACK Ground attack. --- @field #string FERRY Ferry mission. --- @field #string RELOCATECOHORT Relocate cohort. --- @field #string AIRDEFENSE Air defense. --- @field #string EWR Early Warning Radar. --- @field #string RECOVERYTANKER Recovery tanker. --- @field #string REARMING Rearming. --- @field #string NOTHING Nothing. -AUFTRAG.SpecialTask={ - FORMATION="Formation", - PATROLZONE="PatrolZone", - RECON="ReconMission", - AMMOSUPPLY="Ammo Supply", - FUELSUPPLY="Fuel Supply", - ALERT5="Alert5", - ONGUARD="On Guard", - ARMOREDGUARD="ArmoredGuard", - BARRAGE="Barrage", - ARMORATTACK="AmorAttack", - HOVER="Hover", - GROUNDATTACK="Ground Attack", - FERRY="Ferry", - RELOCATECOHORT="Relocate Cohort", - AIRDEFENSE="Air Defense", - EWR="Early Warning Radar", - RECOVERYTANKER="Recovery Tanker", - REARMING="Rearming", - NOTHING="Nothing", -} - ---- Mission status. --- @type AUFTRAG.Status --- @field #string PLANNED Mission is at the early planning stage and has not been added to any queue. --- @field #string QUEUED Mission is queued at a LEGION. --- @field #string REQUESTED Mission assets were requested from the warehouse. --- @field #string SCHEDULED Mission is scheduled in an OPSGROUP queue waiting to be started. --- @field #string STARTED Mission has started but is not executed yet. --- @field #string EXECUTING Mission is being executed. --- @field #string DONE Mission is over. --- @field #string CANCELLED Mission was cancelled. --- @field #string SUCCESS Mission was a success. --- @field #string FAILED Mission failed. -AUFTRAG.Status={ - PLANNED="planned", - QUEUED="queued", - REQUESTED="requested", - SCHEDULED="scheduled", - STARTED="started", - EXECUTING="executing", - DONE="done", - CANCELLED="cancelled", - SUCCESS="success", - FAILED="failed", -} - ---- Mission status of an assigned group. --- @type AUFTRAG.GroupStatus --- @field #string SCHEDULED Mission is scheduled in a FLIGHGROUP queue waiting to be started. --- @field #string STARTED Ops group started this mission but it is not executed yet. --- @field #string EXECUTING Ops group is executing this mission. --- @field #string PAUSED Ops group has paused this mission, e.g. for refuelling. --- @field #string DONE Mission task of the Ops group is done. --- @field #string CANCELLED Mission was cancelled. -AUFTRAG.GroupStatus={ - SCHEDULED="scheduled", - STARTED="started", - EXECUTING="executing", - PAUSED="paused", - DONE="done", - CANCELLED="cancelled", -} - ---- Target type. --- @type AUFTRAG.TargetType --- @field #string GROUP Target is a GROUP object. --- @field #string UNIT Target is a UNIT object. --- @field #string STATIC Target is a STATIC object. --- @field #string COORDINATE Target is a COORDINATE. --- @field #string AIRBASE Target is an AIRBASE. --- @field #string SETGROUP Target is a SET of GROUPs. --- @field #string SETUNIT Target is a SET of UNITs. -AUFTRAG.TargetType={ - GROUP="Group", - UNIT="Unit", - STATIC="Static", - COORDINATE="Coordinate", - AIRBASE="Airbase", - SETGROUP="SetGroup", - SETUNIT="SetUnit", -} - ---- Mission category. --- @type AUFTRAG.Category --- @field #string AIRCRAFT Airplanes and helicopters. --- @field #string AIRPLANE Airplanes. --- @field #string HELICOPTER Helicopter. --- @field #string GROUND Ground troops. --- @field #string NAVAL Naval grous. -AUFTRAG.Category={ - ALL="All", - AIRCRAFT="Aircraft", - AIRPLANE="Airplane", - HELICOPTER="Helicopter", - GROUND="Ground", - NAVAL="Naval", -} - ---- Target data. --- @type AUFTRAG.TargetData --- @field Wrapper.Positionable#POSITIONABLE Target Target Object. --- @field #string Type Target type: "Group", "Unit", "Static", "Coordinate", "Airbase", "SetGroup", "SetUnit". --- @field #string Name Target name. --- @field #number Ninital Number of initial targets. --- @field #number Lifepoints Total life points. --- @field #number Lifepoints0 Inital life points. - ---- Mission capability. --- @type AUFTRAG.Capability --- @field #string MissionType Type of mission. --- @field #number Performance Number describing the performance level. The higher the better. - ---- Mission success. --- @type AUFTRAG.Success --- @field #string SURVIVED Group did survive. --- @field #string ENGAGED Target was engaged. --- @field #string DAMAGED Target was damaged. --- @field #string DESTROYED Target was destroyed. - ---- Generic mission condition. --- @type AUFTRAG.Condition --- @field #function func Callback function to check for a condition. Should return a #boolean. --- @field #table arg Optional arguments passed to the condition callback function. - ---- Group specific data. Each ops group subscribed to this mission has different data for this. --- @type AUFTRAG.GroupData --- @field Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @field Core.Point#COORDINATE waypointcoordinate Ingress waypoint coordinate. --- @field #number waypointindex Mission (ingress) Waypoint UID. --- @field #number waypointEgressUID Egress Waypoint UID. --- @field Core.Point#COORDINATE wpegresscoordinate Egress waypoint coordinate. --- --- @field Ops.OpsGroup#OPSGROUP.Task waypointtask Waypoint task. --- @field #string status Group mission status. --- @field Functional.Warehouse#WAREHOUSE.Assetitem asset The warehouse asset. - - ---- AUFTRAG class version. --- @field #string version -AUFTRAG.version="0.9.7" - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO list -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- TODO: Replace engageRange by missionRange. Here and in other classes. CTRL+H is your friend! --- TODO: Mission success options damaged, destroyed. --- TODO: F10 marker to create new missions. --- DONE: Add orbit mission for moving anker points. --- DONE: Add recovery tanker mission for boat ops. --- DONE: Added auftrag category. --- DONE: Missions can be assigned to multiple legions. --- DONE: Option to assign a specific payload for the mission (requires an AIRWING). --- NOPE: Clone mission. How? Deepcopy? ==> Create a new auftrag. --- DONE: Recon mission. What input? Set of coordinates? --- DONE: Option to assign mission to specific squadrons (requires an AIRWING). --- DONE: Add mission start conditions. --- DONE: Add rescue helo mission for boat ops. --- DONE: Mission ROE and ROT. --- DONE: Mission frequency and TACAN. --- DONE: Mission formation, etc. --- DONE: FSM events. --- DONE: F10 marker functions that are updated on Status event. --- DONE: Evaluate mission result ==> SUCCESS/FAILURE --- DONE: NewAUTO() NewA2G NewA2A --- DONE: Transport mission. --- DONE: Set mission coalition, e.g. for F10 markers. Could be derived from target if target has a coalition. - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Constructor -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Create a new generic AUFTRAG object. --- @param #AUFTRAG self --- @param #string Type Mission type. --- @return #AUFTRAG self -function AUFTRAG:New(Type) - - -- Inherit everything from FSM class. - local self=BASE:Inherit(self, FSM:New()) -- #AUFTRAG - - -- Increase global counter. - _AUFTRAGSNR=_AUFTRAGSNR+1 - - -- Mission type. - self.type=Type - - -- Auftragsnummer. - self.auftragsnummer=_AUFTRAGSNR - - -- Log ID. - self:_SetLogID() - - -- State is planned. - self.status=AUFTRAG.Status.PLANNED - - -- Defaults . - self:SetName() - self:SetPriority() - self:SetTime() - self:SetRequiredAssets() - self.engageAsGroup=true - self.dTevaluate=5 - - -- Init counters and stuff. - self.repeated=0 - self.repeatedSuccess=0 - self.repeatedFailure=0 - self.Nrepeat=0 - self.NrepeatFailure=0 - self.NrepeatSuccess=0 - self.Ncasualties=0 - self.Nkills=0 - self.Nelements=0 - self.Ngroups=0 - self.Nassigned=nil - self.Ndead=0 - - -- FMS start state is PLANNED. - self:SetStartState(self.status) - - -- PLANNED --> (QUEUED) --> (REQUESTED) --> SCHEDULED --> STARTED --> EXECUTING --> DONE - self:AddTransition("*", "Planned", AUFTRAG.Status.PLANNED) -- Mission is in planning stage. Could be in the queue of a COMMANDER or CHIEF. - self:AddTransition(AUFTRAG.Status.PLANNED, "Queued", AUFTRAG.Status.QUEUED) -- Mission is in queue of a LEGION. - self:AddTransition(AUFTRAG.Status.QUEUED, "Requested", AUFTRAG.Status.REQUESTED) -- Mission assets have been requested from the warehouse. - self:AddTransition(AUFTRAG.Status.REQUESTED, "Scheduled", AUFTRAG.Status.SCHEDULED) -- Mission added to the first ops group queue. - - self:AddTransition(AUFTRAG.Status.PLANNED, "Scheduled", AUFTRAG.Status.SCHEDULED) -- From planned directly to scheduled. - - self:AddTransition(AUFTRAG.Status.SCHEDULED, "Started", AUFTRAG.Status.STARTED) -- First asset has started the mission. - self:AddTransition(AUFTRAG.Status.STARTED, "Executing", AUFTRAG.Status.EXECUTING) -- First asset is executing the mission. - - self:AddTransition("*", "Done", AUFTRAG.Status.DONE) -- All assets have reported that mission is done. - - self:AddTransition("*", "Cancel", AUFTRAG.Status.CANCELLED) -- Command to cancel the mission. - - self:AddTransition("*", "Success", AUFTRAG.Status.SUCCESS) - self:AddTransition("*", "Failed", AUFTRAG.Status.FAILED) - - self:AddTransition("*", "Status", "*") - self:AddTransition("*", "Stop", "*") - - self:AddTransition("*", "Repeat", AUFTRAG.Status.PLANNED) - - self:AddTransition("*", "ElementDestroyed", "*") - self:AddTransition("*", "GroupDead", "*") - self:AddTransition("*", "AssetDead", "*") - - ------------------------ - --- Pseudo Functions --- - ------------------------ - - --- Triggers the FSM event "Status". - -- @function [parent=#AUFTRAG] Status - -- @param #AUFTRAG self - - --- Triggers the FSM event "Status" after a delay. - -- @function [parent=#AUFTRAG] __Status - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- Triggers the FSM event "Stop". - -- @function [parent=#AUFTRAG] Stop - -- @param #AUFTRAG self - - --- Triggers the FSM event "Stop" after a delay. - -- @function [parent=#AUFTRAG] __Stop - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - - --- Triggers the FSM event "Planned". - -- @function [parent=#AUFTRAG] Planned - -- @param #AUFTRAG self - - --- Triggers the FSM event "Planned" after a delay. - -- @function [parent=#AUFTRAG] __Planned - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Planned" event. - -- @function [parent=#AUFTRAG] OnAfterPlanned - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- Triggers the FSM event "Queued". - -- @function [parent=#AUFTRAG] Queued - -- @param #AUFTRAG self - - --- Triggers the FSM event "Queued" after a delay. - -- @function [parent=#AUFTRAG] __Queued - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Queued" event. - -- @function [parent=#AUFTRAG] OnAfterQueued - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- Triggers the FSM event "Requested". - -- @function [parent=#AUFTRAG] Requested - -- @param #AUFTRAG self - - --- Triggers the FSM event "Requested" after a delay. - -- @function [parent=#AUFTRAG] __Requested - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Requested" event. - -- @function [parent=#AUFTRAG] OnAfterRequested - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- Triggers the FSM event "Scheduled". - -- @function [parent=#AUFTRAG] Scheduled - -- @param #AUFTRAG self - - --- Triggers the FSM event "Scheduled" after a delay. - -- @function [parent=#AUFTRAG] __Scheduled - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Scheduled" event. - -- @function [parent=#AUFTRAG] OnAfterScheduled - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- Triggers the FSM event "Started". - -- @function [parent=#AUFTRAG] Started - -- @param #AUFTRAG self - - --- Triggers the FSM event "Started" after a delay. - -- @function [parent=#AUFTRAG] __Started - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Started" event. - -- @function [parent=#AUFTRAG] OnAfterStarted - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- Triggers the FSM event "Executing". - -- @function [parent=#AUFTRAG] Executing - -- @param #AUFTRAG self - - --- Triggers the FSM event "Executing" after a delay. - -- @function [parent=#AUFTRAG] __Executing - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Executing" event. - -- @function [parent=#AUFTRAG] OnAfterExecuting - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- Triggers the FSM event "Cancel". - -- @function [parent=#AUFTRAG] Cancel - -- @param #AUFTRAG self - - --- Triggers the FSM event "Cancel" after a delay. - -- @function [parent=#AUFTRAG] __Cancel - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Cancel" event. - -- @function [parent=#AUFTRAG] OnAfterCancel - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- Triggers the FSM event "Done". - -- @function [parent=#AUFTRAG] Done - -- @param #AUFTRAG self - - --- Triggers the FSM event "Done" after a delay. - -- @function [parent=#AUFTRAG] __Done - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Done" event. - -- @function [parent=#AUFTRAG] OnAfterDone - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- Triggers the FSM event "Success". - -- @function [parent=#AUFTRAG] Success - -- @param #AUFTRAG self - - --- Triggers the FSM event "Success" after a delay. - -- @function [parent=#AUFTRAG] __Success - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Success" event. - -- @function [parent=#AUFTRAG] OnAfterSuccess - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - --- Triggers the FSM event "Failed". - -- @function [parent=#AUFTRAG] Failed - -- @param #AUFTRAG self - - --- Triggers the FSM event "Failed" after a delay. - -- @function [parent=#AUFTRAG] __Failed - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Failed" event. - -- @function [parent=#AUFTRAG] OnAfterFailed - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - --- Triggers the FSM event "Repeat". - -- @function [parent=#AUFTRAG] Repeat - -- @param #AUFTRAG self - - --- Triggers the FSM event "Repeat" after a delay. - -- @function [parent=#AUFTRAG] __Repeat - -- @param #AUFTRAG self - -- @param #number delay Delay in seconds. - - --- On after "Repeat" event. - -- @function [parent=#AUFTRAG] OnAfterRepeat - -- @param #AUFTRAG self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - -- Init status update. - self:__Status(-1) - - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Create Missions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- **[AIR]** Create an ANTI-SHIP mission. --- @param #AUFTRAG self --- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be passed as a @{Wrapper.Group#GROUP} or @{Wrapper.Unit#UNIT} object. --- @param #number Altitude Engage altitude in feet. Default 2000 ft. --- @return #AUFTRAG self -function AUFTRAG:NewANTISHIP(Target, Altitude) - - local mission=AUFTRAG:New(AUFTRAG.Type.ANTISHIP) - - mission:_TargetFromObject(Target) - - -- DCS task parameters: - mission.engageWeaponType=ENUMS.WeaponFlag.Auto - mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL - mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000) - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.ANTISHIPSTRIKE - mission.missionAltitude=mission.engageAltitude - mission.missionFraction=0.4 - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.EvadeFire - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR ROTARY]** Create an HOVER mission. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Where to hover. --- @param #number Altitude Hover altitude in feet AGL. Default is 50 feet above ground. --- @param #number Time Time in seconds to hold the hover. Default 300 seconds. --- @param #number Speed Speed in knots to fly to the target coordinate. Default 150kn. --- @param #number MissionAlt Altitide to fly towards the mission in feet AGL. Default 1000ft. --- @return #AUFTRAG self -function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt) - - local mission=AUFTRAG:New(AUFTRAG.Type.HOVER) - - -- Altitude. - if Altitude then - mission.hoverAltitude=Coordinate:GetLandHeight()+UTILS.FeetToMeters(Altitude) - else - mission.hoverAltitude=Coordinate:GetLandHeight()+UTILS.FeetToMeters(50) - end - - mission:_TargetFromObject(Coordinate) - - mission.hoverSpeed = 0.1 -- the DCS Task itself will shortly be build with this so MPS - mission.hoverTime = Time or 300 - self:SetMissionSpeed(Speed or 150) - self:SetMissionAltitude(MissionAlt or 1000) - - -- Mission options: - mission.missionFraction=0.9 - mission.optionROE=ENUMS.ROE.ReturnFire - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.HELICOPTER} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create an ORBIT mission, which can be either a circular orbit or a race-track pattern. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Where to orbit. --- @param #number Altitude Orbit altitude in feet above sea level. Default is y component of `Coordinate`. --- @param #number Speed Orbit speed in knots. Default 350 KIAS. --- @param #number Heading Heading of race-track pattern in degrees. If not specified, a circular orbit is performed. --- @param #number Leg Length of race-track in NM. If not specified, a circular orbit is performed. --- @return #AUFTRAG self -function AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) - - local mission=AUFTRAG:New(AUFTRAG.Type.ORBIT) - - -- Target. - mission:_TargetFromObject(Coordinate) - - -- Set Altitude. - if Altitude then - mission.orbitAltitude=UTILS.FeetToMeters(Altitude) - else - mission.orbitAltitude=Coordinate.y - end - - -- Orbit speed in m/s. - mission.orbitSpeed = UTILS.KnotsToMps(UTILS.KnotsToAltKIAS(Speed or 350, UTILS.MetersToFeet(mission.orbitAltitude))) - - -- Mission speed in km/h. - mission.missionSpeed = UTILS.KnotsToKmph(Speed or 350) - - if Leg then - mission.orbitLeg=UTILS.NMToMeters(Leg) - - -- Relative heading - if Heading and Heading<0 then - mission.orbitHeadingRel=true - Heading=-Heading - end - - -- Heading if given. - mission.orbitHeading=Heading - end - - -- Mission options: - mission.missionAltitude=mission.orbitAltitude*0.9 - mission.missionFraction=0.9 - mission.optionROE=ENUMS.ROE.ReturnFire - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create an ORBIT mission, where the aircraft will go in a circle around the specified coordinate. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Position where to orbit around. --- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. --- @param #number Speed Orbit speed in knots. Default 350 KIAS. --- @return #AUFTRAG self -function AUFTRAG:NewORBIT_CIRCLE(Coordinate, Altitude, Speed) - - local mission=AUFTRAG:NewORBIT(Coordinate, Altitude, Speed) - - return mission -end - ---- **[AIR]** Create an ORBIT mission, where the aircraft will fly a race-track pattern. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Where to orbit. --- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. --- @param #number Speed Orbit speed in knots. Default 350 KIAS. --- @param #number Heading Heading of race-track pattern in degrees. Default random in [0, 360) degrees. --- @param #number Leg Length of race-track in NM. Default 10 NM. --- @return #AUFTRAG self -function AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - - Heading = Heading or math.random(360) - Leg = Leg or 10 - - local mission=AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) - - return mission -end - ---- **[AIR]** Create an ORBIT mission, where the aircraft will fly a circular or race-track pattern over a given group or unit. --- @param #AUFTRAG self --- @param Wrapper.Group#GROUP Group Group where to orbit around. Can also be a UNIT object. --- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft. --- @param #number Speed Orbit speed in knots. Default 350 KIAS. --- @param #number Leg Length of race-track in NM. Default nil. --- @param #number Heading Heading of race-track pattern in degrees. Default is heading of the group. --- @param DCS#Vec2 OffsetVec2 Offset 2D-vector {x=0, y=0} in NM with respect to the group. Default directly overhead. Can also be given in polar coordinates `{r=5, phi=45}`. --- @param #number Distance Threshold distance in NM before orbit pattern is updated. Default 5 NM. --- @return #AUFTRAG self -function AUFTRAG:NewORBIT_GROUP(Group, Altitude, Speed, Leg, Heading, OffsetVec2, Distance) - - -- Set default altitude. - Altitude = Altitude or 6000 - - -- Create orbit mission. - local mission=AUFTRAG:NewORBIT(Group, Altitude, Speed, Heading, Leg) - - -- DCS tasks needs to be updated from time to time. - mission.updateDCSTask=true - - -- Convert offset vector to meters. - if OffsetVec2 then - if OffsetVec2.x then - OffsetVec2.x=UTILS.NMToMeters(OffsetVec2.x) - end - if OffsetVec2.y then - OffsetVec2.y=UTILS.NMToMeters(OffsetVec2.y) - end - if OffsetVec2.r then - OffsetVec2.r=UTILS.NMToMeters(OffsetVec2.r) - end - end - - -- Offset vector. - mission.orbitOffsetVec2=OffsetVec2 - - -- Pattern update distance. - mission.orbitDeltaR=UTILS.NMToMeters(Distance or 5) - - -- Update task with offset etc. - mission:GetDCSMissionTask() - - return mission -end - - ---- **[AIR]** Create a Ground Controlled CAP (GCICAP) mission. Flights with this task are considered for A2A INTERCEPT missions by the CHIEF class. They will perform a compat air patrol but not engage by --- themselfs. They wait for the CHIEF to tell them whom to engage. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Where to orbit. --- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. --- @param #number Speed Orbit speed in knots. Default 350 kts. --- @param #number Heading Heading of race-track pattern in degrees. Default random in [0, 360) degrees. --- @param #number Leg Length of race-track in NM. Default 10 NM. --- @return #AUFTRAG self -function AUFTRAG:NewGCICAP(Coordinate, Altitude, Speed, Heading, Leg) - - -- Create ORBIT first. - local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - - -- Mission type GCICAP. - mission.type=AUFTRAG.Type.GCICAP - - mission:_SetLogID() - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.INTERCEPT - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - return mission -end - ---- **[AIR]** Create a TANKER mission. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Where to orbit. --- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. --- @param #number Speed Orbit speed in knots. Default 350 kts. --- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). --- @param #number Leg Length of race-track in NM. Default 10 NM. --- @param #number RefuelSystem Refueling system (0=boom, 1=probe). This info is *only* for AIRWINGs so they launch the right tanker type. --- @return #AUFTRAG self -function AUFTRAG:NewTANKER(Coordinate, Altitude, Speed, Heading, Leg, RefuelSystem) - - -- Create ORBIT first. - local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - - -- Mission type TANKER. - mission.type=AUFTRAG.Type.TANKER - - mission:_SetLogID() - - mission.refuelSystem=RefuelSystem - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.REFUELING - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create a AWACS mission. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Where to orbit. Altitude is also taken from the coordinate. --- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. --- @param #number Speed Orbit speed in knots. Default 350 kts. --- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). --- @param #number Leg Length of race-track in NM. Default 10 NM. --- @return #AUFTRAG self -function AUFTRAG:NewAWACS(Coordinate, Altitude, Speed, Heading, Leg) - - -- Create ORBIT first. - local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - - -- Mission type AWACS. - mission.type=AUFTRAG.Type.AWACS - - mission:_SetLogID() - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.AWACS - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - - - ---- **[AIR]** Create an INTERCEPT mission. --- @param #AUFTRAG self --- @param Wrapper.Positionable#POSITIONABLE Target The target to intercept. Can also be passed as simple @{Wrapper.Group#GROUP} or @{Wrapper.Unit#UNIT} object. --- @return #AUFTRAG self -function AUFTRAG:NewINTERCEPT(Target) - - local mission=AUFTRAG:New(AUFTRAG.Type.INTERCEPT) - - mission:_TargetFromObject(Target) - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.INTERCEPT - mission.missionFraction=0.1 - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.EvadeFire - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create a CAP mission. --- @param #AUFTRAG self --- @param Core.Zone#ZONE_RADIUS ZoneCAP Circular CAP zone. Detected targets in this zone will be engaged. --- @param #number Altitude Altitude at which to orbit in feet. Default is 10,000 ft. --- @param #number Speed Orbit speed in knots. Default 350 kts. --- @param Core.Point#COORDINATE Coordinate Where to orbit. Default is the center of the CAP zone. --- @param #number Heading Heading of race-track pattern in degrees. If not specified, a simple circular orbit is performed. --- @param #number Leg Length of race-track in NM. If not specified, a simple circular orbit is performed. --- @param #table TargetTypes Table of target types. Default {"Air"}. --- @return #AUFTRAG self -function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, TargetTypes) - - -- Ensure given TargetTypes parameter is a table. - if TargetTypes then - if type(TargetTypes)~="table" then - TargetTypes={TargetTypes} - end - end - - -- Create ORBIT first. - local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAP:GetCoordinate(), Altitude or 10000, Speed or 350, Heading, Leg) - - -- Mission type CAP. - mission.type=AUFTRAG.Type.CAP - mission:_SetLogID() - - -- DCS task parameters: - mission.engageZone=ZoneCAP - mission.engageTargetTypes=TargetTypes or {"Air"} - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.CAP - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.EvadeFire - mission.missionSpeed = UTILS.KnotsToKmph(UTILS.KnotsToAltKIAS(Speed or 350, Altitude)) - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create a CAP mission on a group. --- @param #AUFTRAG self --- @param Wrapper.Group#GROUP Grp. --- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft. --- @param #number Speed Orbit speed in knots. Default 250 KIAS. --- @param #number RelHeading Relative heading [0, 360) of race-track pattern in degrees wrt heading of the carrier. Default is heading of the carrier. --- @param #number Leg Length of race-track in NM. Default 14 NM. --- @param #number OffsetDist Relative distance of the first race-track point wrt to the carrier. Default 6 NM. --- @param #number OffsetAngle Relative angle of the first race-track point wrt. to the carrier. Default 180 (behind the boat). --- @param #number UpdateDistance Threshold distance in NM before orbit pattern is updated. Default 5 NM. --- @param #table TargetTypes (Optional) Table of target types. Default `{"Helicopters", "Ground Units", "Light armed ships"}`. --- @param #number EngageRange Max range in nautical miles that the escort group(s) will engage enemies. Default 32 NM (60 km). --- @return #AUFTRAG self -function AUFTRAG:NewCAPGROUP(Grp, Altitude, Speed, RelHeading, Leg, OffsetDist, OffsetAngle, UpdateDistance, TargetTypes, EngageRange) - - -- Ensure given TargetTypes parameter is a table. - if TargetTypes then - if type(TargetTypes)~="table" then - TargetTypes={TargetTypes} - end - end - -- Six NM astern. - local OffsetVec2={r=OffsetDist or 6, phi=OffsetAngle or 180} - - -- Default leg. - Leg=Leg or 14 - - local Heading=nil - if RelHeading then - Heading=-math.abs(RelHeading) - end - - -- Create orbit mission. - local mission=AUFTRAG:NewORBIT_GROUP(Grp, Altitude, Speed, Leg, Heading, OffsetVec2, UpdateDistance) - -- Mission type CAP. - mission.type=AUFTRAG.Type.CAP - mission:_SetLogID() - - -- DCS task parameters: - local engage = EngageRange or 32 - local zoneCAPGroup = ZONE_GROUP:New("CAPGroup", Grp, UTILS.NMToMeters(engage)) - mission.engageZone=zoneCAPGroup - mission.engageTargetTypes=TargetTypes or {"Air"} - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.CAP - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.EvadeFire - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create a CAS mission. --- @param #AUFTRAG self --- @param Core.Zone#ZONE_RADIUS ZoneCAS Circular CAS zone. Detected targets in this zone will be engaged. --- @param #number Altitude Altitude at which to orbit. Default is 10,000 ft. --- @param #number Speed Orbit speed in knots. Default 350 KIAS. --- @param Core.Point#COORDINATE Coordinate Where to orbit. Default is the center of the CAS zone. --- @param #number Heading Heading of race-track pattern in degrees. If not specified, a simple circular orbit is performed. --- @param #number Leg Length of race-track in NM. If not specified, a simple circular orbit is performed. --- @param #table TargetTypes (Optional) Table of target types. Default `{"Helicopters", "Ground Units", "Light armed ships"}`. --- @return #AUFTRAG self -function AUFTRAG:NewCAS(ZoneCAS, Altitude, Speed, Coordinate, Heading, Leg, TargetTypes) - - -- Ensure given TargetTypes parameter is a table. - if TargetTypes then - if type(TargetTypes)~="table" then - TargetTypes={TargetTypes} - end - end - - -- Create ORBIT first. - local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAS:GetCoordinate(), Altitude or 10000, Speed, Heading, Leg) - - -- Mission type CAS. - mission.type=AUFTRAG.Type.CAS - mission:_SetLogID() - - -- DCS Task options: - mission.engageZone=ZoneCAS - mission.engageTargetTypes=TargetTypes or {"Helicopters", "Ground Units", "Light armed ships"} - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.CAS - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.EvadeFire - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - 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 Core.Set#SET_ZONE NoEngageZoneSet Set of zones in which targets are *not* engaged. Default is nowhere. --- @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"}`. --- @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. --- @param #string Designation Designation of target. See `AI.Task.Designation`. Default `AI.Task.Designation.AUTO`. --- @param #boolean DataLink Enable data link. Default `true`. --- @param #number Frequency Radio frequency in MHz the FAC uses for communication. Default is 133 MHz. --- @param #number Modulation Radio modulation band. Default 0=AM. Use 1 for FM. See radio.modulation.AM or radio.modulaton.FM. --- @return #AUFTRAG self -function AUFTRAG:NewFACA(Target, Designation, DataLink, Frequency, Modulation) - - local mission=AUFTRAG:New(AUFTRAG.Type.FACA) - - mission:_TargetFromObject(Target) - - -- TODO: check that target is really a group object! - - -- DCS Task options: - mission.facDesignation=Designation --or AI.Task.Designation.AUTO - mission.facDatalink=true - mission.facFreq=Frequency or 133 - mission.facModu=Modulation or radio.modulation.AM - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.AFAC - mission.missionAltitude=nil - mission.missionFraction=0.5 - mission.optionROE=ENUMS.ROE.ReturnFire - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - - ---- **[AIR]** Create a BAI mission. --- @param #AUFTRAG self --- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. --- @param #number Altitude Engage altitude in feet. Default 5000 ft. --- @return #AUFTRAG self -function AUFTRAG:NewBAI(Target, Altitude) - - local mission=AUFTRAG:New(AUFTRAG.Type.BAI) - - mission:_TargetFromObject(Target) - - -- DCS Task options: - mission.engageWeaponType=ENUMS.WeaponFlag.Auto - mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL - mission.engageAltitude=UTILS.FeetToMeters(Altitude or 5000) - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.GROUNDATTACK - mission.missionAltitude=mission.engageAltitude - mission.missionFraction=0.75 - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create a SEAD mission. --- @param #AUFTRAG self --- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP or UNIT object. --- @param #number Altitude Engage altitude in feet. Default 25000 ft. --- @return #AUFTRAG self -function AUFTRAG:NewSEAD(Target, Altitude) - - local mission=AUFTRAG:New(AUFTRAG.Type.SEAD) - - mission:_TargetFromObject(Target) - - -- DCS Task options: - mission.engageWeaponType=ENUMS.WeaponFlag.Auto - mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL - mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000) - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.SEAD - mission.missionAltitude=mission.engageAltitude - mission.missionFraction=0.2 - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.EvadeFire - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create a STRIKE mission. Flight will attack the closest map object to the specified coordinate. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC or TARGET object. --- @param #number Altitude Engage altitude in feet. Default 2000 ft. --- @return #AUFTRAG self -function AUFTRAG:NewSTRIKE(Target, Altitude) - - local mission=AUFTRAG:New(AUFTRAG.Type.STRIKE) - - mission:_TargetFromObject(Target) - - -- DCS Task options: - mission.engageWeaponType=ENUMS.WeaponFlag.Auto - mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL - mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000) - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.GROUNDATTACK - mission.missionAltitude=mission.engageAltitude - mission.missionFraction=0.75 - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create a BOMBING mission. Flight will drop bombs a specified coordinate. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC or TARGET object. --- @param #number Altitude Engage altitude in feet. Default 25000 ft. --- @return #AUFTRAG self -function AUFTRAG:NewBOMBING(Target, Altitude) - - local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING) - - mission:_TargetFromObject(Target) - - -- DCS task options: - mission.engageWeaponType=ENUMS.WeaponFlag.Auto - mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL - mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000) - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.GROUNDATTACK - mission.missionAltitude=mission.engageAltitude*0.8 - mission.missionFraction=0.5 - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.NoReaction -- No reaction is better. - - -- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed. - mission.dTevaluate=5*60 - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - -- Get DCS task. - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create a BOMBRUNWAY mission. --- @param #AUFTRAG self --- @param Wrapper.Airbase#AIRBASE Airdrome The airbase to bomb. This must be an airdrome (not a FARP or ship) as these to not have a runway. --- @param #number Altitude Engage altitude in feet. Default 25000 ft. --- @return #AUFTRAG self -function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude) - - if type(Airdrome)=="string" then - Airdrome=AIRBASE:FindByName(Airdrome) - end - - local mission=AUFTRAG:New(AUFTRAG.Type.BOMBRUNWAY) - - mission:_TargetFromObject(Airdrome) - - -- DCS task options: - mission.engageWeaponType=ENUMS.WeaponFlag.Auto - mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL - mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000) - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.RUNWAYATTACK - mission.missionAltitude=mission.engageAltitude*0.8 - mission.missionFraction=0.75 - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.PassiveDefense - - -- Evaluate result after 5 min. - mission.dTevaluate=5*60 - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - -- Get DCS task. - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR]** Create a CARPET BOMBING mission. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT or STATIC object. --- @param #number Altitude Engage altitude in feet. Default 25000 ft. --- @param #number CarpetLength Length of bombing carpet in meters. Default 500 m. --- @return #AUFTRAG self -function AUFTRAG:NewBOMBCARPET(Target, Altitude, CarpetLength) - - local mission=AUFTRAG:New(AUFTRAG.Type.BOMBCARPET) - - mission:_TargetFromObject(Target) - - -- DCS task options: - mission.engageWeaponType=ENUMS.WeaponFlag.Auto - mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL - mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000) - mission.engageCarpetLength=CarpetLength or 500 - mission.engageAsGroup=false -- Looks like this must be false or the task is not executed. It is not available in the ME anyway but in the task of the mission file. - mission.engageDirection=nil -- This is also not available in the ME. - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.GROUNDATTACK - mission.missionAltitude=mission.engageAltitude*0.8 - mission.missionFraction=0.5 - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.NoReaction - - -- Evaluate result after 5 min. - mission.dTevaluate=5*60 - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - -- Get DCS task. - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - - ---- **[AIR]** Create an ESCORT (or FOLLOW) mission. Flight will escort another group and automatically engage certain target types. --- @param #AUFTRAG self --- @param Wrapper.Group#GROUP EscortGroup The group to escort. --- @param DCS#Vec3 OffsetVector A table with x, y and z components specifying the offset of the flight to the escorted group. Default {x=-100, y=0, z=200} for z=200 meters to the right, same alitude (y=0), x=-100 meters behind. --- @param #number EngageMaxDistance Max engage distance of targets in nautical miles. Default auto 32 NM. --- @param #table TargetTypes Types of targets to engage automatically. Default is {"Air"}, i.e. all enemy airborne units. Use an empty set {} for a simple "FOLLOW" mission. --- @return #AUFTRAG self -function AUFTRAG:NewESCORT(EscortGroup, OffsetVector, EngageMaxDistance, TargetTypes) - - local mission=AUFTRAG:New(AUFTRAG.Type.ESCORT) - - -- If only a string is passed we set a variable and check later if the group exists. - if type(EscortGroup)=="string" then - mission.escortGroupName=EscortGroup - mission:_TargetFromObject() - else - mission:_TargetFromObject(EscortGroup) - end - - -- DCS task parameters: - mission.escortVec3=OffsetVector or {x=-100, y=0, z=200} - mission.engageMaxDistance=EngageMaxDistance and UTILS.NMToMeters(EngageMaxDistance) or UTILS.NMToMeters(32) - mission.engageTargetTypes=TargetTypes or {"Air"} - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.ESCORT - mission.missionFraction=0.1 - mission.missionAltitude=1000 - mission.optionROE=ENUMS.ROE.OpenFire -- TODO: what's the best ROE here? Make dependent on ESCORT or FOLLOW! - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR ROTARY]** Create a RESCUE HELO mission. --- @param #AUFTRAG self --- @param Wrapper.Unit#UNIT Carrier The carrier unit. --- @return #AUFTRAG self -function AUFTRAG:NewRESCUEHELO(Carrier) - - local mission=AUFTRAG:New(AUFTRAG.Type.RESCUEHELO) - - mission:_TargetFromObject(Carrier) - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.NOTHING - mission.missionFraction=0.5 - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionROT=ENUMS.ROT.NoReaction - - mission.categories={AUFTRAG.Category.HELICOPTER} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIRPANE]** Create a RECOVERY TANKER mission. --- @param #AUFTRAG self --- @param Wrapper.Unit#UNIT Carrier The carrier unit. --- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft. --- @param #number Speed Orbit speed in knots. Default 250 KIAS. --- @param #number Leg Length of race-track in NM. Default 14 NM. --- @param #number RelHeading Relative heading [0, 360) of race-track pattern in degrees wrt heading of the carrier. Default is heading of the carrier. --- @param #number OffsetDist Relative distance of the first race-track point wrt to the carrier. Default 6 NM. --- @param #number OffsetAngle Relative angle of the first race-track point wrt. to the carrier. Default 180 (behind the boat). --- @param #number UpdateDistance Threshold distance in NM before orbit pattern is updated. Default 5 NM. --- @return #AUFTRAG self -function AUFTRAG:NewRECOVERYTANKER(Carrier, Altitude, Speed, Leg, RelHeading, OffsetDist, OffsetAngle, UpdateDistance) - - -- Six NM astern. - local OffsetVec2={r=OffsetDist or 6, phi=OffsetAngle or 180} - - -- Default leg. - Leg=Leg or 14 - - -- Default Speed. - Speed=Speed or 250 - - local Heading=nil - if RelHeading then - Heading=-math.abs(RelHeading) - end - - -- Create orbit mission. - local mission=AUFTRAG:NewORBIT_GROUP(Carrier, Altitude, Speed, Leg, Heading, OffsetVec2, UpdateDistance) - - -- Set the type. - mission.type=AUFTRAG.Type.RECOVERYTANKER - - -- Mission options: - mission.missionTask=ENUMS.MissionTask.REFUELING - mission.missionFraction=0.9 - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionROT=ENUMS.ROT.NoReaction - - mission.categories={AUFTRAG.Category.AIRPLANE} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - - ---- **[AIR ROTARY, GROUND]** Create a TROOP TRANSPORT mission. --- @param #AUFTRAG self --- @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 first transport group. --- @param #number PickupRadius Radius around the pickup coordinate in meters. Default 100 m. --- @return #AUFTRAG self -function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupCoordinate, PickupRadius) - - local mission=AUFTRAG:New(AUFTRAG.Type.TROOPTRANSPORT) - - if TransportGroupSet:IsInstanceOf("GROUP") then - mission.transportGroupSet=SET_GROUP:New() - mission.transportGroupSet:AddGroup(TransportGroupSet) - elseif TransportGroupSet:IsInstanceOf("SET_GROUP") then - mission.transportGroupSet=TransportGroupSet - else - mission:E(mission.lid.."ERROR: TransportGroupSet must be a GROUP or SET_GROUP object!") - return nil - end - - mission:_TargetFromObject(mission.transportGroupSet) - - 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 Transport") - --mission.transportDropoff:MarkToAll("Drop off") - - -- TODO: what's the best ROE here? - mission.optionROE=ENUMS.ROE.ReturnFire - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.HELICOPTER, AUFTRAG.Category.GROUND} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR ROTARY]** Create a CARGO TRANSPORT mission. --- **Important Note:** --- The dropoff zone has to be a zone defined in the Mission Editor. This is due to a restriction in the used DCS task, which takes the zone ID as input. --- Only ME zones have an ID that can be referenced. --- @param #AUFTRAG self --- @param Wrapper.Static#STATIC StaticCargo Static cargo object. --- @param Core.Zone#ZONE DropZone Zone where to drop off the cargo. **Has to be a zone defined in the ME!** --- @return #AUFTRAG self -function AUFTRAG:NewCARGOTRANSPORT(StaticCargo, DropZone) - - local mission=AUFTRAG:New(AUFTRAG.Type.CARGOTRANSPORT) - - mission:_TargetFromObject(StaticCargo) - - mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CARGOTRANSPORT) - - -- Set ROE and ROT. - mission.optionROE=ENUMS.ROE.ReturnFire - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.HELICOPTER} - - mission.DCStask=mission:GetDCSMissionTask() - - mission.DCStask.params.groupId=StaticCargo:GetID() - mission.DCStask.params.zoneId=DropZone.ZoneID - mission.DCStask.params.zone=DropZone - mission.DCStask.params.cargo=StaticCargo - - return mission -end - ---[[ - ---- **[AIR, GROUND, NAVAL]** Create a OPS TRANSPORT mission. --- @param #AUFTRAG self --- @param Core.Set#SET_GROUP CargoGroupSet The set group(s) to be transported. --- @param Core.Zone#ZONE PickupZone Pick up zone --- @param Core.Zone#ZONE DeployZone Deploy zone --- @return #AUFTRAG self -function AUFTRAG:NewOPSTRANSPORT(CargoGroupSet, PickupZone, DeployZone) - - local mission=AUFTRAG:New(AUFTRAG.Type.OPSTRANSPORT) - - mission.transportGroupSet=CargoGroupSet - - mission:_TargetFromObject(mission.transportGroupSet) - - mission.opstransport=OPSTRANSPORT:New(CargoGroupSet, PickupZone, DeployZone) - - function mission.opstransport:OnAfterExecuting(From, Event, To) - mission:Executing() - end - - function mission.opstransport:OnAfterDelivered(From, Event, To) - mission:Done() - end - - -- TODO: what's the best ROE here? - mission.optionROE=ENUMS.ROE.ReturnFire - mission.optionROT=ENUMS.ROT.PassiveDefense - - mission.categories={AUFTRAG.Category.ALL} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - -]] - ---- **[GROUND, NAVAL]** Create an ARTY mission. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Target Center of the firing solution. --- @param #number Nshots Number of shots to be fired. Default `#nil`. --- @param #number Radius Radius of the shells in meters. Default 100 meters. --- @param #number Altitude Altitude in meters. Can be used to setup a Barrage. Default `#nil`. --- @return #AUFTRAG self -function AUFTRAG:NewARTY(Target, Nshots, Radius, Altitude) - - local mission=AUFTRAG:New(AUFTRAG.Type.ARTY) - - mission:_TargetFromObject(Target) - - mission.artyShots=Nshots or nil - mission.artyRadius=Radius or 100 - mission.artyAltitude=Altitude - - mission.engageWeaponType=ENUMS.WeaponFlag.Auto - - mission.optionROE=ENUMS.ROE.OpenFire -- Ground/naval need open fire! - mission.optionAlarm=0 - - mission.missionFraction=0.0 - - -- Evaluate after 8 min. - mission.dTevaluate=8*60 - - mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[GROUND, NAVAL]** Create an BARRAGE mission. Assigned groups will move to a random coordinate within a given zone and start firing into the air. --- @param #AUFTRAG self --- @param Core.Zone#ZONE Zone The zone where the unit will go. --- @param #number Heading Heading in degrees. Default random heading [0, 360). --- @param #number Angle Shooting angle in degrees. Default random [45, 85]. --- @param #number Radius Radius of the shells in meters. Default 100 meters. --- @param #number Altitude Altitude in meters. Default 500 m. --- @param #number Nshots Number of shots to be fired. Default is until ammo is empty (`#nil`). --- @return #AUFTRAG self -function AUFTRAG:NewBARRAGE(Zone, Heading, Angle, Radius, Altitude, Nshots) - - local mission=AUFTRAG:New(AUFTRAG.Type.BARRAGE) - - mission:_TargetFromObject(Zone) - - mission.artyShots=Nshots - mission.artyRadius=Radius or 100 - mission.artyAltitude=Altitude - mission.artyHeading=Heading - mission.artyAngle=Angle - - mission.engageWeaponType=ENUMS.WeaponFlag.Auto - - mission.optionROE=ENUMS.ROE.OpenFire -- Ground/naval need open fire! - mission.optionAlarm=0 - - mission.missionFraction=0.0 - - -- Evaluate after instantly. - mission.dTevaluate=10 - - mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[AIR, GROUND, NAVAL]** Create a PATROLZONE mission. Group(s) will go to the zone and patrol it randomly. --- @param #AUFTRAG self --- @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 by ground units during patrol. Default "Off Road". --- @return #AUFTRAG self -function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude, Formation) - - local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE) - - -- Ensure we got a ZONE and not just the zone name. - if type(Zone)=="string" then - Zone=ZONE:New(Zone) - end - - mission:_TargetFromObject(Zone) - - mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.PATROLZONE) - - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionROT=ENUMS.ROT.PassiveDefense - mission.optionAlarm=ENUMS.AlarmState.Auto - - 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.ALL} - - mission.DCStask=mission:GetDCSMissionTask() - - mission.DCStask.params.formation=Formation or "Off Road" - - return mission -end - - ---- **[OBSOLETE]** Create a ARMORATTACK mission. --- ** Note that this is actually creating a GROUNDATTACK mission!** --- @param #AUFTRAG self --- @param Ops.Target#TARGET Target The target to attack. Can be a GROUP, UNIT or STATIC object. --- @param #number Speed Speed in knots. --- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. --- @return #AUFTRAG self -function AUFTRAG:NewARMORATTACK(Target, Speed, Formation) - - local mission=AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation) - - -- Mission type. - mission.type=AUFTRAG.Type.ARMORATTACK - - return mission -end - ---- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack. --- @param #AUFTRAG self --- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. --- @param #number Speed Speed in knots. Default max. --- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`. --- @return #AUFTRAG self -function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation) - - local mission=AUFTRAG:New(AUFTRAG.Type.GROUNDATTACK) - - mission:_TargetFromObject(Target) - - mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.GROUNDATTACK) - - -- Defaults. - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionAlarm=ENUMS.AlarmState.Auto - mission.optionFormation="On Road" - mission.missionFraction=0.70 - mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil - - mission.categories={AUFTRAG.Category.GROUND} - - mission.DCStask=mission:GetDCSMissionTask() - - mission.DCStask.params.speed=Speed - mission.DCStask.params.formation=Formation or ENUMS.Formation.Vehicle.Vee - - return mission -end - ---- **[AIR, GROUND, NAVAL]** Create a RECON mission. --- @param #AUFTRAG self --- @param Core.Set#SET_ZONE ZoneSet The recon zones. --- @param #number Speed Speed in knots. --- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL. --- @param #boolean Adinfinitum If `true`, the group will start over again after reaching the final zone. --- @param #boolean Randomly If `true`, the group will select a random zone. --- @param #string Formation Formation used during recon route. --- @return #AUFTRAG self -function AUFTRAG:NewRECON(ZoneSet, Speed, Altitude, Adinfinitum, Randomly, Formation) - - local mission=AUFTRAG:New(AUFTRAG.Type.RECON) - - mission:_TargetFromObject(ZoneSet) - - mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.RECON) - - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionROT=ENUMS.ROT.PassiveDefense - mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=0.5 - mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil - mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or UTILS.FeetToMeters(2000) - - mission.categories={AUFTRAG.Category.ALL} - - mission.DCStask=mission:GetDCSMissionTask() - mission.DCStask.params.adinfinitum=Adinfinitum - mission.DCStask.params.randomly=Randomly - mission.DCStask.params.formation=Formation - - return mission -end - ---- **[GROUND]** Create a AMMO SUPPLY mission. --- @param #AUFTRAG self --- @param Core.Zone#ZONE Zone The zone, where supply units go. --- @return #AUFTRAG self -function AUFTRAG:NewAMMOSUPPLY(Zone) - - local mission=AUFTRAG:New(AUFTRAG.Type.AMMOSUPPLY) - - mission:_TargetFromObject(Zone) - - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=1.0 - - mission.missionWaypointRadius=0 - - mission.categories={AUFTRAG.Category.GROUND} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[GROUND]** Create a FUEL SUPPLY mission. --- @param #AUFTRAG self --- @param Core.Zone#ZONE Zone The zone, where supply units go. --- @return #AUFTRAG self -function AUFTRAG:NewFUELSUPPLY(Zone) - - local mission=AUFTRAG:New(AUFTRAG.Type.FUELSUPPLY) - - mission:_TargetFromObject(Zone) - - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=1.0 - - mission.categories={AUFTRAG.Category.GROUND} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[GROUND]** Create a REARMING mission. --- @param #AUFTRAG self --- @param Core.Zone#ZONE Zone The zone, where units go and look for ammo supply. --- @return #AUFTRAG self -function AUFTRAG:NewREARMING(Zone) - - local mission=AUFTRAG:New(AUFTRAG.Type.REARMING) - - mission:_TargetFromObject(Zone) - - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=1.0 - - mission.missionWaypointRadius=0 - - mission.categories={AUFTRAG.Category.GROUND} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - - ---- **[AIR]** Create an ALERT 5 mission. Aircraft will be spawned uncontrolled and wait for an assignment. You must specify **one** mission type which is performed. --- This determines the payload and the DCS mission task which are used when the aircraft is spawned. --- @param #AUFTRAG self --- @param #string MissionType Mission type `AUFTRAG.Type.XXX`. Determines payload and mission task (intercept, ground attack, etc.). --- @return #AUFTRAG self -function AUFTRAG:NewALERT5(MissionType) - - local mission=AUFTRAG:New(AUFTRAG.Type.ALERT5) - - mission.missionTask=self:GetMissionTaskforMissionType(MissionType) - - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionROT=ENUMS.ROT.NoReaction - - mission.alert5MissionType=MissionType - - mission.missionFraction=1.0 - - mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[GROUND, NAVAL]** Create an ON GUARD mission. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Coordinate, where to stand guard. --- @return #AUFTRAG self -function AUFTRAG:NewONGUARD(Coordinate) - - local mission=AUFTRAG:New(AUFTRAG.Type.ONGUARD) - - mission:_TargetFromObject(Coordinate) - - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=1.0 - - mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[GROUND, NAVAL]** Create an AIRDEFENSE mission. --- @param #AUFTRAG self --- @param Core.Zone#ZONE Zone Zone where the air defense group(s) should be stationed. --- @return #AUFTRAG self -function AUFTRAG:NewAIRDEFENSE(Zone) - - local mission=AUFTRAG:New(AUFTRAG.Type.AIRDEFENSE) - - mission:_TargetFromObject(Zone) - - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=1.0 - - mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[GROUND]** Create an EWR mission. --- @param #AUFTRAG self --- @param Core.Zone#ZONE Zone Zone where the Early Warning Radar group(s) should be stationed. --- @return #AUFTRAG self -function AUFTRAG:NewEWR(Zone) - - local mission=AUFTRAG:New(AUFTRAG.Type.EWR) - - mission:_TargetFromObject(Zone) - - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=1.0 - - mission.categories={AUFTRAG.Category.GROUND} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - - ---- **[PRIVATE, AIR, GROUND, NAVAL]** Create a mission to relocate all cohort assets to another LEGION. --- @param #AUFTRAG self --- @param Ops.Legion#LEGION Legion The new legion. --- @param Ops.Cohort#COHORT Cohort The cohort to be relocated. --- @return #AUFTRAG self -function AUFTRAG:_NewRELOCATECOHORT(Legion, Cohort) - - local mission=AUFTRAG:New(AUFTRAG.Type.RELOCATECOHORT) - - mission:_TargetFromObject(Legion.spawnzone) - - mission.optionROE=ENUMS.ROE.ReturnFire - mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=0.0 - - mission.categories={AUFTRAG.Category.ALL} - - mission.DCStask=mission:GetDCSMissionTask() - - if Cohort.isGround then - mission.optionFormation=ENUMS.Formation.Vehicle.OnRoad - end - - mission.DCStask.params.legion=Legion - mission.DCStask.params.cohort=Cohort - - return mission -end - ---- **[GROUND, NAVAL]** Create a mission to do NOTHING. --- @param #AUFTRAG self --- @param Core.Zone#ZONE RelaxZone Zone where the assets are supposed to do nothing. --- @return #AUFTRAG self -function AUFTRAG:NewNOTHING(RelaxZone) - - local mission=AUFTRAG:New(AUFTRAG.Type.NOTHING) - - mission:_TargetFromObject(RelaxZone) - - mission.optionROE=ENUMS.ROE.WeaponHold - mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=1.0 - - mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- **[GROUND]** Create an ARMORED ON GUARD mission. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Coordinate, where to stand guard. --- @param #string Formation Formation to take, e.g. "On Road", "Vee" etc. --- @return #AUFTRAG self -function AUFTRAG:NewARMOREDGUARD(Coordinate,Formation) - - local mission=AUFTRAG:New(AUFTRAG.Type.ARMOREDGUARD) - - mission:_TargetFromObject(Coordinate) - - mission.optionROE=ENUMS.ROE.OpenFire - mission.optionAlarm=ENUMS.AlarmState.Auto - mission.optionFormation=Formation or "On Road" - - mission.missionFraction=1.0 - - mission.categories={AUFTRAG.Category.GROUND} - - mission.DCStask=mission:GetDCSMissionTask() - - return mission -end - ---- Create a mission to attack a TARGET object. --- @param #AUFTRAG self --- @param Ops.Target#TARGET Target The target. --- @param #string MissionType The mission type. --- @return #AUFTRAG self -function AUFTRAG:NewFromTarget(Target, MissionType) - - local mission=nil --#AUFTRAG - - if MissionType==AUFTRAG.Type.ANTISHIP then - mission=self:NewANTISHIP(Target, Altitude) - elseif MissionType==AUFTRAG.Type.ARTY then - mission=self:NewARTY(Target, Nshots, Radius) - elseif MissionType==AUFTRAG.Type.BAI then - mission=self:NewBAI(Target, Altitude) - elseif MissionType==AUFTRAG.Type.BOMBCARPET then - mission=self:NewBOMBCARPET(Target, Altitude, CarpetLength) - elseif MissionType==AUFTRAG.Type.BOMBING then - mission=self:NewBOMBING(Target, Altitude) - elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then - mission=self:NewBOMBRUNWAY(Target, Altitude) - elseif MissionType==AUFTRAG.Type.CAS then - mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,Target:GetAverageCoordinate(),Heading,Leg,TargetTypes) - elseif MissionType==AUFTRAG.Type.CASENHANCED then - mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,RangeMax,NoEngageZoneSet,TargetTypes) - elseif MissionType==AUFTRAG.Type.INTERCEPT then - mission=self:NewINTERCEPT(Target) - elseif MissionType==AUFTRAG.Type.SEAD then - mission=self:NewSEAD(Target, Altitude) - elseif MissionType==AUFTRAG.Type.STRIKE then - mission=self:NewSTRIKE(Target, Altitude) - elseif MissionType==AUFTRAG.Type.ARMORATTACK then - mission=self:NewARMORATTACK(Target, Speed) - elseif MissionType==AUFTRAG.Type.GROUNDATTACK then - mission=self:NewGROUNDATTACK(Target, Speed, Formation) - else - return nil - end - - return mission -end - - ---- Create a mission to attack a group. Mission type is automatically chosen from the group category. --- @param #AUFTRAG self --- @param Wrapper.Positionable#POSITIONABLE Target Target object. --- @return #string Auftrag type, e.g. `AUFTRAG.Type.BAI` (="BAI"). -function AUFTRAG:_DetermineAuftragType(Target) - - local group=nil --Wrapper.Group#GROUP - local airbase=nil --Wrapper.Airbase#AIRBASE - local scenery=nil --Wrapper.Scenery#SCENERY - local coordinate=nil --Core.Point#COORDINATE - local auftrag=nil - - if Target:IsInstanceOf("GROUP") then - group=Target --Target is already a group. - elseif Target:IsInstanceOf("UNIT") then - group=Target:GetGroup() - elseif Target:IsInstanceOf("AIRBASE") then - airbase=Target - elseif Target:IsInstanceOf("SCENERY") then - scenery=Target - end - - if group then - - local category=group:GetCategory() - local attribute=group:GetAttribute() - - if category==Group.Category.AIRPLANE or category==Group.Category.HELICOPTER then - - --- - -- A2A: Intercept - --- - - auftrag=AUFTRAG.Type.INTERCEPT - - elseif category==Group.Category.GROUND or category==Group.Category.TRAIN then - - --- - -- GROUND - --- - - if attribute==GROUP.Attribute.GROUND_SAM then - - -- SEAD/DEAD - - auftrag=AUFTRAG.Type.SEAD - - elseif attribute==GROUP.Attribute.GROUND_AAA then - - auftrag=AUFTRAG.Type.BAI - - elseif attribute==GROUP.Attribute.GROUND_ARTILLERY then - - auftrag=AUFTRAG.Type.BAI - - elseif attribute==GROUP.Attribute.GROUND_INFANTRY then - - auftrag=AUFTRAG.Type.CAS - - elseif attribute==GROUP.Attribute.GROUND_TANK then - - auftrag=AUFTRAG.Type.BAI - - else - - auftrag=AUFTRAG.Type.BAI - - end - - - elseif category==Group.Category.SHIP then - - --- - -- NAVAL - --- - - auftrag=AUFTRAG.Type.ANTISHIP - - else - self:T(self.lid.."ERROR: Unknown Group category!") - end - - elseif airbase then - auftrag=AUFTRAG.Type.BOMBRUNWAY - elseif scenery then - auftrag=AUFTRAG.Type.STRIKE - elseif coordinate then - auftrag=AUFTRAG.Type.BOMBING - end - - return auftrag -end - ---- Create a mission to attack a group. Mission type is automatically chosen from the group category. --- @param #AUFTRAG self --- @param Wrapper.Group#GROUP EngageGroup Group to be engaged. --- @return #AUFTRAG self -function AUFTRAG:NewAUTO(EngageGroup) - - local mission=nil --#AUFTRAG - - local Target=EngageGroup - - local auftrag=self:_DetermineAuftragType(EngageGroup) - - if auftrag==AUFTRAG.Type.ANTISHIP then - mission=AUFTRAG:NewANTISHIP(Target) - elseif auftrag==AUFTRAG.Type.ARTY then - mission=AUFTRAG:NewARTY(Target) - elseif auftrag==AUFTRAG.Type.AWACS then - mission=AUFTRAG:NewAWACS(Coordinate, Altitude,Speed,Heading,Leg) - elseif auftrag==AUFTRAG.Type.BAI then - mission=AUFTRAG:NewBAI(Target,Altitude) - elseif auftrag==AUFTRAG.Type.BOMBING then - mission=AUFTRAG:NewBOMBING(Target,Altitude) - elseif auftrag==AUFTRAG.Type.BOMBRUNWAY then - mission=AUFTRAG:NewBOMBRUNWAY(Airdrome,Altitude) - elseif auftrag==AUFTRAG.Type.BOMBCARPET then - mission=AUFTRAG:NewBOMBCARPET(Target,Altitude,CarpetLength) - elseif auftrag==AUFTRAG.Type.CAP then - mission=AUFTRAG:NewCAP(ZoneCAP,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes) - elseif auftrag==AUFTRAG.Type.CAS then - mission=AUFTRAG:NewCAS(ZoneCAS,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes) - elseif auftrag==AUFTRAG.Type.ESCORT then - mission=AUFTRAG:NewESCORT(EscortGroup,OffsetVector,EngageMaxDistance,TargetTypes) - elseif auftrag==AUFTRAG.Type.FACA then - mission=AUFTRAG:NewFACA(Target,Designation,DataLink,Frequency,Modulation) - elseif auftrag==AUFTRAG.Type.FERRY then - -- Not implemented yet. - elseif auftrag==AUFTRAG.Type.GCICAP then - mission=AUFTRAG:NewGCICAP(Coordinate,Altitude,Speed,Heading,Leg) - elseif auftrag==AUFTRAG.Type.INTERCEPT then - mission=AUFTRAG:NewINTERCEPT(Target) - elseif auftrag==AUFTRAG.Type.ORBIT then - mission=AUFTRAG:NewORBIT(Coordinate,Altitude,Speed,Heading,Leg) - elseif auftrag==AUFTRAG.Type.RECON then - -- Not implemented yet. - elseif auftrag==AUFTRAG.Type.RESCUEHELO then - mission=AUFTRAG:NewRESCUEHELO(Carrier) - elseif auftrag==AUFTRAG.Type.SEAD then - mission=AUFTRAG:NewSEAD(Target,Altitude) - elseif auftrag==AUFTRAG.Type.STRIKE then - mission=AUFTRAG:NewSTRIKE(Target,Altitude) - elseif auftrag==AUFTRAG.Type.TANKER then - mission=AUFTRAG:NewTANKER(Coordinate,Altitude,Speed,Heading,Leg,RefuelSystem) - elseif auftrag==AUFTRAG.Type.TROOPTRANSPORT then - mission=AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet,DropoffCoordinate,PickupCoordinate) - else - - end - - if mission then - mission:SetPriority(10, true) - end - - return mission -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- User API Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Set mission start and stop time. --- @param #AUFTRAG self --- @param #string ClockStart Time the mission is started, e.g. "05:00" for 5 am. If specified as a #number, it will be relative (in seconds) to the current mission time. Default is 5 seconds after mission was added. --- @param #string ClockStop (Optional) Time the mission is stopped, e.g. "13:00" for 1 pm. If mission could not be started at that time, it will be removed from the queue. If specified as a #number it will be relative (in seconds) to the current mission time. --- @return #AUFTRAG self -function AUFTRAG:SetTime(ClockStart, ClockStop) - - -- Current mission time. - local Tnow=timer.getAbsTime() - - -- Set start time. Default in 5 sec. - local Tstart=Tnow+5 - if ClockStart and type(ClockStart)=="number" then - Tstart=Tnow+ClockStart - elseif ClockStart and type(ClockStart)=="string" then - Tstart=UTILS.ClockToSeconds(ClockStart) - end - - -- Set stop time. Default nil. - local Tstop=nil - if ClockStop and type(ClockStop)=="number" then - Tstop=Tnow+ClockStop - elseif ClockStop and type(ClockStop)=="string" then - Tstop=UTILS.ClockToSeconds(ClockStop) - end - - self.Tstart=Tstart - self.Tstop=Tstop - - if Tstop then - self.duration=self.Tstop-self.Tstart - end - - return self -end - ---- Set time how long the mission is executed. Once this time limit has passed, the mission is cancelled. --- @param #AUFTRAG self --- @param #number Duration Duration in seconds. --- @return #AUFTRAG self -function AUFTRAG:SetDuration(Duration) - self.durationExe=Duration - return self -end - ---- Set that mission assets are teleported to the mission execution waypoint. --- @param #AUFTRAG self --- @param #boolean Switch If `true` or `nil`, teleporting is on. If `false`, teleporting is off. --- @return #AUFTRAG self -function AUFTRAG:SetTeleport(Switch) - if Switch==nil then - Switch=true - end - self.teleport=Switch - return self -end - - ---- Set mission push time. This is the time the mission is executed. If the push time is not passed, the group will wait at the mission execution waypoint. --- @param #AUFTRAG self --- @param #string ClockPush Time the mission is executed, e.g. "05:00" for 5 am. Can also be given as a `#number`, where it is interpreted as relative push time in seconds. --- @return #AUFTRAG self -function AUFTRAG:SetPushTime(ClockPush) - - if ClockPush then - if type(ClockPush)=="string" then - self.Tpush=UTILS.ClockToSeconds(ClockPush) - elseif type(ClockPush)=="number" then - self.Tpush=timer.getAbsTime()+ClockPush - end - end - - return self -end - ---- Set mission priority and (optional) urgency. Urgent missions can cancel other running missions. --- @param #AUFTRAG self --- @param #number Prio Priority 1=high, 100=low. Default 50. --- @param #boolean Urgent If *true*, another running mission might be cancelled if it has a lower priority. --- @param #number Importance Number 1-10. If missions with lower value are in the queue, these have to be finished first. Default is `nil`. --- @return #AUFTRAG self -function AUFTRAG:SetPriority(Prio, Urgent, Importance) - self.prio=Prio or 50 - self.urgent=Urgent - self.importance=Importance - return self -end - ---- **[LEGION, COMMANDER, CHIEF]** Set how many times the mission is repeated. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level. --- @param #AUFTRAG self --- @param #number Nrepeat Number of repeats. Default 0. --- @return #AUFTRAG self -function AUFTRAG:SetRepeat(Nrepeat) - self.Nrepeat=Nrepeat or 0 - return self -end - ---- **[LEGION, COMMANDER, CHIEF]** Set how many times the mission is repeated if it fails. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level. --- @param #AUFTRAG self --- @param #number Nrepeat Number of repeats. Default 0. --- @return #AUFTRAG self -function AUFTRAG:SetRepeatOnFailure(Nrepeat) - self.NrepeatFailure=Nrepeat or 0 - return self -end - ---- **[LEGION, COMMANDER, CHIEF]** Set how many times the mission is repeated if it was successful. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level. --- @param #AUFTRAG self --- @param #number Nrepeat Number of repeats. Default 0. --- @return #AUFTRAG self -function AUFTRAG:SetRepeatOnSuccess(Nrepeat) - self.NrepeatSuccess=Nrepeat or 0 - return self -end - ---- **[LEGION, COMMANDER, CHIEF]** Define how many assets are required to do the job. Only used if the mission is handled by a **LEGION** (AIRWING, BRIGADE, ...) or higher level. --- @param #AUFTRAG self --- @param #number NassetsMin Minimum number of asset groups. Default 1. --- @param #number NassetsMax Maximum Number of asset groups. Default is same as `NassetsMin`. --- @return #AUFTRAG self -function AUFTRAG:SetRequiredAssets(NassetsMin, NassetsMax) - - self.NassetsMin=NassetsMin or 1 - - self.NassetsMax=NassetsMax or self.NassetsMin - - -- Ensure that max is at least equal to min. - if self.NassetsMaxself.Tstop then - return false - end - - -- All start conditions true? - local startme=self:EvalConditionsAll(self.conditionStart) - - if not startme then - return false - end - - - -- We're good to go! - return true -end - ---- Check if mission is ready to be started. --- * Mission stop already passed. --- * Any stop condition is true. --- @param #AUFTRAG self --- @return #boolean If true, mission should be cancelled. -function AUFTRAG:IsReadyToCancel() - - local Tnow=timer.getAbsTime() - - -- Stop time already passed. - if self.Tstop and Tnow>=self.Tstop then - return true - end - - -- Evaluate failure condition. One is enough. - local failure=self:EvalConditionsAny(self.conditionFailure) - - if failure then - self.failurecondition=true - return true - end - - -- Evaluate success consitions. One is enough. - local success=self:EvalConditionsAny(self.conditionSuccess) - - if success then - self.successcondition=true - return true - end - - -- No criterion matched. - return false -end - ---- Check if mission is ready to be pushed. --- * Mission push time already passed. --- * **All** push conditions are true. --- @param #AUFTRAG self --- @return #boolean If true, mission groups can push. -function AUFTRAG:IsReadyToPush() - - local Tnow=timer.getAbsTime() - - -- Push time passed? - if self.Tpush and Tnow<=self.Tpush then - return false - end - - -- Evaluate push condition(s) if any. All need to be true. - local push=self:EvalConditionsAll(self.conditionPush) - - return push -end - ---- Check if all given condition are true. --- @param #AUFTRAG self --- @param #table Conditions Table of conditions. --- @return #boolean If true, all conditions were true. Returns false if at least one condition returned false. -function AUFTRAG:EvalConditionsAll(Conditions) - - -- Any stop condition must be true. - for _,_condition in pairs(Conditions or {}) do - local condition=_condition --#AUFTRAG.Condition - - -- Call function. - local istrue=condition.func(unpack(condition.arg)) - - -- Any false will return false. - if not istrue then - return false - end - - end - - -- All conditions were true. - return true -end - - ---- Check if any of the given conditions is true. --- @param #AUFTRAG self --- @param #table Conditions Table of conditions. --- @return #boolean If true, at least one condition is true. -function AUFTRAG:EvalConditionsAny(Conditions) - - -- Any stop condition must be true. - for _,_condition in pairs(Conditions or {}) do - local condition=_condition --#AUFTRAG.Condition - - -- Call function. - local istrue=condition.func(unpack(condition.arg)) - - -- Any true will return true. - if istrue then - return true - end - - end - - -- No condition was true. - return false -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Mission Status -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- On after "Status" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterStatus(From, Event, To) - - -- Current abs. mission time. - local Tnow=timer.getAbsTime() - - -- ESCORT: Check if only the group NAME of an escort had been specified. - if self.escortGroupName then - -- Try to find the group. - local group=GROUP:FindByName(self.escortGroupName) - if group and group:IsAlive() then - - -- Debug info. - self:T(self.lid..string.format("ESCORT group %s is now alive. Updating DCS task and adding group to TARGET", tostring(self.escortGroupName))) - - -- Add TARGET object. - self.engageTarget:AddObject(group) - - -- Update DCS task with the known group ID. - self.DCStask=self:GetDCSMissionTask() - - -- Set value to nil so we do not do this again in the next cycle. - self.escortGroupName=nil - end - end - - -- Number of alive mission targets. - local Ntargets=self:CountMissionTargets() - local Ntargets0=self:GetTargetInitialNumber() - - -- Number of alive groups attached to this mission. - local Ngroups=self:CountOpsGroups() - - -- Check if mission is not OVER yet. - if self:IsNotOver() then - - if self:CheckGroupsDone() then - - -- All groups have reported MISSON DONE. - self:Done() - - elseif (self.Tstop and Tnow>self.Tstop+10) then - - -- Cancel mission if stop time passed. - self:Cancel() - - elseif self.durationExe and self.Texecuting and Tnow-self.Texecuting>self.durationExe then - - -- Backup repeat values - local Nrepeat=self.Nrepeat - local NrepeatS=self.NrepeatSuccess - local NrepeatF=self.NrepeatFailure - - -- Cancel mission if stop time passed. - self:Cancel() - - self.Nrepeat=Nrepeat - self.NrepeatSuccess=NrepeatS - self.NrepeatFailure=NrepeatF - - elseif (Ntargets0>0 and Ntargets==0) then - - -- Cancel mission if mission targets are gone (if there were any in the beginning). - -- TODO: I commented this out for some reason but I forgot why... - self:T(self.lid.."No targets left cancelling mission!") - self:Cancel() - - elseif self:IsExecuting() then - - -- Had the case that mission was in state Executing but all assigned groups were dead. - -- TODO: might need to loop over all assigned groups - if Ngroups==0 then - self:Done() - else - local done=true - for groupname,data in pairs(self.groupdata or {}) do - local groupdata=data --#AUFTRAG.GroupData - local opsgroup=groupdata.opsgroup - if opsgroup:IsAlive() then - done=false - end - end - if done then - self:Done() - end - end - - end - - end - - -- Current FSM state. - local fsmstate=self:GetState() - - -- Check for error. - if fsmstate~=self.status then - self:T(self.lid..string.format("ERROR: FSM state %s != %s mission status!", fsmstate, self.status)) - end - - -- General info. - if self.verbose>=1 then - - -- Mission start stop time. - local Cstart=UTILS.SecondsToClock(self.Tstart, true) - local Cstop=self.Tstop and UTILS.SecondsToClock(self.Tstop, true) or "INF" - - local targetname=self:GetTargetName() or "unknown" - - local Nlegions=#self.legions - local commander=self.commander and self.statusCommander or "N/A" - local chief=self.chief and self.statusChief or "N/A" - - -- Info message. - self:T(self.lid..string.format("Status %s: Target=%s, T=%s-%s, assets=%d, groups=%d, targets=%d, legions=%d, commander=%s, chief=%s", - self.status, targetname, Cstart, Cstop, #self.assets, Ngroups, Ntargets, Nlegions, commander, chief)) - end - - -- Group info. - if self.verbose>=2 then - -- Data on assigned groups. - local text="Group data:" - for groupname,_groupdata in pairs(self.groupdata) do - local groupdata=_groupdata --#AUFTRAG.GroupData - text=text..string.format("\n- %s: status mission=%s opsgroup=%s", groupname, groupdata.status, groupdata.opsgroup and groupdata.opsgroup:GetState() or "N/A") - end - self:I(self.lid..text) - end - - -- Ready to evaluate mission outcome? - local ready2evaluate=self.Tover and Tnow-self.Tover>=self.dTevaluate or false - - -- Check if mission is OVER (done or cancelled) and enough time passed to evaluate the result. - if self:IsOver() and ready2evaluate then - -- Evaluate success or failure of the mission. - self:Evaluate() - else - self:__Status(-30) - end - - -- Update F10 marker. - if self.markerOn then - self:UpdateMarker() - end - -end - ---- Evaluate mission outcome - success or failure. --- @param #AUFTRAG self --- @return #AUFTRAG self -function AUFTRAG:Evaluate() - - -- Assume success and check if any failed condition applies. - local failed=false - - -- Target damage in %. - local targetdamage=self:GetTargetDamage() - - -- Own damage in %. - local owndamage=self.Ncasualties/self.Nelements*100 - - -- Current number of mission targets. - local Ntargets=self:CountMissionTargets() - local Ntargets0=self:GetTargetInitialNumber() - - local Life=self:GetTargetLife() - local Life0=self:GetTargetInitialLife() - - - if Ntargets0>0 then - - --- - -- Mission had targets - --- - - -- Check if failed. - if self.type==AUFTRAG.Type.TROOPTRANSPORT or self.type==AUFTRAG.Type.ESCORT then - - -- Transported or escorted groups have to survive. - if Ntargets0 then - failed=true - end - - end - - else - - --- - -- Mission had NO targets - --- - - -- No targets and everybody died ==> mission failed. Well, unless success condition is true. - if self.Nelements==self.Ncasualties then - failed=true - end - - end - - - -- Any success condition true? - local successCondition=self:EvalConditionsAny(self.conditionSuccess) - - -- Any failure condition true? - local failureCondition=self:EvalConditionsAny(self.conditionFailure) - - if failureCondition then - failed=true - elseif successCondition then - failed=false - end - - -- Debug text. - if self.verbose > 0 then - local text=string.format("Evaluating mission:\n") - text=text..string.format("Own casualties = %d/%d\n", self.Ncasualties, self.Nelements) - text=text..string.format("Own losses = %.1f %%\n", owndamage) - text=text..string.format("Killed units = %d\n", self.Nkills) - text=text..string.format("--------------------------\n") - text=text..string.format("Targets left = %d/%d\n", Ntargets, Ntargets0) - text=text..string.format("Targets life = %.1f/%.1f\n", Life, Life0) - text=text..string.format("Enemy losses = %.1f %%\n", targetdamage) - text=text..string.format("--------------------------\n") - text=text..string.format("Success Cond = %s\n", tostring(successCondition)) - text=text..string.format("Failure Cond = %s\n", tostring(failureCondition)) - text=text..string.format("--------------------------\n") - text=text..string.format("Final Success = %s\n", tostring(not failed)) - text=text..string.format("=========================") - self:I(self.lid..text) - end - - -- Trigger events. - if failed then - self:I(self.lid..string.format("Mission %d [%s] failed!", self.auftragsnummer, self.type)) - if self.chief then - self.chief.Nfailure=self.chief.Nfailure+1 - end - self:Failed() - else - self:I(self.lid..string.format("Mission %d [%s] success!", self.auftragsnummer, self.type)) - if self.chief then - self.chief.Nsuccess=self.chief.Nsuccess+1 - end - self:Success() - end - - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Asset Data -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Get all OPS groups. --- @param #AUFTRAG self --- @return #table Table of Ops.OpsGroup#OPSGROUP or {}. -function AUFTRAG:GetOpsGroups() - local opsgroups={} - for _,_groupdata in pairs(self.groupdata or {}) do - local groupdata=_groupdata --#AUFTRAG.GroupData - table.insert(opsgroups, groupdata.opsgroup) - end - return opsgroups -end - ---- Get asset data table. --- @param #AUFTRAG self --- @param #string AssetName Name of the asset. --- @return #AUFTRAG.GroupData Group data or *nil* if OPS group does not exist. -function AUFTRAG:GetAssetDataByName(AssetName) - return self.groupdata[tostring(AssetName)] -end - ---- Get flight data table. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The flight group. --- @return #AUFTRAG.GroupData Flight data or nil if opsgroup does not exist. -function AUFTRAG:GetGroupData(opsgroup) - if opsgroup and self.groupdata then - return self.groupdata[opsgroup.groupname] - end - return nil -end - ---- Set opsgroup mission status. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The flight group. --- @param #string status New status. --- @return #AUFTRAG self -function AUFTRAG:SetGroupStatus(opsgroup, status) - - -- Current status. - local oldstatus=self:GetGroupStatus(opsgroup) - - -- Debug info. - self:T(self.lid..string.format("Setting OPSGROUP %s to status %s-->%s", opsgroup and opsgroup.groupname or "nil", tostring(oldstatus), tostring(status))) - - if oldstatus==AUFTRAG.GroupStatus.CANCELLED and status==AUFTRAG.GroupStatus.DONE then - -- Do not overwrite a CANCELLED status with a DONE status. - else - local groupdata=self:GetGroupData(opsgroup) - if groupdata then - groupdata.status=status - else - self:T(self.lid.."WARNING: Could not SET flight data for flight group. Setting status to DONE") - end - end - - -- Check if mission is NOT over. - local isNotOver=self:IsNotOver() - - -- Check if all assigned groups are done. - 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(groupsDone))) - - -- Check if ALL flights are done with their mission. - if isNotOver and groupsDone then - self:T3(self.lid.."All assigned OPSGROUPs done ==> mission DONE!") - self:Done() - else - self:T3(self.lid.."Mission NOT DONE yet!") - end - - return self -end - ---- Get ops group mission status. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @return #string The group status. -function AUFTRAG:GetGroupStatus(opsgroup) - self:T3(self.lid..string.format("Trying to get Flight status for flight group %s", opsgroup and opsgroup.groupname or "nil")) - - local groupdata=self:GetGroupData(opsgroup) - - if groupdata then - return groupdata.status - else - - self:T(self.lid..string.format("WARNING: Could not GET groupdata for opsgroup %s. Returning status DONE.", opsgroup and opsgroup.groupname or "nil")) - return AUFTRAG.GroupStatus.DONE - - end -end - ---- Add LEGION to mission. --- @param #AUFTRAG self --- @param Ops.Legion#LEGION Legion The legion. --- @return #AUFTRAG self -function AUFTRAG:AddLegion(Legion) - - -- Debug info. - self:T(self.lid..string.format("Adding legion %s", Legion.alias)) - - -- Add legion to table. - table.insert(self.legions, Legion) - - return self -end - ---- Remove LEGION from mission. --- @param #AUFTRAG self --- @param Ops.Legion#LEGION Legion The legion. --- @return #AUFTRAG self -function AUFTRAG:RemoveLegion(Legion) - - -- Loop over legions - for i=#self.legions,1,-1 do - local legion=self.legions[i] --Ops.Legion#LEGION - - if legion.alias==Legion.alias then - - -- Debug info. - self:T(self.lid..string.format("Removing legion %s", Legion.alias)) - table.remove(self.legions, i) - - -- Set legion status to nil. - self.statusLegion[Legion.alias]=nil - - return self - end - - end - - self:T(self.lid..string.format("ERROR: Legion %s not found and could not be removed!", Legion.alias)) - return self -end - ---- Set LEGION mission status. --- @param #AUFTRAG self --- @param Ops.Legion#LEGION Legion The legion. --- @param #string Status New status. --- @return #AUFTRAG self -function AUFTRAG:SetLegionStatus(Legion, Status) - - -- Old status - local status=self:GetLegionStatus(Legion) - - -- Debug info. - self:T(self.lid..string.format("Setting LEGION %s to status %s-->%s", Legion.alias, tostring(status), tostring(Status))) - - -- New status. - self.statusLegion[Legion.alias]=Status - - return self -end - ---- Get LEGION mission status. --- @param #AUFTRAG self --- @param Ops.Legion#LEGION Legion The legion. --- @return #string status Current status. -function AUFTRAG:GetLegionStatus(Legion) - - -- New status. - local status=self.statusLegion[Legion.alias] or "unknown" - - return status -end - - ---- Set mission (ingress) waypoint coordinate for OPS group. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @param Core.Point#COORDINATE coordinate Waypoint Coordinate. --- @return #AUFTRAG self -function AUFTRAG:SetGroupWaypointCoordinate(opsgroup, coordinate) - local groupdata=self:GetGroupData(opsgroup) - if groupdata then - groupdata.waypointcoordinate=coordinate - end - return self -end - ---- Get mission (ingress) waypoint coordinate of OPS group --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @return Core.Point#COORDINATE Waypoint Coordinate. -function AUFTRAG:GetGroupWaypointCoordinate(opsgroup) - local groupdata=self:GetGroupData(opsgroup) - if groupdata then - return groupdata.waypointcoordinate - end -end - - ---- Set mission waypoint task for OPS group. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @param Ops.OpsGroup#OPSGROUP.Task task Waypoint task. -function AUFTRAG:SetGroupWaypointTask(opsgroup, task) - self:T2(self.lid..string.format("Setting waypoint task %s", task and task.description or "WTF")) - local groupdata=self:GetGroupData(opsgroup) - if groupdata then - groupdata.waypointtask=task - end -end - ---- Get mission waypoint task of OPS group. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @return Ops.OpsGroup#OPSGROUP.Task task Waypoint task. Waypoint task. -function AUFTRAG:GetGroupWaypointTask(opsgroup) - local groupdata=self:GetGroupData(opsgroup) - if groupdata then - return groupdata.waypointtask - end -end - ---- Set mission (ingress) waypoint UID for OPS group. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @param #number waypointindex Waypoint UID. --- @return #AUFTRAG self -function AUFTRAG:SetGroupWaypointIndex(opsgroup, waypointindex) - self:T2(self.lid..string.format("Setting Mission waypoint UID=%d", waypointindex)) - local groupdata=self:GetGroupData(opsgroup) - if groupdata then - groupdata.waypointindex=waypointindex - end - return self -end - ---- Get mission (ingress) waypoint UID of OPS group. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @return #number Waypoint UID. -function AUFTRAG:GetGroupWaypointIndex(opsgroup) - local groupdata=self:GetGroupData(opsgroup) - if groupdata then - return groupdata.waypointindex - end -end - ---- Set Egress waypoint UID for OPS group. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @param #number waypointindex Waypoint UID. --- @return #AUFTRAG self -function AUFTRAG:SetGroupEgressWaypointUID(opsgroup, waypointindex) - self:T2(self.lid..string.format("Setting Egress waypoint UID=%d", waypointindex)) - local groupdata=self:GetGroupData(opsgroup) - if groupdata then - groupdata.waypointEgressUID=waypointindex - end - return self -end - ---- Get Egress waypoint UID of OPS group. --- @param #AUFTRAG self --- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @return #number Waypoint UID. -function AUFTRAG:GetGroupEgressWaypointUID(opsgroup) - local groupdata=self:GetGroupData(opsgroup) - if groupdata then - return groupdata.waypointEgressUID - end -end - - - ---- Check if all flights are done with their mission (or dead). --- @param #AUFTRAG self --- @return #boolean If true, all flights are done with the mission. -function AUFTRAG:CheckGroupsDone() - - -- Check status of all OPS groups. - for groupname,data in pairs(self.groupdata) do - local groupdata=data --#AUFTRAG.GroupData - if groupdata then - if not (groupdata.status==AUFTRAG.GroupStatus.DONE or groupdata.status==AUFTRAG.GroupStatus.CANCELLED) then - -- At least this flight is not DONE or CANCELLED. - self:T2(self.lid..string.format("CheckGroupsDone: OPSGROUP %s is not DONE or CANCELLED but in state %s. Mission NOT DONE!", groupdata.opsgroup.groupname, groupdata.status:upper())) - return false - end - end - end - - -- Check status of all LEGIONs. - for _,_legion in pairs(self.legions) do - local legion=_legion --Ops.Legion#LEGION - local status=self:GetLegionStatus(legion) - if not status==AUFTRAG.Status.CANCELLED then - -- At least one LEGION has not CANCELLED. - self:T2(self.lid..string.format("CheckGroupsDone: LEGION %s is not CANCELLED but in state %s. Mission NOT DONE!", legion.alias, status)) - return false - end - end - - -- Check commander status. - if self.commander then - if not self.statusCommander==AUFTRAG.Status.CANCELLED then - self:T2(self.lid..string.format("CheckGroupsDone: COMMANDER is not CANCELLED but in state %s. Mission NOT DONE!", self.statusCommander)) - return false - end - end - - -- Check chief status. - if self.chief then - if not self.statusChief==AUFTRAG.Status.CANCELLED then - self:T2(self.lid..string.format("CheckGroupsDone: CHIEF is not CANCELLED but in state %s. Mission NOT DONE!", self.statusChief)) - return false - end - end - - -- These are early stages, where we might not even have a opsgroup defined to be checked. If there were any groups, we checked above. - if self:IsPlanned() or self:IsQueued() or self:IsRequested() then - self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] (PLANNED or QUEUED or REQUESTED). Mission NOT DONE!", self.status, self:GetState())) - return false - end - - -- It could be that all flights were destroyed on the way to the mission execution waypoint. - -- TODO: would be better to check if everybody is dead by now. - if self:IsStarted() and self:CountOpsGroups()==0 then - self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!", self.status, self:GetState())) - return true - end - - return true -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- EVENT Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Unit lost event. --- @param #AUFTRAG self --- @param Core.Event#EVENTDATA EventData Event data. -function AUFTRAG:OnEventUnitLost(EventData) - - -- Check that this is the right group. - if EventData and EventData.IniGroup and EventData.IniUnit then - local unit=EventData.IniUnit - local group=EventData.IniGroup - local unitname=EventData.IniUnitName - - for _,_groupdata in pairs(self.groupdata) do - local groupdata=_groupdata --#AUFTRAG.GroupData - if groupdata and groupdata.opsgroup and groupdata.opsgroup.groupname==EventData.IniGroupName then - self:T(self.lid..string.format("UNIT LOST event for opsgroup %s unit %s", groupdata.opsgroup.groupname, EventData.IniUnitName)) - end - end - - end - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- FSM Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - - ---- On after "Planned" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterPlanned(From, Event, To) - self.status=AUFTRAG.Status.PLANNED - self:T(self.lid..string.format("New mission status=%s", self.status)) -end - ---- On after "Queue" event. Mission is added to the mission queue of a LEGION. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterQueued(From, Event, To, Airwing) - self.status=AUFTRAG.Status.QUEUED - self:T(self.lid..string.format("New mission status=%s", self.status)) -end - - ---- On after "Requested" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterRequested(From, Event, To) - self.status=AUFTRAG.Status.REQUESTED - self:T(self.lid..string.format("New mission status=%s", self.status)) -end - ---- On after "Assign" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterAssign(From, Event, To) - self.status=AUFTRAG.Status.ASSIGNED - self:T(self.lid..string.format("New mission status=%s", self.status)) -end - ---- On after "Schedule" event. Mission is added to the mission queue of an OPSGROUP. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterScheduled(From, Event, To) - self.status=AUFTRAG.Status.SCHEDULED - self:T(self.lid..string.format("New mission status=%s", self.status)) -end - ---- On after "Start" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterStarted(From, Event, To) - self.status=AUFTRAG.Status.STARTED - self.Tstarted=timer.getAbsTime() - self:T(self.lid..string.format("New mission status=%s", self.status)) -end - ---- On after "Execute" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterExecuting(From, Event, To) - self.status=AUFTRAG.Status.EXECUTING - self.Texecuting=timer.getAbsTime() - self:T(self.lid..string.format("New mission status=%s", self.status)) -end - ---- On after "ElementDestroyed" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Ops.OpsGroup#OPSGROUP OpsGroup The ops group to which the element belongs. --- @param Ops.OpsGroup#OPSGROUP.Element Element The element that got destroyed. -function AUFTRAG:onafterElementDestroyed(From, Event, To, OpsGroup, Element) - -- Increase number of own casualties. - self.Ncasualties=self.Ncasualties+1 -end - ---- On after "GroupDead" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Ops.OpsGroup#OPSGROUP OpsGroup The ops group that is dead now. -function AUFTRAG:onafterGroupDead(From, Event, To, OpsGroup) - - local asset=self:GetAssetByName(OpsGroup.groupname) - if asset then - self:AssetDead(asset) - end - - -- Number of dead groups. - self.Ndead=self.Ndead+1 - -end - ---- On after "AssetDead" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset. -function AUFTRAG:onafterAssetDead(From, Event, To, Asset) - - -- Number of groups alive. - local N=self:CountOpsGroups() - - self:T(self.lid..string.format("Asset %s dead! Number of ops groups remaining %d", tostring(Asset.spawngroupname), N)) - - -- All assets dead? - if N==0 then - - if self:IsNotOver() then - - -- Cancel mission. Wait for next mission update to evaluate SUCCESS or FAILURE. - self:Cancel() - - else - - --self:E(self.lid.."ERROR: All assets are dead not but mission was already over... Investigate!") - -- Now this can happen, because when a opsgroup dies (sometimes!), the mission is DONE - - end - end - - -- Delete asset from mission. - self:DelAsset(Asset) - -end - ---- On after "Cancel" event. Cancells the mission. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterCancel(From, Event, To) - - -- Number of OPSGROUPS assigned and alive. - local Ngroups = self:CountOpsGroups() - - -- Debug info. - self:T(self.lid..string.format("CANCELLING mission in status %s. Will wait for %d groups to report mission DONE before evaluation", self.status, Ngroups)) - - -- Time stamp. - self.Tover=timer.getAbsTime() - - -- No more repeats. - self.Nrepeat=self.repeated - self.NrepeatFailure=self.repeatedFailure - self.NrepeatSuccess=self.repeatedSuccess - - -- Not necessary to delay the evaluaton?! - self.dTevaluate=0 - - if self.chief then - - -- Debug info. - self:T(self.lid..string.format("CHIEF will cancel the mission. Will wait for mission DONE before evaluation!")) - - -- CHIEF will cancel the mission. - self.chief:MissionCancel(self) - - elseif self.commander then - - -- Debug info. - self:T(self.lid..string.format("COMMANDER will cancel the mission. Will wait for mission DONE before evaluation!")) - - -- COMMANDER will cancel the mission. - self.commander:MissionCancel(self) - - elseif self.legions and #self.legions>0 then - - -- Loop over all LEGIONs. - for _,_legion in pairs(self.legions or {}) do - local legion=_legion --Ops.Legion#LEGION - - -- Debug info. - self:T(self.lid..string.format("LEGION %s will cancel the mission. Will wait for mission DONE before evaluation!", legion.alias)) - - -- Legion will cancel all flight missions and remove queued request from warehouse queue. - legion:MissionCancel(self) - - end - - else - - -- Debug info. - self:T(self.lid..string.format("No legion, commander or chief. Attached groups will cancel the mission on their own. Will wait for mission DONE before evaluation!")) - - -- Loop over all groups. - for _,_groupdata in pairs(self.groupdata or {}) do - local groupdata=_groupdata --#AUFTRAG.GroupData - groupdata.opsgroup:MissionCancel(self) - end - - end - - -- Special mission states. - if self:IsPlanned() or self:IsQueued() or self:IsRequested() or Ngroups==0 then - self:T(self.lid..string.format("Cancelled mission was in %s stage with %d groups assigned and alive. Call it done!", self.status, Ngroups)) - self:Done() - end - -end - ---- On after "Done" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterDone(From, Event, To) - self.status=AUFTRAG.Status.DONE - self:T(self.lid..string.format("New mission status=%s", self.status)) - - -- Set time stamp. - self.Tover=timer.getAbsTime() - - -- Not executing any more. - self.Texecuting=nil - - -- Set status for CHIEF. - self.statusChief=AUFTRAG.Status.DONE - - -- Set status for COMMANDER. - self.statusCommander=AUFTRAG.Status.DONE - - -- Set status for LEGIONs. - for _,_legion in pairs(self.legions) do - local Legion=_legion --Ops.Legion#LEGION - - self:SetLegionStatus(Legion, AUFTRAG.Status.DONE) - - -- Remove pending request from legion queue. - if self.type==AUFTRAG.Type.RELOCATECOHORT then - - -- Get request ID - local requestid=self.requestID[Legion.alias] - - if requestid then - - -- Debug info. - self:T(self.lid.."Removing request from pending queue") - - -- Remove request from pending queue. - Legion:_DeleteQueueItemByID(requestid, Legion.pending) - - -- Remove cohort from old legion. - local Cohort=self.DCStask.params.cohort --Ops.Cohort#COHORT - Legion:DelCohort(Cohort) - - else - self:E(self.lid.."WARNING: Could NOT remove relocation request from from pending queue (all assets were spawned?)") - end - end - end - - -- Trigger relocated event. - if self.type==AUFTRAG.Type.RELOCATECOHORT then - local cohort=self.DCStask.params.cohort --Ops.Cohort#COHORT - cohort:Relocated() - end -end - ---- On after "Success" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterSuccess(From, Event, To) - - self.status=AUFTRAG.Status.SUCCESS - self:T(self.lid..string.format("New mission status=%s", self.status)) - - -- Set status for CHIEF, COMMANDER and LEGIONs - self.statusChief=self.status - self.statusCommander=self.status - for _,_legion in pairs(self.legions) do - local Legion=_legion --Ops.Legion#LEGION - self:SetLegionStatus(Legion, self.status) - end - - local repeatme=self.repeatedSuccess Repeat mission!", self.repeated+1, N)) - self:Repeat() - - else - - -- Stop mission. - self:T(self.lid..string.format("Mission SUCCESS! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) - self:Stop() - - end - -end - ---- On after "Failed" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterFailed(From, Event, To) - - self.status=AUFTRAG.Status.FAILED - self:T(self.lid..string.format("New mission status=%s", self.status)) - - -- Set status for CHIEF, COMMANDER and LEGIONs - self.statusChief=self.status - self.statusCommander=self.status - for _,_legion in pairs(self.legions) do - local Legion=_legion --Ops.Legion#LEGION - self:SetLegionStatus(Legion, self.status) - end - - local repeatme=self.repeatedFailure Repeat mission!", self.repeated+1, N)) - self:Repeat() - - else - - -- Stop mission. - self:T(self.lid..string.format("Mission FAILED! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) - self:Stop() - - end - -end - ---- On before "Repeat" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onbeforeRepeat(From, Event, To) - - if not (self.chief or self.commander or #self.legions>0) then - self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, COMMANDER or LEGION! Stopping AUFTRAG") - self:Stop() - return false - end - - return true -end - ---- On after "Repeat" event. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterRepeat(From, Event, To) - - -- Set mission status to PLANNED. - self.status=AUFTRAG.Status.PLANNED - - -- Debug info. - self:T(self.lid..string.format("New mission status=%s (on Repeat)", self.status)) - - -- Set status for CHIEF, COMMANDER and LEGIONs - self.statusChief=self.status - self.statusCommander=self.status - for _,_legion in pairs(self.legions) do - local Legion=_legion --Ops.Legion#LEGION - self:SetLegionStatus(Legion, self.status) - end - - -- Increase repeat counter. - self.repeated=self.repeated+1 - - if self.chief then - - -- Set status for chief. - self.statusChief=AUFTRAG.Status.PLANNED - - -- Remove mission from wingcommander because Chief will assign it again. - if self.commander then - self.statusCommander=AUFTRAG.Status.PLANNED - end - - -- Remove mission from legions because commander will assign it again but maybe to different legion(s). - for _,_legion in pairs(self.legions) do - local legion=_legion --Ops.Legion#LEGION - legion:RemoveMission(self) - end - - elseif self.commander then - - -- Set status for commander. - self.statusCommander=AUFTRAG.Status.PLANNED - - -- Remove mission from legion(s) because commander will assign it again but maybe to different legion(s). - for _,_legion in pairs(self.legions) do - local legion=_legion --Ops.Legion#LEGION - legion:RemoveMission(self) - self:SetLegionStatus(legion, AUFTRAG.Status.PLANNED) - end - - elseif #self.legions>0 then - - -- Remove mission from airwing because WC will assign it again but maybe to a different wing. - for _,_legion in pairs(self.legions) do - local legion=_legion --Ops.Legion#LEGION - legion:RemoveMission(self) - self:SetLegionStatus(legion, AUFTRAG.Status.PLANNED) - legion:AddMission(self) - end - - else - self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, COMMANDER or LEGION! Stopping AUFTRAG") - self:Stop() - return - end - - - -- No mission assets. - self.assets={} - - - -- Remove OPS groups. This also removes the mission from the OPSGROUP mission queue. - for groupname,_groupdata in pairs(self.groupdata) do - local groupdata=_groupdata --#AUFTRAG.GroupData - local opsgroup=groupdata.opsgroup - if opsgroup then - self:DelOpsGroup(opsgroup) - end - - end - -- No flight data. - self.groupdata={} - - -- Reset casualties and units assigned. - self.Ncasualties=0 - self.Nelements=0 - self.Ngroups=0 - self.Nassigned=nil - self.Ndead=0 - - -- Update DCS mission task. Could be that the initial task (e.g. for bombing) was destroyed. Then we need to update the coordinate. - self.DCStask=self:GetDCSMissionTask() - - -- Call status again. - self:__Status(-30) - -end - ---- On after "Stop" event. Remove mission from AIRWING and FLIGHTGROUP mission queues. --- @param #AUFTRAG self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AUFTRAG:onafterStop(From, Event, To) - - -- Debug info. - self:T(self.lid..string.format("STOPPED mission in status=%s. Removing missions from queues. Stopping CallScheduler!", self.status)) - - -- TODO: Mission should be OVER! we dont want to remove running missions from any queues. - - -- Remove mission from CHIEF queue. - if self.chief then - self.chief:RemoveMission(self) - end - - -- Remove mission from WINGCOMMANDER queue. - if self.commander then - self.commander:RemoveMission(self) - end - - -- Remove mission from LEGION queues. - if #self.legions>0 then - for _,_legion in pairs(self.legions) do - local legion=_legion --Ops.Legion#LEGION - legion:RemoveMission(self) - end - end - - -- Remove mission from OPSGROUP queue - for _,_groupdata in pairs(self.groupdata) do - local groupdata=_groupdata --#AUFTRAG.GroupData - groupdata.opsgroup:RemoveMission(self) - end - - -- No mission assets. - self.assets={} - - -- No flight data. - self.groupdata={} - - -- Clear pending scheduler calls. - self.CallScheduler:Clear() - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Target Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Create target data from a given object. --- @param #AUFTRAG self --- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC. -function AUFTRAG:_TargetFromObject(Object) - - if not self.engageTarget then - - if Object and Object:IsInstanceOf("TARGET") then - - self.engageTarget=Object - - else --if Object then - - self.engageTarget=TARGET:New(Object) - - end - - else - - -- Target was already specified elsewhere. - - end - - -- Debug info. - --self:T2(self.lid..string.format("Mission Target %s Type=%s, Ntargets=%d, Lifepoints=%d", self.engageTarget.lid, self.engageTarget.lid, self.engageTarget.N0, self.engageTarget:GetLife())) - - return self -end - - ---- Count alive mission targets. --- @param #AUFTRAG self --- @return #number Number of alive target units. -function AUFTRAG:CountMissionTargets() - - local N=0 - - if self.engageTarget then - N=self.engageTarget:CountTargets() - end - - return N -end - ---- Get initial number of targets. --- @param #AUFTRAG self --- @return #number Number of initial life points when mission was planned. -function AUFTRAG:GetTargetInitialNumber() - local target=self:GetTargetData() - if target then - return target.N0 - else - return 0 - end -end - - ---- Get target life points. --- @param #AUFTRAG self --- @return #number Number of initial life points when mission was planned. -function AUFTRAG:GetTargetInitialLife() - local target=self:GetTargetData() - if target then - return target.life0 - else - return 0 - end -end - ---- Get target damage. --- @param #AUFTRAG self --- @return #number Damage in percent. -function AUFTRAG:GetTargetDamage() - local target=self:GetTargetData() - if target then - return target:GetDamage() - else - return 0 - end -end - - ---- Get target life points. --- @param #AUFTRAG self --- @return #number Life points of target. -function AUFTRAG:GetTargetLife() - local target=self:GetTargetData() - if target then - return target:GetLife() - else - return 0 - end -end - ---- Get target. --- @param #AUFTRAG self --- @return Ops.Target#TARGET The target object. Could be many things. -function AUFTRAG:GetTargetData() - return self.engageTarget -end - ---- Get mission objective object. Could be many things depending on the mission type. --- @param #AUFTRAG self --- @return Wrapper.Positionable#POSITIONABLE The target object. Could be many things. -function AUFTRAG:GetObjective() - local objective=self:GetTargetData():GetObject() - return objective -end - ---- Get type of target. --- @param #AUFTRAG self --- @return #string The target type. -function AUFTRAG:GetTargetType() - local target=self.engageTarget - if target then - local to=target:GetObjective() - if to then - return to.Type - else - return "Unknown" - end - else - return "Unknown" - end -end - ---- Get 2D vector of target. --- @param #AUFTRAG self --- @return DCS#VEC2 The target 2D vector or *nil*. -function AUFTRAG:GetTargetVec2() - local coord=self:GetTargetCoordinate() - if coord then - local vec2=coord:GetVec2() - return vec2 - end - return nil -end - ---- Get coordinate of target. --- @param #AUFTRAG self --- @return Core.Point#COORDINATE The target coordinate or *nil*. -function AUFTRAG:GetTargetCoordinate() - - if self.transportPickup then - - -- Special case where we defined a - return self.transportPickup - - elseif self.engageTarget then - - local coord=self.engageTarget:GetCoordinate() - return coord - - elseif self.type==AUFTRAG.Type.ALERT5 then - - -- For example, COMMANDER will not assign a coordiante. This will be done later, when the mission is assigned to an airwing. - return nil - - else - self:T(self.lid.."ERROR: Cannot get target coordinate!") - end - - return nil -end - ---- Get heading of target. --- @param #AUFTRAG self --- @return #number Heading of target in degrees. -function AUFTRAG:GetTargetHeading() - if self.engageTarget then - local heading=self.engageTarget:GetHeading() - return heading - end - return nil -end - ---- Get name of the target. --- @param #AUFTRAG self --- @return #string Name of the target or "N/A". -function AUFTRAG:GetTargetName() - - if self.engageTarget then - local name=self.engageTarget:GetName() - return name - end - - return "N/A" -end - - ---- Get distance to target. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE FromCoord The coordinate from which the distance is measured. --- @return #number Distance in meters or 0. -function AUFTRAG:GetTargetDistance(FromCoord) - - local TargetCoord=self:GetTargetCoordinate() - - if TargetCoord and FromCoord then - return TargetCoord:Get2DDistance(FromCoord) - else - self:T(self.lid.."ERROR: TargetCoord or FromCoord does not exist in AUFTRAG:GetTargetDistance() function! Returning 0") - end - - return 0 -end - - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Misc Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Add asset to mission. --- @param #AUFTRAG self --- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be added to the mission. --- @return #AUFTRAG self -function AUFTRAG:AddAsset(Asset) - - -- Debug info - self:T(self.lid..string.format("Adding asset \"%s\" to mission", tostring(Asset.spawngroupname))) - - -- Add to table. - self.assets=self.assets or {} - - -- Add to table. - table.insert(self.assets, Asset) - - self.Nassigned=self.Nassigned or 0 - - self.Nassigned=self.Nassigned+1 - - return self -end - ---- Add assets to mission. --- @param #AUFTRAG self --- @param #table Assets List of assets. --- @return #AUFTRAG self -function AUFTRAG:_AddAssets(Assets) - - for _,asset in pairs(Assets) do - self:AddAsset(asset) - end - - return self -end - ---- Delete asset from mission. --- @param #AUFTRAG self --- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be removed. --- @return #AUFTRAG self -function AUFTRAG:DelAsset(Asset) - - for i,_asset in pairs(self.assets or {}) do - local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - - if asset.uid==Asset.uid then - self:T(self.lid..string.format("Removing asset \"%s\" from mission", tostring(Asset.spawngroupname))) - table.remove(self.assets, i) - return self - end - - end - - return self -end - ---- Get asset by its spawn group name. --- @param #AUFTRAG self --- @param #string Name Asset spawn group name. --- @return Functional.Warehouse#WAREHOUSE.Assetitem Asset. -function AUFTRAG:GetAssetByName(Name) - - for i,_asset in pairs(self.assets or {}) do - local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - - if asset.spawngroupname==Name then - return asset - end - - end - - return nil -end - ---- Count alive OPS groups assigned for this mission. --- @param #AUFTRAG self --- @return #number Number of alive OPS groups. -function AUFTRAG:CountOpsGroups() - local N=0 - for _,_groupdata in pairs(self.groupdata) do - local groupdata=_groupdata --#AUFTRAG.GroupData - if groupdata and groupdata.opsgroup and groupdata.opsgroup:IsAlive() and not groupdata.opsgroup:IsDead() then - N=N+1 - end - end - return N -end - ---- Count OPS groups in a certain status. --- @param #AUFTRAG self --- @param #string Status Status of group, e.g. `AUFTRAG.GroupStatus.EXECUTING`. --- @return #number Number of alive OPS groups. -function AUFTRAG:CountOpsGroupsInStatus(Status) - local N=0 - for _,_groupdata in pairs(self.groupdata) do - local groupdata=_groupdata --#AUFTRAG.GroupData - if groupdata and groupdata.status==Status then - N=N+1 - end - end - return N -end - - - ---- Get coordinate of target. First unit/group of the set is used. --- @param #AUFTRAG self --- @param #table MissionTypes A table of mission types. --- @return #string Comma separated list of mission types. -function AUFTRAG:GetMissionTypesText(MissionTypes) - - local text="" - for _,missiontype in pairs(MissionTypes) do - text=text..string.format("%s, ", missiontype) - end - - return text -end - ---- Set the mission waypoint coordinate where the mission is executed. Note that altitude is set via `:SetMissionAltitude`. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Coordinate where the mission is executed. --- @return #AUFTRAG self -function AUFTRAG:SetMissionWaypointCoord(Coordinate) - - -- Obviously a zone was passed. We get the coordinate. - if Coordinate:IsInstanceOf("ZONE_BASE") then - Coordinate=Coordinate:GetCoordinate() - end - - self.missionWaypointCoord=Coordinate - return self -end - ---- Set randomization of the mission waypoint coordinate. Each assigned group will get a random ingress coordinate, where the mission is executed. --- @param #AUFTRAG self --- @param #number Radius Distance in meters. Default `#nil`. --- @return #AUFTRAG self -function AUFTRAG:SetMissionWaypointRandomization(Radius) - self.missionWaypointRadius=Radius - return self -end - ---- Set the mission egress coordinate. This is the coordinate where the assigned group will go once the mission is finished. --- @param #AUFTRAG self --- @param Core.Point#COORDINATE Coordinate Egrees coordinate. --- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate. --- @return #AUFTRAG self -function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude) - - -- Obviously a zone was passed. We get the coordinate. - if Coordinate:IsInstanceOf("ZONE_BASE") then - Coordinate=Coordinate:GetCoordinate() - end - - self.missionEgressCoord=Coordinate - - if Altitude then - self.missionEgressCoord.y=UTILS.FeetToMeters(Altitude) - end -end - ---- Get the mission egress coordinate if this was defined. --- @param #AUFTRAG self --- @return Core.Point#COORDINATE Coordinate Coordinate or nil. -function AUFTRAG:GetMissionEgressCoord() - return self.missionEgressCoord -end - ---- Get coordinate which was set as mission waypoint coordinate. --- @param #AUFTRAG self --- @return Core.Point#COORDINATE Coordinate where the mission is executed or `#nil`. -function AUFTRAG:_GetMissionWaypointCoordSet() - - -- Check if a coord has been explicitly set. - if self.missionWaypointCoord then - local coord=self.missionWaypointCoord - if self.missionAltitude then - coord.y=self.missionAltitude - end - - - return coord - end - -end - ---- Get coordinate of target. First unit/group of the set is used. --- @param #AUFTRAG self --- @param Wrapper.Group#GROUP group Group. --- @param #number randomradius Random radius in meters. --- @param #table surfacetypes Surface types of random zone. --- @return Core.Point#COORDINATE Coordinate where the mission is executed. -function AUFTRAG:GetMissionWaypointCoord(group, randomradius, surfacetypes) - - -- Check if a coord has been explicitly set. - if self.missionWaypointCoord then - local coord=self.missionWaypointCoord - if self.missionAltitude then - coord.y=self.missionAltitude - end - return coord - end - - -- Create waypoint coordinate half way between us and the target. - local waypointcoord=group:GetCoordinate():GetIntermediateCoordinate(self:GetTargetCoordinate(), self.missionFraction) - local alt=waypointcoord.y - - -- Add some randomization. - if randomradius then - waypointcoord=ZONE_RADIUS:New("Temp", waypointcoord:GetVec2(), randomradius):GetRandomCoordinate(nil, nil, surfacetypes):SetAltitude(alt, false) - end - - -- Set altitude of mission waypoint. - if self.missionAltitude then - waypointcoord:SetAltitude(self.missionAltitude, true) - end - - return waypointcoord -end - - ---- Set log ID string. --- @param #AUFTRAG self --- @return #AUFTRAG self -function AUFTRAG:_SetLogID() - self.lid=string.format("Auftrag #%d %s | ", self.auftragsnummer, tostring(self.type)) - return self -end - - ---- Update DCS task. --- @param #AUFTRAG self --- @return #AUFTRAG self -function AUFTRAG:_UpdateTask() - - - - return self -end - ---- Update mission F10 map marker. --- @param #AUFTRAG self --- @return #AUFTRAG self -function AUFTRAG:UpdateMarker() - - -- Marker text. - local text=string.format("%s %s: %s", self.name, self.type:upper(), self.status:upper()) - text=text..string.format("\n%s", self:GetTargetName()) - text=text..string.format("\nTargets %d/%d, Life Points=%d/%d", self:CountMissionTargets(), self:GetTargetInitialNumber(), self:GetTargetLife(), self:GetTargetInitialLife()) - text=text..string.format("\nOpsGroups %d/%d", self:CountOpsGroups(), self:GetNumberOfRequiredAssets()) - - if not self.marker then - - -- Get target coordinates. Can be nil! - local targetcoord=self:GetTargetCoordinate() - - if self.markerCoaliton and self.markerCoaliton>=0 then - self.marker=MARKER:New(targetcoord, text):ReadOnly():ToCoalition(self.markerCoaliton) - else - self.marker=MARKER:New(targetcoord, text):ReadOnly():ToAll() - end - - else - - if self.marker:GetText()~=text then - self.marker:UpdateText(text) - end - - end - - return self -end - ---- Get DCS task table for the given mission. --- @param #AUFTRAG self --- @return DCS#Task The DCS task table. If multiple tasks are necessary, this is returned as a combo task. -function AUFTRAG:GetDCSMissionTask() - - local DCStasks={} - - -- Create DCS task based on current self. - if self.type==AUFTRAG.Type.ANTISHIP then - - ---------------------- - -- ANTISHIP Mission -- - ---------------------- - - -- Add enroute anti-ship task. - local DCStask=CONTROLLABLE.EnRouteTaskAntiShip(nil) - table.insert(self.enrouteTasks, DCStask) - - self:_GetDCSAttackTask(self.engageTarget, DCStasks) - - elseif self.type==AUFTRAG.Type.AWACS then - - ------------------- - -- AWACS Mission -- - ------------------- - - local DCStask=CONTROLLABLE.EnRouteTaskAWACS(nil) - - table.insert(self.enrouteTasks, DCStask) - - elseif self.type==AUFTRAG.Type.BAI then - - ----------------- - -- BAI Mission -- - ----------------- - - self:_GetDCSAttackTask(self.engageTarget, DCStasks) - - elseif self.type==AUFTRAG.Type.BOMBING then - - --------------------- - -- BOMBING Mission -- - --------------------- - - local DCStask=CONTROLLABLE.TaskBombing(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, Divebomb) - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.BOMBRUNWAY then - - ------------------------ - -- BOMBRUNWAY Mission -- - ------------------------ - - local DCStask=CONTROLLABLE.TaskBombingRunway(nil, self.engageTarget:GetObject(), self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAsGroup) - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.BOMBCARPET then - - ------------------------ - -- BOMBCARPET Mission -- - ------------------------ - - local DCStask=CONTROLLABLE.TaskCarpetBombing(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, self.engageCarpetLength) - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.CAP then - - ----------------- - -- CAP Mission -- - ----------------- - - local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, self.engageZone:GetVec2(), self.engageZone:GetRadius(), self.engageTargetTypes, Priority) - - table.insert(self.enrouteTasks, DCStask) - - elseif self.type==AUFTRAG.Type.CAS then - - ----------------- - -- CAS Mission -- - ----------------- - - local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, self.engageZone:GetVec2(), self.engageZone:GetRadius(), self.engageTargetTypes, Priority) - - table.insert(self.enrouteTasks, DCStask) - - elseif self.type==AUFTRAG.Type.ESCORT then - - -------------------- - -- ESCORT Mission -- - -------------------- - - local DCStask=CONTROLLABLE.TaskEscort(nil, self.engageTarget:GetObject(), self.escortVec3, LastWaypointIndex, self.engageMaxDistance, self.engageTargetTypes) - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.FACA then - - ----------------- - -- FAC Mission -- - ----------------- - - local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil, self.engageTarget:GetObject(), self.engageWeaponType, self.facDesignation, self.facDatalink, self.facFreq, self.facModu, CallsignName, CallsignNumber) - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.FERRY then - - ------------------- - -- FERRY Mission -- - ------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.FERRY - - -- We create a "fake" DCS task. - local param={} - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.RELOCATECOHORT then - - ---------------------- - -- RELOCATE Mission -- - ---------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.RELOCATECOHORT - - -- We create a "fake" DCS task. - local param={} - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.INTERCEPT then - - ----------------------- - -- INTERCEPT Mission -- - ----------------------- - - self:_GetDCSAttackTask(self.engageTarget, DCStasks) - - elseif self.type==AUFTRAG.Type.ORBIT then - - ------------------- - -- ORBIT Mission -- - ------------------- - - -- Done below as also other mission types use the orbit task. - - elseif self.type==AUFTRAG.Type.GCICAP then - - -------------------- - -- GCICAP Mission -- - -------------------- - - -- Done below as also other mission types use the orbit task. - - elseif self.type==AUFTRAG.Type.RECON then - - ------------------- - -- RECON Mission -- - ------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.RECON - - -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. - local param={} - param.target=self.engageTarget - param.altitude=self.missionAltitude - param.speed=self.missionSpeed - param.lastindex=nil - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.SEAD then - - ------------------ - -- SEAD Mission -- - ------------------ - - --[[ - local DCStask=CONTROLLABLE.EnRouteTaskEngageTargets(nil, nil ,{"Air Defence"} , 0) - table.insert(self.enrouteTasks, DCStask) - DCStask.key="SEAD" - ]] - - self:_GetDCSAttackTask(self.engageTarget, DCStasks) - - elseif self.type==AUFTRAG.Type.STRIKE then - - -------------------- - -- STRIKE Mission -- - -------------------- - - local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.TANKER or self.type==AUFTRAG.Type.RECOVERYTANKER then - - -------------------- - -- TANKER Mission -- - -------------------- - - local DCStask=CONTROLLABLE.EnRouteTaskTanker(nil) - - table.insert(self.enrouteTasks, DCStask) - - elseif self.type==AUFTRAG.Type.TROOPTRANSPORT then - - ---------------------------- - -- TROOPTRANSPORT Mission -- - ---------------------------- - - -- Task to embark the troops at the pick up point. - local TaskEmbark=CONTROLLABLE.TaskEmbarking(TaskControllable, self.transportPickup, self.transportGroupSet, self.transportWaitForCargo) - - -- Task to disembark the troops at the drop off point. - local TaskDisEmbark=CONTROLLABLE.TaskDisembarking(TaskControllable, self.transportDropoff, self.transportGroupSet) - - table.insert(DCStasks, TaskEmbark) - table.insert(DCStasks, TaskDisEmbark) - - elseif self.type==AUFTRAG.Type.OPSTRANSPORT then - - -------------------------- - -- OPSTRANSPORT Mission -- - -------------------------- - - local DCStask={} - - DCStask.id="OpsTransport" - - -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. - local param={} - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.CARGOTRANSPORT then - - ---------------------------- - -- CARGOTRANSPORT Mission -- - ---------------------------- - - -- Task to transport cargo. - local TaskCargoTransportation={ - id = "CargoTransportation", - params = {} - } - - table.insert(DCStasks, TaskCargoTransportation) - - elseif self.type==AUFTRAG.Type.RESCUEHELO then - - ------------------------- - -- RESCUE HELO Mission -- - ------------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.FORMATION - - -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. - local param={} - param.unitname=self:GetTargetName() - param.offsetX=200 - param.offsetZ=240 - param.altitude=70 - param.dtFollow=1.0 - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.ARTY then - - ------------------ - -- ARTY Mission -- - ------------------ - - - if self.artyShots==1 or self.artyRadius<10 or true then - - local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, self:GetTargetVec2(), self.artyRadius, self.artyShots, self.engageWeaponType, self.artyAltitude) - table.insert(DCStasks, DCStask) - - else - - local Vec2=self:GetTargetVec2() - - local zone=ZONE_RADIUS:New("temp", Vec2, self.artyRadius) - - for i=1,self.artyShots do - - local vec2=zone:GetRandomVec2() - - local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, vec2, 0, 1, self.engageWeaponType, self.artyAltitude) - table.insert(DCStasks, DCStask) - - end - - end - - elseif self.type==AUFTRAG.Type.BARRAGE then - - --------------------- - -- BARRAGE Mission -- - --------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.BARRAGE - - -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. - local param={} - param.zone=self:GetObjective() - param.altitude=self.artyAltitude - param.radius=self.artyRadius - param.heading=self.artyHeading - param.angle=self.artyAngle - param.shots=self.artyShots - param.weaponTypoe=self.engageWeaponType - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.PATROLZONE then - - ------------------------- - -- PATROL ZONE Mission -- - ------------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.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.CASENHANCED then - - ------------------------- - -- CAS ENHANCED Mission -- - ------------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.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.GROUNDATTACK then - - --------------------------- - -- GROUND ATTACK Mission -- - --------------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.GROUNDATTACK - - -- We create a "fake" DCS task and pass the parameters to the ARMYGROUP. - local param={} - param.target=self:GetTargetData() - param.action="Wedge" - param.speed=self.missionSpeed - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.AMMOSUPPLY then - - ------------------------- - -- AMMO SUPPLY Mission -- - ------------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.AMMOSUPPLY - - -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. - local param={} - param.zone=self:GetObjective() - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.FUELSUPPLY then - - ------------------------- - -- FUEL SUPPLY Mission -- - ------------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.FUELSUPPLY - - -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. - local param={} - param.zone=self:GetObjective() - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.AMMOSUPPLY then - - ---------------------- - -- REARMING Mission -- - ---------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.REARMING - - -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. - local param={} - param.zone=self:GetObjective() - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.ALERT5 then - - --------------------- - -- ALERT 5 Mission -- - --------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.ALERT5 - - -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. - local param={} - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.NOTHING then - - --------------------- - -- NOTHING Mission -- - --------------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.NOTHING - - -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. - local param={} - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.HOVER then - - --------------------- - -- HOVER Mission -- - --------------------- - - local DCStask={} - DCStask.id=AUFTRAG.SpecialTask.HOVER - - local param={} - - param.hoverAltitude=self.hoverAltitude - param.hoverTime = self.hoverTime - param.missionSpeed = self.missionSpeed - param.missionAltitude = self.missionAltitude - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.ONGUARD or self.type==AUFTRAG.Type.ARMOREDGUARD then - - ---------------------- - -- ON GUARD Mission -- - ---------------------- - - local DCStask={} - - DCStask.id= self.type==AUFTRAG.Type.ONGUARD and AUFTRAG.SpecialTask.ONGUARD or AUFTRAG.SpecialTask.ARMOREDGUARD - - -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. - local param={} - param.coordinate=self:GetObjective() - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.AIRDEFENSE then - - ------------------------ - -- AIRDEFENSE Mission -- - ------------------------ - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.AIRDEFENSE - - -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. - local param={} - param.zone=self:GetObjective() - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - elseif self.type==AUFTRAG.Type.EWR then - - ----------------- - -- EWR Mission -- - ----------------- - - local DCStask={} - - DCStask.id=AUFTRAG.SpecialTask.EWR - - -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. - local param={} - param.zone=self:GetObjective() - - DCStask.params=param - - table.insert(DCStasks, DCStask) - - -- EWR is an enroute task - local Enroutetask=CONTROLLABLE.EnRouteTaskEWR() - table.insert(self.enrouteTasks, Enroutetask) - - else - self:T(self.lid..string.format("ERROR: Unknown mission task!")) - return nil - end - - - -- Set ORBIT task. Also applies to other missions: AWACS, TANKER, CAP, CAS. - if self.type==AUFTRAG.Type.ORBIT or - self.type==AUFTRAG.Type.CAP or - self.type==AUFTRAG.Type.CAS or - self.type==AUFTRAG.Type.GCICAP or - self.type==AUFTRAG.Type.AWACS or - self.type==AUFTRAG.Type.TANKER or - self.type==AUFTRAG.Type.RECOVERYTANKER then - - ------------------- - -- ORBIT Mission -- - ------------------- - - -- Get/update orbit vector. - self.orbitVec2=self:GetTargetVec2() - - if self.orbitVec2 then - - -- Heading of the target. - self.targetHeading=self:GetTargetHeading() - - local OffsetVec2=nil --DCS#Vec2 - if (self.orbitOffsetVec2~=nil) then - OffsetVec2=UTILS.DeepCopy(self.orbitOffsetVec2) - end - - if OffsetVec2 then - - if self.orbitOffsetVec2.r then - -- Polar coordinates - local r=self.orbitOffsetVec2.r - local phi=(self.orbitOffsetVec2.phi or 0) + self.targetHeading - - OffsetVec2.x=r*math.cos(math.rad(phi)) - OffsetVec2.y=r*math.sin(math.rad(phi)) - else - -- Cartesian coordinates - OffsetVec2.x=self.orbitOffsetVec2.x - OffsetVec2.y=self.orbitOffsetVec2.y - end - - end - - -- Actual orbit position with possible offset. - local orbitVec2=OffsetVec2 and UTILS.Vec2Add(self.orbitVec2, OffsetVec2) or self.orbitVec2 - - -- Check for race-track pattern. - local orbitRaceTrack=nil --DCS#Vec2 - if self.orbitLeg then - - -- Default heading is due North. - local heading=0 - - -- Check if specific heading was specified. - if self.orbitHeading then - - -- Is heading realtive to target? - if self.orbitHeadingRel then - -- Relative heading wrt target. - heading=self.targetHeading+self.orbitHeading - else - -- Take given heading. - heading=self.orbitHeading - end - - else - -- Not specific heading specified ==> Take heading of target. - heading=self.targetHeading or 0 - end - - -- Race-track vector. - orbitRaceTrack=UTILS.Vec2Translate(orbitVec2, self.orbitLeg, heading) - end - - local orbitRaceTrackCoord = nil - if orbitRaceTrack then - orbitRaceTrackCoord = COORDINATE:NewFromVec2(orbitRaceTrack) - end - - -- Create orbit task. - local DCStask=CONTROLLABLE.TaskOrbit(nil, COORDINATE:NewFromVec2(orbitVec2), self.orbitAltitude, self.orbitSpeed, orbitRaceTrackCoord) - - -- Add DCS task. - table.insert(DCStasks, DCStask) - - end - - end - - -- Debug info. - self:T3({missiontask=DCStasks}) - - -- Return the task. - if #DCStasks==1 then - return DCStasks[1] - else - return CONTROLLABLE.TaskCombo(nil, DCStasks) - end - -end - ---- Get DCS task table for an attack group or unit task. --- @param #AUFTRAG self --- @param Ops.Target#TARGET Target Target data. --- @param #table DCStasks DCS DCS tasks table to which the task is added. --- @return DCS#Task The DCS task table. -function AUFTRAG:_GetDCSAttackTask(Target, DCStasks) - - DCStasks=DCStasks or {} - - for _,_target in pairs(Target.targets) do - local target=_target --Ops.Target#TARGET.Object - - if target.Type==TARGET.ObjectType.GROUP then - - local DCStask=CONTROLLABLE.TaskAttackGroup(nil, target.Object, self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageAsGroup) - - table.insert(DCStasks, DCStask) - - elseif target.Type==TARGET.ObjectType.UNIT or target.Type==TARGET.ObjectType.STATIC then - - local DCStask=CONTROLLABLE.TaskAttackUnit(nil, target.Object, self.engageAsGroup, self.WeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) - - table.insert(DCStasks, DCStask) - - end - - end - - return DCStasks -end - ---- Get DCS task table for an attack group or unit task. --- @param #AUFTRAG self --- @param #string MissionType Mission (AUFTAG) type. --- @return #string DCS mission task for the auftrag type. -function AUFTRAG:GetMissionTaskforMissionType(MissionType) - - local mtask=ENUMS.MissionTask.NOTHING - - if MissionType==AUFTRAG.Type.ANTISHIP then - mtask=ENUMS.MissionTask.ANTISHIPSTRIKE - elseif MissionType==AUFTRAG.Type.AWACS then - mtask=ENUMS.MissionTask.AWACS - elseif MissionType==AUFTRAG.Type.BAI then - mtask=ENUMS.MissionTask.GROUNDATTACK - elseif MissionType==AUFTRAG.Type.BOMBCARPET then - mtask=ENUMS.MissionTask.GROUNDATTACK - elseif MissionType==AUFTRAG.Type.BOMBING then - mtask=ENUMS.MissionTask.GROUNDATTACK - elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then - mtask=ENUMS.MissionTask.RUNWAYATTACK - elseif MissionType==AUFTRAG.Type.CAP then - mtask=ENUMS.MissionTask.CAP - elseif MissionType==AUFTRAG.Type.GCICAP then - mtask=ENUMS.MissionTask.CAP - elseif MissionType==AUFTRAG.Type.CAS then - 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 - mtask=ENUMS.MissionTask.AFAC - elseif MissionType==AUFTRAG.Type.FERRY then - mtask=ENUMS.MissionTask.NOTHING - elseif MissionType==AUFTRAG.Type.INTERCEPT then - mtask=ENUMS.MissionTask.INTERCEPT - elseif MissionType==AUFTRAG.Type.RECON then - mtask=ENUMS.MissionTask.RECONNAISSANCE - elseif MissionType==AUFTRAG.Type.SEAD then - mtask=ENUMS.MissionTask.SEAD - elseif MissionType==AUFTRAG.Type.STRIKE then - mtask=ENUMS.MissionTask.GROUNDATTACK - elseif MissionType==AUFTRAG.Type.TANKER then - mtask=ENUMS.MissionTask.REFUELING - elseif MissionType==AUFTRAG.Type.TROOPTRANSPORT then - mtask=ENUMS.MissionTask.TRANSPORT - elseif MissionType==AUFTRAG.Type.CARGOTRANSPORT then - mtask=ENUMS.MissionTask.TRANSPORT - elseif MissionType==AUFTRAG.Type.ARMORATTACK then - mtask=ENUMS.MissionTask.NOTHING - elseif MissionType==AUFTRAG.Type.HOVER then - mtask=ENUMS.MissionTask.NOTHING - end - - return mtask -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Global Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Checks if a mission type is contained in a table of possible types. --- @param #string MissionType The requested mission type. --- @param #table PossibleTypes A table with possible mission types. --- @return #boolean If true, the requested mission type is part of the possible mission types. -function AUFTRAG.CheckMissionType(MissionType, PossibleTypes) - - if type(PossibleTypes)=="string" then - PossibleTypes={PossibleTypes} - end - - for _,canmission in pairs(PossibleTypes) do - if canmission==MissionType then - return true - end - end - - return false -end - ---- Check if a mission type is contained in a list of possible capabilities. --- @param #table MissionTypes The requested mission type. Can also be passed as a single mission type `#string`. --- @param #table Capabilities A table with possible capabilities `Ops.Auftrag#AUFTRAG.Capability`. --- @param #boolean All If `true`, given mission type must be includedin ALL capabilities. If `false` or `nil`, it must only match one. --- @return #boolean If true, the requested mission type is part of the possible mission types. -function AUFTRAG.CheckMissionCapability(MissionTypes, Capabilities, All) - - -- Ensure table. - if type(MissionTypes)~="table" then - MissionTypes={MissionTypes} - end - - for _,cap in pairs(Capabilities) do - local capability=cap --Ops.Auftrag#AUFTRAG.Capability - for _,MissionType in pairs(MissionTypes) do - if All==true then - if capability.MissionType~=MissionType then - return false - end - else - if capability.MissionType==MissionType then - return true - end - end - end - end - - if All==true then - return true - else - return false - end -end - - ---- Check if a mission type is contained in a list of possible capabilities. --- @param #table MissionTypes The requested mission type. Can also be passed as a single mission type `#string`. --- @param #table Capabilities A table with possible capabilities `Ops.Auftrag#AUFTRAG.Capability`. --- @return #boolean If true, the requested mission type is part of the possible mission types. -function AUFTRAG.CheckMissionCapabilityAny(MissionTypes, Capabilities) - - local res=AUFTRAG.CheckMissionCapability(MissionTypes, Capabilities, false) - - return res -end - - ---- Check if a mission type is contained in a list of possible capabilities. --- @param #table MissionTypes The requested mission type. Can also be passed as a single mission type `#string`. --- @param #table Capabilities A table with possible capabilities `Ops.Auftrag#AUFTRAG.Capability`. --- @return #boolean If true, the requested mission type is part of the possible mission types. -function AUFTRAG.CheckMissionCapabilityAll(MissionTypes, Capabilities) - - local res=AUFTRAG.CheckMissionCapability(MissionTypes, Capabilities, true) - - return res -end - - - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -- -- ### Author: **funkyfranky** -- @@ -6348,6 +66,8 @@ end -- @field #table conditionSuccess If all conditions are true, the mission is cancelled. -- @field #table conditionFailure If all conditions are true, the mission is cancelled. -- @field #table conditionPush If all conditions are true, the mission is executed. Before, the group(s) wait at the mission execution waypoint. +-- @field #boolean conditionSuccessSet +-- @field #boolean conditionFailureSet -- -- @field #number orbitSpeed Orbit speed in m/s. -- @field #number orbitAltitude Orbit altitude in meters. @@ -6660,6 +380,8 @@ AUFTRAG = { conditionSuccess = {}, conditionFailure = {}, conditionPush = {}, + conditionSuccessSet = false, + conditionFailureSet = false, } --- Global mission counter. @@ -6917,7 +639,7 @@ AUFTRAG.Category={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="0.9.9" +AUFTRAG.version="0.9.10" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -7732,10 +1454,14 @@ function AUFTRAG:NewCASENHANCED(CasZone, Altitude, Speed, RangeMax, NoEngageZone mission.missionFraction=1.0 mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or nil - + + -- Evaluate result after x secs. We might need time until targets have been detroyed. + mission.dTevaluate=15 + mission.categories={AUFTRAG.Category.AIRCRAFT} mission.DCStask=mission:GetDCSMissionTask() + return mission end @@ -9714,7 +3440,9 @@ function AUFTRAG:AddConditionSuccess(ConditionFunction, ...) end table.insert(self.conditionSuccess, condition) - + + self.conditionSuccessSet = true + return self end @@ -9734,7 +3462,9 @@ function AUFTRAG:AddConditionFailure(ConditionFunction, ...) end table.insert(self.conditionFailure, condition) - + + self.conditionFailureSet = true + return self end @@ -10027,7 +3757,7 @@ function AUFTRAG:IsReadyToGo() return true end ---- Check if mission is ready to be started. +--- Check if mission is ready to be cancelled. -- * Mission stop already passed. -- * Any stop condition is true. -- @param #AUFTRAG self @@ -10284,7 +4014,18 @@ function AUFTRAG:onafterStatus(From, Event, To) end self:I(self.lid..text) end - + + -- check conditions if set + if self.conditionFailureSet then + local failed = self:EvalConditionsAny(self.conditionFailure) + if failed then self:__Failed(-1) end + end + + if self.conditionSuccessSet then + local success = self:EvalConditionsAny(self.conditionSuccess) + if success then self:__Success(-1) end + end + -- Ready to evaluate mission outcome? local ready2evaluate=self.Tover and Tnow-self.Tover>=self.dTevaluate or false From b62058b36a7509b17db18b83679640c3b1560044 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 22 Jan 2023 13:10:45 +0100 Subject: [PATCH 192/603] changes --- Moose Development/Moose/Core/Event.lua | 8 ++++--- Moose Development/Moose/Core/Zone.lua | 2 +- Moose Development/Moose/Ops/CTLD.lua | 31 ++++++++++++++++++-------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 595e094f4..1ce9b2cae 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -1280,9 +1280,11 @@ function EVENT:onEvent( Event ) --local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :( -- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground. --Event.Place=UNIT:Find(Event.place) - else - Event.Place=AIRBASE:Find(Event.place) - Event.PlaceName=Event.Place:GetName() + else + if Event.place:isExist() and Event.place:getCategory() ~= Object.Category.SCENERY then + Event.Place=AIRBASE:Find(Event.place) + Event.PlaceName=Event.Place:GetName() + end end end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 7475c20e3..a38484361 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1387,7 +1387,7 @@ end -- @param #ZONE_RADIUS self -- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0 m. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone in meters. Default is the radius of the zone. --- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type! +-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 100 times to find the right type! -- @return Core.Point#COORDINATE The random coordinate. function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 23d0252a9..3becb3d03 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update December 2022 +-- Last Update Jan 2023 do @@ -712,6 +712,8 @@ do -- my_ctld.usesubcats = false -- use sub-category names for crates, adds an extra menu layer in "Get Crates", useful if you have > 10 crate types. -- my_ctld.placeCratesAhead = false -- place crates straight ahead of the helicopter, in a random way. If true, crates are more neatly sorted. -- my_ctld.nobuildinloadzones = true -- forbid players to build stuff in LOAD zones if set to `true` +-- my_ctld.movecratesbeforebuild = true -- crates must be moved once before they can be build. Set to false for direct builds. +-- my_ctld.surfacetypes = {land.SurfaceType.LAND,land.SurfaceType.ROAD,land.SurfaceType.RUNWAY,land.SurfaceType.SHALLOW_WATER} -- surfaces for loading back objects -- -- ## 2.1 User functions -- @@ -1089,7 +1091,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.26" +CTLD.version="1.0.27" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1257,6 +1259,8 @@ function CTLD:New(Coalition, Prefixes, Alias) -- disallow building in loadzones self.nobuildinloadzones = true + self.movecratesbeforebuild = true + self.surfacetypes = {land.SurfaceType.LAND,land.SurfaceType.ROAD,land.SurfaceType.RUNWAY,land.SurfaceType.SHALLOW_WATER} local AliaS = string.gsub(self.alias," ","_") self.filename = string.format("CTLD_%s_Persist.csv",AliaS) @@ -2901,7 +2905,7 @@ function CTLD:_BuildCrates(Group, Unit,Engineering) -- get dropped crates for _,_crate in pairs(crates) do local Crate = _crate -- #CTLD_CARGO - if Crate:WasDropped() and not Crate:IsRepair() and not Crate:IsStatic() then + if (Crate:WasDropped() or not self.movecratesbeforebuild) and not Crate:IsRepair() and not Crate:IsStatic() then -- we can build these - maybe local name = Crate:GetName() local required = Crate:GetCratesNeeded() @@ -2946,7 +2950,12 @@ function CTLD:_BuildCrates(Group, Unit,Engineering) local text = string.format("Type: %s | Required %d | Found %d | Can Build %s", name, needed, found, txtok) report:Add(text) end -- end list buildables - if not foundbuilds then report:Add(" --- None Found ---") end + if not foundbuilds then + report:Add(" --- None found! ---") + if self.movecratesbeforebuild then + report:Add("*** Crates need to be moved before building!") + end + end report:Add("------------------------------------------------------------") local text = report:Text() if not Engineering then @@ -4339,6 +4348,8 @@ end -- @param #CTLD self -- @param Core.Zone#ZONE Zone The zone where to drop the troops. -- @param Ops.CTLD#CTLD_CARGO Cargo The #CTLD_CARGO object to spawn. + -- @param #table Surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type! + -- @param #boolean PreciseLocation (Optional) Don't try to get a random position in the zone but use the dead center. Caution not to stack up stuff on another! -- @return #CTLD self -- @usage Use this function to pre-populate the field with Troops or Engineers at a random coordinate in a zone: -- -- create a matching #CTLD_CARGO type @@ -4346,8 +4357,8 @@ end -- -- get a #ZONE object -- local dropzone = ZONE:New("InjectZone") -- Core.Zone#ZONE -- -- and go: - -- my_ctld:InjectTroops(dropzone,InjectTroopsType) - function CTLD:InjectTroops(Zone,Cargo) + -- my_ctld:InjectTroops(dropzone,InjectTroopsType,{land.SurfaceType.LAND}) + function CTLD:InjectTroops(Zone,Cargo,Surfacetypes,PreciseLocation) self:T(self.lid.." InjectTroops") local cargo = Cargo -- #CTLD_CARGO @@ -4379,8 +4390,10 @@ end local temptable = cargo:GetTemplates() or {} local factor = 1.5 local zone = Zone - - local randomcoord = zone:GetRandomCoordinate(10,30*factor):GetVec2() + local randomcoord = zone:GetRandomCoordinate(10,30*factor,Surfacetypes):GetVec2() + if PreciseLocation then + randomcoord = zone:GetCoordinate():GetVec2() + end for _,_template in pairs(temptable) do self.TroopCounter = self.TroopCounter + 1 local alias = string.format("%s-%d", _template, math.random(1,100000)) @@ -5082,7 +5095,7 @@ end self:InjectVehicles(dropzone,injectvehicle) elseif cargotype == CTLD_CARGO.Enum.TROOPS or cargotype == CTLD_CARGO.Enum.ENGINEERS then local injecttroops = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) - self:InjectTroops(dropzone,injecttroops) + self:InjectTroops(dropzone,injecttroops,self.surfacetypes) end elseif (type(groupname) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR then local cargotemplates = dataset[6] From cad8f8678ed613d86723d50e2377d44980be7b72 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 23 Jan 2023 10:53:58 +0100 Subject: [PATCH 193/603] Adding ACLS --- .../Moose/Wrapper/Controllable.lua | 101 +++++++++--------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 97e1a4e42..e2d2d0bdd 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -668,6 +668,53 @@ function CONTROLLABLE:CommandActivateBeacon( Type, System, Frequency, UnitID, Ch return self end +--- Activate ACLS system of the CONTROLLABLE. The controllable should be an aircraft carrier! Also needs Link4 to work. +-- @param #CONTROLLABLE self +-- @param #number UnitID The DCS UNIT ID of the unit the ACLS system is attached to. Useful if more units are in one group. +-- @param #string Name (Optional) Name of the ACLS Beacon +-- @param #number Delay (Optional) Delay in seconds before the ICLS is deactivated. +-- @return #CONTROLLABLE self +function CONTROLLABLE:CommandActivateACLS( UnitID, Name, Delay ) + + -- Command to activate ACLS system. + local CommandActivateACLS= { + id = 'ActivateACLS', + params = { + unitId = UnitID or self:GetID(), + name = Name, + } +} + + if Delay and Delay > 0 then + SCHEDULER:New( nil, self.CommandActivateACLS, { self, UnitID, Name }, Delay ) + else + self:SetCommand( CommandActivateACLS ) + end + + return self +end + +--- Deactivate ACLS system of the CONTROLLABLE. The controllable should be an aircraft carrier! +-- @param #CONTROLLABLE self +-- @param #number Delay (Optional) Delay in seconds before the ICLS is deactivated. +-- @return #CONTROLLABLE self +function CONTROLLABLE:CommandDeactivateACLS( Delay ) + + -- Command to activate ACLS system. + local CommandDeactivateACLS= { + id = 'DeactivateACLS', + params = { } +} + + if Delay and Delay > 0 then + SCHEDULER:New( nil, self.CommandDeactivateACLS, { self }, Delay ) + else + self:SetCommand( CommandDeactivateACLS ) + end + + return self +end + --- Activate ICLS system of the CONTROLLABLE. The controllable should be an aircraft carrier! -- @param #CONTROLLABLE self -- @param #number Channel ICLS channel. @@ -683,13 +730,13 @@ function CONTROLLABLE:CommandActivateICLS( Channel, UnitID, Callsign, Delay ) params = { ["type"] = BEACON.Type.ICLS, ["channel"] = Channel, - ["unitId"] = UnitID, + ["unitId"] = UnitID or self:GetID(), ["callsign"] = Callsign, }, } if Delay and Delay > 0 then - SCHEDULER:New( nil, self.CommandActivateICLS, { self }, Delay ) + SCHEDULER:New( nil, self.CommandActivateICLS, { self, Channel, UnitID, Callsign }, Delay ) else self:SetCommand( CommandActivateICLS ) end @@ -711,41 +758,13 @@ function CONTROLLABLE:CommandActivateLink4(Frequency, UnitID, Callsign, Delay) id = "ActivateLink4", params= { ["frequency "] = Frequency*1000, - ["unitId"] = UnitID, + ["unitId"] = UnitID or self:GetID(), ["name"] = Callsign, } } if Delay and Delay>0 then - SCHEDULER:New(nil, self.CommandActivateLink4, {self}, Delay) - else - self:SetCommand(CommandActivateLink4) - end - - return self -end - ---- Activate LINK4 system of the CONTROLLABLE. The controllable should be an aircraft carrier! --- @param #CONTROLLABLE self --- @param #number Frequency Link4 Frequency in MHz, e.g. 336 --- @param #number UnitID The DCS UNIT ID of the unit the LINK4 system is attached to. Useful if more units are in one group. --- @param #string Callsign Morse code identification callsign. --- @param #number Delay (Optional) Delay in seconds before the LINK4 is deactivated. --- @return #CONTROLLABLE self -function CONTROLLABLE:CommandActivateLink4(Frequency, UnitID, Callsign, Delay) - - -- Command to activate Link4 system. - local CommandActivateLink4= { - id = "ActivateLink4", - params= { - ["frequency "] = Frequency*1000, - ["unitId"] = UnitID, - ["name"] = Callsign, - } - } - - if Delay and Delay>0 then - SCHEDULER:New(nil, self.CommandActivateLink4, {self}, Delay) + SCHEDULER:New(nil, self.CommandActivateLink4, {self, Frequency, UnitID, Callsign}, Delay) else self:SetCommand(CommandActivateLink4) end @@ -809,24 +828,6 @@ function CONTROLLABLE:CommandDeactivateICLS( Delay ) return self end ---- Deactivate the active Link4 of the CONTROLLABLE. --- @param #CONTROLLABLE self --- @param #number Delay (Optional) Delay in seconds before the Link4 is deactivated. --- @return #CONTROLLABLE self -function CONTROLLABLE:CommandDeactivateLink4(Delay) - - -- Command to deactivate - local CommandDeactivateLink4={id='DeactivateLink4', params={}} - - if Delay and Delay>0 then - SCHEDULER:New(nil, self.CommandDeactivateLink4, {self}, Delay) - else - self:SetCommand(CommandDeactivateLink4) - end - - return self -end - --- Set callsign of the CONTROLLABLE. See [DCS command setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign) -- @param #CONTROLLABLE self -- @param DCS#CALLSIGN CallName Number corresponding the the callsign identifier you wish this group to be called. From 67cad1963f3d1c5d8ac9d52560b29dbb73c7a574 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 23 Jan 2023 17:10:52 +0100 Subject: [PATCH 194/603] #CONTROLLABLE * Added CommandActivateACLS() * Added CommandDeactivateACLS() --- .../Moose/Wrapper/Controllable.lua | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index e2d2d0bdd..920c8b2b7 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -670,9 +670,9 @@ end --- Activate ACLS system of the CONTROLLABLE. The controllable should be an aircraft carrier! Also needs Link4 to work. -- @param #CONTROLLABLE self --- @param #number UnitID The DCS UNIT ID of the unit the ACLS system is attached to. Useful if more units are in one group. +-- @param #number UnitID (Opional) The DCS UNIT ID of the unit the ACLS system is attached to. Defaults to the UNIT itself. -- @param #string Name (Optional) Name of the ACLS Beacon --- @param #number Delay (Optional) Delay in seconds before the ICLS is deactivated. +-- @param #number Delay (Optional) Delay in seconds before the ICLS is activated. -- @return #CONTROLLABLE self function CONTROLLABLE:CommandActivateACLS( UnitID, Name, Delay ) @@ -681,10 +681,12 @@ function CONTROLLABLE:CommandActivateACLS( UnitID, Name, Delay ) id = 'ActivateACLS', params = { unitId = UnitID or self:GetID(), - name = Name, + name = Name or "ACL", } } + self:T({CommandActivateACLS}) + if Delay and Delay > 0 then SCHEDULER:New( nil, self.CommandActivateACLS, { self, UnitID, Name }, Delay ) else @@ -720,7 +722,7 @@ end -- @param #number Channel ICLS channel. -- @param #number UnitID The DCS UNIT ID of the unit the ICLS system is attached to. Useful if more units are in one group. -- @param #string Callsign Morse code identification callsign. --- @param #number Delay (Optional) Delay in seconds before the ICLS is deactivated. +-- @param #number Delay (Optional) Delay in seconds before the ICLS is activated. -- @return #CONTROLLABLE self function CONTROLLABLE:CommandActivateICLS( Channel, UnitID, Callsign, Delay ) @@ -746,23 +748,27 @@ end --- Activate LINK4 system of the CONTROLLABLE. The controllable should be an aircraft carrier! -- @param #CONTROLLABLE self --- @param #number Frequency Link4 Frequency in MHz, e.g. 336 --- @param #number UnitID The DCS UNIT ID of the unit the LINK4 system is attached to. Useful if more units are in one group. --- @param #string Callsign Morse code identification callsign. --- @param #number Delay (Optional) Delay in seconds before the LINK4 is deactivated. +-- @param #number Frequency Link4 Frequency in MHz, e.g. 336 (defaults to 336 MHz) +-- @param #number UnitID (Optional) The DCS UNIT ID of the unit the LINK4 system is attached to. Defaults to the UNIT itself. +-- @param #string Callsign (Optional) Morse code identification callsign. +-- @param #number Delay (Optional) Delay in seconds before the LINK4 is activated. -- @return #CONTROLLABLE self function CONTROLLABLE:CommandActivateLink4(Frequency, UnitID, Callsign, Delay) - + + local freq = Frequency or 336 + -- Command to activate Link4 system. local CommandActivateLink4= { id = "ActivateLink4", params= { - ["frequency "] = Frequency*1000, + ["frequency "] = freq*1000000, ["unitId"] = UnitID or self:GetID(), - ["name"] = Callsign, + ["name"] = Callsign or "LNK", } } - + + self:T({CommandActivateLink4}) + if Delay and Delay>0 then SCHEDULER:New(nil, self.CommandActivateLink4, {self, Frequency, UnitID, Callsign}, Delay) else From 95d149274f91616c0fe9c9731e5a4fa485551e8d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 24 Jan 2023 10:05:39 +0100 Subject: [PATCH 195/603] #1885 #PSEUDOATC - Menu shows Waypoint name if it has been set --- Moose Development/Moose/Functional/PseudoATC.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 1930394b6..5ee714705 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -100,7 +100,7 @@ PSEUDOATC.id="PseudoATC | " --- PSEUDOATC version. -- @field #number version -PSEUDOATC.version="0.9.4" +PSEUDOATC.version="0.9.5" ----------------------------------------------------------------------------------------------------------------------------------------- @@ -521,7 +521,7 @@ function PSEUDOATC:PlayerLeft(unit) local GID=group:GetID() local UID=unit:GetDCSObject():getID() - if self.group[GID].player[UID] then + if self.group[GID] and self.group[GID].player and self.group[GID].player[UID] then local PlayerName=self.group[GID].player[UID].playername local CallSign=self.group[GID].player[UID].callsign local UnitName=self.group[GID].player[UID].unitname @@ -707,7 +707,9 @@ function PSEUDOATC:MenuWaypoints(GID, UID) -- Position of Waypoint local pos=COORDINATE:New(wp.x, wp.alt, wp.y) local name=string.format("Waypoint %d", i-1) - + if wp.name and wp.name ~= "" then + name = string.format("Waypoint %s",wp.name) + end -- "F10/PseudoATC/Waypoints/Waypoint X" local submenu=missionCommands.addSubMenuForGroup(GID, name, self.group[GID].player[UID].menu_waypoints) From 58032b007510eb79d570eb173e04c0304fc464bb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 25 Jan 2023 17:56:03 +0100 Subject: [PATCH 196/603] * AIRBASE.SouthAtlantic.Rio_Turbio --- Moose Development/Moose/Functional/Range.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 32052f218..c3425f26b 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -578,7 +578,7 @@ RANGE.MenuF10Root = nil --- Range script version. -- @field #string version -RANGE.version = "2.5.0" +RANGE.version = "2.5.1" -- TODO list: -- TODO: Verbosity level for messages. @@ -1207,13 +1207,18 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE) self.controlmsrs:SetLabel("RANGEC") self.controlsrsQ = MSRSQUEUE:New("CONTROL") - + self.instructmsrs=MSRS:New(PathToSRS, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0) self.instructmsrs:SetPort(Port) self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE) self.instructmsrs:SetLabel("RANGEI") self.instructsrsQ = MSRSQUEUE:New("INSTRUCT") + if PathToGoogleKey then + self.instructmsrs:SetGoogle(PathToGoogleKey) + self.instructmsrs:SetGoogle(PathToGoogleKey) + end + else self:E(self.lid..string.format("ERROR: No SRS path specified!")) end From 375e4231aa9ea68be36a41e59daa7b1ce682bf7e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 30 Jan 2023 18:00:29 +0100 Subject: [PATCH 197/603] #POINT * Added COORDINATE:SetAtLandheight() --- Moose Development/Moose/Core/Point.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 9f3e6badd..0a9d28e25 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1321,7 +1321,15 @@ do -- COORDINATE self.y=alt return self end - + + --- Set altitude to be at land height (i.e. on the ground!) + -- @param #COORDINATE self + function COORDINATE:SetAtLandheight() + local alt=self:GetLandHeight() + self.y=alt + return self + end + --- Build an air type route point. -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. From 9dac61cbb62cb56dfe0326e57a15fa4cd5bf7f14 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 31 Jan 2023 11:28:14 +0100 Subject: [PATCH 198/603] #CTLD * Added Subcategories for static cargo objects --- Moose Development/Moose/Ops/CTLD.lua | 33 ++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index f05e95038..17b9f16bd 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -733,6 +733,7 @@ do -- ["SA342Minigun"] = {type="SA342Minigun", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12, cargoweightlimit = 400}, -- ["UH-1H"] = {type="UH-1H", crates=true, troops=true, cratelimit = 1, trooplimit = 8, length = 15, cargoweightlimit = 700}, -- ["Mi-8MT"] = {type="Mi-8MT", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15, cargoweightlimit = 3000}, +-- ["Mi-8MTV2"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15, cargoweightlimit = 3000}, -- ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 15, cargoweightlimit = 0}, -- ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700}, -- ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700}, @@ -1195,7 +1196,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.28" +CTLD.version="1.0.29" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3325,6 +3326,12 @@ function CTLD:_RefreshF10Menus() self.subcats[entry.Subcategory] = entry.Subcategory end end + for _id,_cargo in pairs(self.Cargo_Statics) do + local entry = _cargo -- #CTLD_CARGO + if not self.subcats[entry.Subcategory] then + self.subcats[entry.Subcategory] = entry.Subcategory + end + end end -- build unit menus @@ -3388,6 +3395,13 @@ function CTLD:_RefreshF10Menus() local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry) end + for _,_entry in pairs(self.Cargo_Statics) do + local entry = _entry -- #CTLD_CARGO + local subcat = entry.Subcategory + menucount = menucount + 1 + local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry) + end else for _,_entry in pairs(self.Cargo_Crates) do local entry = _entry -- #CTLD_CARGO @@ -3395,12 +3409,12 @@ function CTLD:_RefreshF10Menus() local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) end - end - for _,_entry in pairs(self.Cargo_Statics) do - local entry = _entry -- #CTLD_CARGO - menucount = menucount + 1 - local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) - menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) + for _,_entry in pairs(self.Cargo_Statics) do + local entry = _entry -- #CTLD_CARGO + menucount = menucount + 1 + local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) + end end listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit) local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit) @@ -3494,13 +3508,14 @@ end -- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1". -- @param #number Mass Mass in kg of each static in kg, e.g. 100. -- @param #number Stock Number of groups in stock. Nil for unlimited. -function CTLD:AddStaticsCargo(Name,Mass,Stock) +-- @param #string SubCategory Name of sub-category (optional). +function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory) self:T(self.lid .. " AddStaticsCargo") self.CargoCounter = self.CargoCounter + 1 local type = CTLD_CARGO.Enum.STATIC local template = STATIC:FindByName(Name,true):GetTypeName() -- Crates are not directly loadable - local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock) + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock,SubCategory) table.insert(self.Cargo_Statics,cargo) return self end From b81bf447a992ce25c13f67648302c1e148e77359 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 2 Feb 2023 09:47:05 +0100 Subject: [PATCH 199/603] fix --- Moose Development/Moose/Ops/PlayerRecce.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index a4a30d6f8..df55a88d4 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -411,7 +411,7 @@ function PLAYERRECCE:_CameraOn(client,playername) if vivihorizontal < -0.7 or vivihorizontal > 0.7 then camera = false end - elseif typename == "Ka-50" or typename = "Ka-50_3" then + elseif string.find(typename,"Ka-50") then camera = true end end From 2c411736e55d705d6ae79201fc42cf4aa2fbae57 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 3 Feb 2023 16:41:01 +0100 Subject: [PATCH 200/603] #NET - initial version --- Moose Development/Moose/Wrapper/Net.lua | 265 ++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 Moose Development/Moose/Wrapper/Net.lua diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua new file mode 100644 index 000000000..742e1e624 --- /dev/null +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -0,0 +1,265 @@ +--- **Wrapper** - DCS net functions. +-- +-- Encapsules **multiplayer** environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) +-- +-- === +-- +-- ### Author: **applevangelist** +-- +-- === +-- +-- @module Wrapper.Net +-- @image Utils_Profiler.jpg + +do +--- The NET class +-- @type NET +-- @field #string ClassName +-- @field #string Version +-- @extends Core.Base#BASE + +--- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) +-- +-- @field #NET +NET = { + ClassName = "NET", + Version = "0.0.1" +} + +--- Instantiate a new NET object. +-- @param #NET self +-- @return #NET self +function NET:New() + -- Inherit base. + local self = BASE:Inherit(self, BASE:New()) -- #NET + return self +end + +--- Send chat message. +-- @param #NET self +-- @param #string Message Message to send +-- @param #boolean ToAll (Optional) +-- @return #NET self +function NET:SendChat(Message,ToAll) + if Message then + net.send_chat(Message, ToAll) + end + return self +end + +--- Send chat message to a specific player. +-- @param #NET self +-- @param #string Message The text message +-- @param Wrapper.Client#CLIENT ToClient Client receiving the message +-- @param Wrapper.Client#CLIENT FromClient(Optional) Client sending the message +-- @return #NET self +function NET:SendChatToPlayer(Message, ToClient, FromClient) + local PlayerId = ToClient:GetID() + local FromId = FromClient:GetID() + if Message and PlayerId and FromId then + net.send_chat_to(Message, tonumber(PlayerId) , tonumber(FromId)) + elseif Message and PlayerId then + net.send_chat_to(Message, tonumber(PlayerId)) + end + return self +end + +--- Load a specific mission. +-- @param #NET self +-- @param #string Path and Mission +-- @return #boolean success +-- @usage +-- mynet:LoadMission(lfs.writeDir() .. 'Missions\\' .. 'MyTotallyAwesomeMission.miz') +function NET:LoadMission(Path) + local outcome = false + if Path then + outcome = net.load_mission(Path) + end + return outcome +end + +--- Load next mission. Returns false if at the end of list. +-- @param #NET self +-- @return #boolean success +function NET:LoadNextMission() + local outcome = false + outcome = net.load_next_mission() + return outcome +end + +--- Return a table of players currently connected to the server. +-- @param #NET self +-- @return #table PlayerList +function NET:GetPlayerList() + local plist = nil + plist = net.get_player_list() + return plist +end + +--- Returns the playerID of the local player. Always returns 1 for server. +-- @param #NET self +-- @return #number ID +function NET:GetMyPlayerID() + return net.get_my_player_id() +end + +--- Returns the playerID of the server. Currently always 1. +-- @param #NET self +-- @return #number ID +function NET:GetServerID() + return net.get_server_id() +end + +--- Return a table of attributes for a given client. If optional attribute is present, only that value is returned. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client. +-- @param #string Attribute (Optional) The attribute to obtain. List see below. +-- @return #table PlayerInfo or nil if it cannot be found +-- @usage +-- Table holds these attributes: +-- +-- 'id' : playerID +-- 'name' : player name +-- 'side' : 0 - spectators, 1 - red, 2 - blue +-- 'slot' : slotID of the player or +-- 'ping' : ping of the player in ms +-- 'ipaddr': IP address of the player, SERVER ONLY +-- 'ucid' : Unique Client Identifier, SERVER ONLY +-- +function NET:GetPlayerInfo(Client,Attribute) + local PlayerID = Client:GetID() + if PlayerID then + return net.get_player_info(tonumber(PlayerID), Attribute) + else + return nil + end +end + +--- Kicks a player from the server. Can display a message to the user. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @param #string Message (Optional) The message to send. +-- @return #boolean success +function NET:Kick(Client,Message) + local PlayerID = Client:GetID() + if PlayerID and tonumber(PlayerID) ~= 1 then + return net.kick(tonumber(PlayerID), Message) + else + return false + end +end + +--- Return a statistic for a given client. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @param #number StatisticID The statistic to obtain +-- @return #number Statistic or nil +-- @usage +-- StatisticIDs are: +-- +-- net.PS_PING (0) - ping (in ms) +-- net.PS_CRASH (1) - number of crashes +-- net.PS_CAR (2) - number of destroyed vehicles +-- net.PS_PLANE (3) - ... planes/helicopters +-- net.PS_SHIP (4) - ... ships +-- net.PS_SCORE (5) - total score +-- net.PS_LAND (6) - number of landings +-- net.PS_EJECT (7) - of ejects +-- +-- mynet:GetPlayerStatistic(Client,7) -- return number of ejects +function NET:GetPlayerStatistic(Client,StatisticID) + local PlayerID = Client:GetID() + local stats = StatisticID or 0 + if stats > 7 or stats < 0 then stats = 0 end + if PlayerID then + return net.get_stat(tonumber(PlayerID),stats) + else + return nil + end +end + + +--- Return the name of a given client. Same a CLIENT:GetPlayerName(). +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @return #string Name +function NET:GetName(Client) + local PlayerID = Client:GetID() + if PlayerID then + return net.get_name(tonumber(PlayerID)) + else + return nil + end +end + +--- Returns the SideId and SlotId of a given client. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @return #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue +-- @return #number SlotID +function NET:GetSlot(Client) + local PlayerID = Client:GetID() + if PlayerID then + local side,slot = net.get_slot(tonumber(PlayerID)) + return side,slot + else + return nil,nil + end +end + +--- Force the slot for a specific client. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @param #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue +-- @param #number SlotID Slot number +-- @return #boolean Success +function NET:ForceSlot(Client,SideID,SlotID) + local PlayerID = Client:GetID() + if PlayerID then + return net.force_player_slot(tonumber(PlayerID), SideID, SlotID ) + else + return false + end +end + +--- Converts a lua value to a JSON string. +-- @param #string Lua Anything lua +-- @return #table Json +function NET.Lua2Json(Lua) + return net.lua2json(Lua) +end + +--- Converts a JSON string to a lua value. +-- @param #string Json Anything JSON +-- @return #table Lua +function NET.Lua2Json(Json) + return net.json2lua(Json) +end + +--- Executes a lua string in a given lua environment in the game. +-- @param #NET self +-- @param #string State The state in which to execute - see below. +-- @param #string DoString The lua string to be executed. +-- @return #string Output +-- @usage +-- States are: +-- 'config': the state in which $INSTALL_DIR/Config/main.cfg is executed, as well as $WRITE_DIR/Config/autoexec.cfg - used for configuration settings +-- 'mission': holds current mission +-- 'export': runs $WRITE_DIR/Scripts/Export.lua and the relevant export API +function NET:DoStringIn(State,DoString) + return net.dostring_in(State,DoString) +end + +--- Write an "INFO" entry to the DCS log file, with the message Message. +-- @param #NET self +-- @param #string Message The message to be logged. +-- @return #NET self +function NET:Log(Message) + net.log(Message) + return self +end + +------------------------------------------------------------------------------- +-- End of NET +------------------------------------------------------------------------------- +end From a4dd15013db302ec7ed35bd88d684807b3a5897c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 3 Feb 2023 16:41:11 +0100 Subject: [PATCH 201/603] #changes --- Moose Development/Moose/Modules_local.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 848ba726f..64ec2effa 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -45,6 +45,7 @@ __Moose.Include( 'Wrapper\\Static.lua' ) __Moose.Include( 'Wrapper\\Airbase.lua' ) __Moose.Include( 'Wrapper\\Scenery.lua' ) __Moose.Include( 'Wrapper\\Marker.lua' ) +__Moose.Include( 'Wrapper\\Net.lua' ) __Moose.Include( 'Cargo\\Cargo.lua' ) __Moose.Include( 'Cargo\\CargoUnit.lua' ) From 036b6b77b1d56ffd561cbf54c205422352452a97 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 4 Feb 2023 17:14:52 +0100 Subject: [PATCH 202/603] #CTLD * Added preload crates --- Moose Development/Moose/Ops/CTLD.lua | 94 ++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 9523395bf..c92235e24 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1776,6 +1776,22 @@ function CTLD:_FindTroopsCargoObject(Name) return nil end +--- (Internal) Find a crates CTLD_CARGO object in stock +-- @param #CTLD self +-- @param #string Name of the object +-- @return #CTLD_CARGO Cargo object, nil if it cannot be found +function CTLD:_FindCratesCargoObject(Name) + self:T(self.lid .. " _FindCratesCargoObject") + local cargo = nil + for _,_cargo in pairs(self.Cargo_Crates)do + local cargo = _cargo -- #CTLD_CARGO + if cargo.Name == Name then + return cargo + end + end + return nil +end + --- (User) Pre-load troops into a helo, e.g. for airstart. Unit **must** be alive in-game, i.e. player has taken the slot! -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object @@ -1801,6 +1817,84 @@ function CTLD:PreloadTroops(Unit,Troopname) return self end +--- (User) Pre-load crates into a helo. Do not use standalone! +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group The group to load into, can be handed as Wrapper.Client#CLIENT object +-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object +-- @param #CTLD_CARGO Cargo The Cargo crate object to load +-- @param #number NumberOfCrates (Optional) Number of crates to be loaded. Default - all necessary to build this object. Might overload the helo! +-- @return #CTLD self +function CTLD:_PreloadCrates(Group, Unit, Cargo, NumberOfCrates) + -- load crate into heli + local group = Group -- Wrapper.Group#GROUP + local unit = Unit -- Wrapper.Unit#UNIT + local unitname = unit:GetName() + -- see if this heli can load crates + local unittype = unit:GetTypeName() + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + local cancrates = capabilities.crates -- #boolean + local cratelimit = capabilities.cratelimit -- #number + if not cancrates then + self:_SendMessage("Sorry this chopper cannot carry crates!", 10, false, Group) + return self + else + -- have we loaded stuff already? + local numberonboard = 0 + local massonboard = 0 + local loaded = {} + if self.Loaded_Cargo[unitname] then + loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo + numberonboard = loaded.Cratesloaded or 0 + massonboard = self:_GetUnitCargoMass(Unit) + else + loaded = {} -- #CTLD.LoadedCargo + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} + end + local crate = Cargo -- #CTLD_CARGO + local numbercrates = NumberOfCrates or crate:GetCratesNeeded() + for i=1,numbercrates do + loaded.Cratesloaded = loaded.Cratesloaded + 1 + crate:SetHasMoved(true) + crate:SetWasDropped(false) + table.insert(loaded.Cargo, crate) + crate.Positionable = nil + self:_SendMessage(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()), 10, false, Group) + --self:__CratesPickedUp(1, Group, Unit, crate) + self.Loaded_Cargo[unitname] = loaded + self:_UpdateUnitCargoMass(Unit) + end + end + return self +end + +--- (User) Pre-load crates into a helo, e.g. for airstart. Unit **must** be alive in-game, i.e. player has taken the slot! +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object +-- @param #string Cratesname The name of the cargo to be loaded. Must be created prior in the CTLD setup! +-- @param #number NumberOfCrates (Optional) Number of crates to be loaded. Default - all necessary to build this object. Might overload the helo! +-- @return #CTLD self +-- @usage +-- local client = UNIT:FindByName("Helo-1-1") +-- if client and client:IsAlive() then +-- myctld:PreloadCrates(client,"Humvee") +-- end +function CTLD:PreloadCrates(Unit,Cratesname,NumberOfCrates) + self:T(self.lid .. " PreloadCrates") + local name = Cratesname or "Unknown" + if Unit and Unit:IsAlive() then + local cargo = self:_FindCratesCargoObject(name) + local group = Unit:GetGroup() + if cargo then + self:_PreloadCrates(group,Unit,cargo,NumberOfCrates) + else + self:E(self.lid.." Crates preload - Cargo Object "..name.." not found!") + end + end + return self +end + --- (Internal) Function to load troops into a heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group From c08b39f1480ccf33a48ac8426c244265727eeb77 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 4 Feb 2023 17:15:19 +0100 Subject: [PATCH 203/603] #NET * Added logic to get player ID from client object --- Moose Development/Moose/Wrapper/Net.lua | 50 ++++++++++++++++++------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index 742e1e624..e5f3b068e 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -23,7 +23,7 @@ do -- @field #NET NET = { ClassName = "NET", - Version = "0.0.1" + Version = "0.0.2" } --- Instantiate a new NET object. @@ -47,15 +47,40 @@ function NET:SendChat(Message,ToAll) return self end +--- Find the PlayerID by name +-- @param #NET self +-- @param #string Name The player name whose ID to find +-- @return #number PlayerID or nil +function NET:GetPlayerIdByName(Name) + local playerList = self:GetPlayerList() + for i=1,#playerList do + local playerName = net.get_name(i) + if playerName == Name then + return playerList[i] + end + end + return nil +end + +--- Find the PlayerID from a CLIENT object. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @return #number PlayerID or nil +function NET:GetPlayerIDFromClient(Client) + local name = Client:GetPlayerName() + local id = self:GetPlayerIdByName(name) + return id +end + --- Send chat message to a specific player. -- @param #NET self -- @param #string Message The text message -- @param Wrapper.Client#CLIENT ToClient Client receiving the message --- @param Wrapper.Client#CLIENT FromClient(Optional) Client sending the message +-- @param Wrapper.Client#CLIENT FromClient (Optional) Client sending the message -- @return #NET self function NET:SendChatToPlayer(Message, ToClient, FromClient) - local PlayerId = ToClient:GetID() - local FromId = FromClient:GetID() + local PlayerId = self:GetPlayerIDFromClient(ToClient) + local FromId = self:GetPlayerIDFromClient(FromClient) if Message and PlayerId and FromId then net.send_chat_to(Message, tonumber(PlayerId) , tonumber(FromId)) elseif Message and PlayerId then @@ -103,7 +128,7 @@ function NET:GetMyPlayerID() return net.get_my_player_id() end ---- Returns the playerID of the server. Currently always 1. +--- Returns the playerID of the server. Currently always returns 1. -- @param #NET self -- @return #number ID function NET:GetServerID() @@ -127,7 +152,7 @@ end -- 'ucid' : Unique Client Identifier, SERVER ONLY -- function NET:GetPlayerInfo(Client,Attribute) - local PlayerID = Client:GetID() + local PlayerID = self:GetPlayerIDFromClient(Client) if PlayerID then return net.get_player_info(tonumber(PlayerID), Attribute) else @@ -141,7 +166,7 @@ end -- @param #string Message (Optional) The message to send. -- @return #boolean success function NET:Kick(Client,Message) - local PlayerID = Client:GetID() + local PlayerID = self:GetPlayerIDFromClient(Client) if PlayerID and tonumber(PlayerID) ~= 1 then return net.kick(tonumber(PlayerID), Message) else @@ -168,7 +193,7 @@ end -- -- mynet:GetPlayerStatistic(Client,7) -- return number of ejects function NET:GetPlayerStatistic(Client,StatisticID) - local PlayerID = Client:GetID() + local PlayerID = self:GetPlayerIDFromClient(Client) local stats = StatisticID or 0 if stats > 7 or stats < 0 then stats = 0 end if PlayerID then @@ -178,13 +203,12 @@ function NET:GetPlayerStatistic(Client,StatisticID) end end - --- Return the name of a given client. Same a CLIENT:GetPlayerName(). -- @param #NET self -- @param Wrapper.Client#CLIENT Client The client --- @return #string Name +-- @return #string Name or nil if not obtainable function NET:GetName(Client) - local PlayerID = Client:GetID() + local PlayerID = self:GetPlayerIDFromClient(Client) if PlayerID then return net.get_name(tonumber(PlayerID)) else @@ -198,7 +222,7 @@ end -- @return #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue -- @return #number SlotID function NET:GetSlot(Client) - local PlayerID = Client:GetID() + local PlayerID = self:GetPlayerIDFromClient(Client) if PlayerID then local side,slot = net.get_slot(tonumber(PlayerID)) return side,slot @@ -214,7 +238,7 @@ end -- @param #number SlotID Slot number -- @return #boolean Success function NET:ForceSlot(Client,SideID,SlotID) - local PlayerID = Client:GetID() + local PlayerID = self:GetPlayerIDFromClient(Client) if PlayerID then return net.force_player_slot(tonumber(PlayerID), SideID, SlotID ) else From 8a45f782519bc3c311eb537d5914157dfbf3ef19 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 4 Feb 2023 17:20:05 +0100 Subject: [PATCH 204/603] fix --- Moose Development/Moose/Ops/CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index c92235e24..db2afa9c0 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1196,7 +1196,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.29" +CTLD.version="1.0.30" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1817,7 +1817,7 @@ function CTLD:PreloadTroops(Unit,Troopname) return self end ---- (User) Pre-load crates into a helo. Do not use standalone! +--- (Internal) Pre-load crates into a helo. Do not use standalone! -- @param #CTLD self -- @param Wrapper.Group#GROUP Group The group to load into, can be handed as Wrapper.Client#CLIENT object -- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object From b250111d579a5bb37db2268ef644cbfa965d69c1 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Sat, 4 Feb 2023 21:48:23 +0100 Subject: [PATCH 205/603] Scoring credits a kill to player who hit the same unit before it spawned (#1905) (#1906) Co-authored-by: Mr.Alien <124381209+MrAlien753@users.noreply.github.com> --- Moose Development/Moose/Functional/Scoring.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 24085edf3..ea6933c61 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -875,6 +875,7 @@ function SCORING:OnEventBirth( Event ) if Event.IniUnit then if Event.IniObjectCategory == 1 then local PlayerName = Event.IniUnit:GetPlayerName() + Event.IniUnit.BirthTime = timer.getTime() if PlayerName then self:_AddPlayerFromUnit( Event.IniUnit ) self:SetScoringMenu( Event.IniGroup ) @@ -1211,7 +1212,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) local Destroyed = false -- What is the player destroying? - if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered??? + if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered??? local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType From 9ea831a0f23ca07a7ba75a584e0feeb07643d278 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 6 Feb 2023 08:17:43 +0100 Subject: [PATCH 206/603] added weapon --- Moose Development/Moose/Modules_local.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 64ec2effa..c638e81db 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -46,6 +46,7 @@ __Moose.Include( 'Wrapper\\Airbase.lua' ) __Moose.Include( 'Wrapper\\Scenery.lua' ) __Moose.Include( 'Wrapper\\Marker.lua' ) __Moose.Include( 'Wrapper\\Net.lua' ) +__Moose.Include( 'Wrapper\\Weapon.lua' ) __Moose.Include( 'Cargo\\Cargo.lua' ) __Moose.Include( 'Cargo\\CargoUnit.lua' ) From 49c45f97fc987c7def8ab210c548f65a4c97389e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 7 Feb 2023 10:51:14 +0100 Subject: [PATCH 207/603] Test smaller dev moose --- Moose Development/Moose/Modules.lua | 127 ++++++++++++++-------------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index aea3a4ec5..ad37bf77e 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -4,7 +4,7 @@ __Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Socket.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' ) -__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' ) +--__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' ) __Moose.Include( 'Scripts/Moose/Core/Base.lua' ) @@ -50,12 +50,13 @@ __Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Weapon.lua' ) +__Moose.Include( 'Scripts/Moose/Wrapper/Net.lua' ) -__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' ) -__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' ) -__Moose.Include( 'Scripts/Moose/Cargo/CargoSlingload.lua' ) -__Moose.Include( 'Scripts/Moose/Cargo/CargoCrate.lua' ) -__Moose.Include( 'Scripts/Moose/Cargo/CargoGroup.lua' ) +--__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' ) +--__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' ) +--__Moose.Include( 'Scripts/Moose/Cargo/CargoSlingload.lua' ) +--__Moose.Include( 'Scripts/Moose/Cargo/CargoCrate.lua' ) +--__Moose.Include( 'Scripts/Moose/Cargo/CargoGroup.lua' ) __Moose.Include( 'Scripts/Moose/Functional/AICSAR.lua' ) __Moose.Include( 'Scripts/Moose/Functional/AmmoTruck.lua' ) @@ -69,8 +70,8 @@ __Moose.Include( 'Scripts/Moose/Functional/DetectionZones.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Escort.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Fox.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Mantis.lua' ) -__Moose.Include( 'Scripts/Moose/Functional/MissileTrainer.lua' ) -__Moose.Include( 'Scripts/Moose/Functional/Movement.lua' ) +--__Moose.Include( 'Scripts/Moose/Functional/MissileTrainer.lua' ) +--__Moose.Include( 'Scripts/Moose/Functional/Movement.lua' ) __Moose.Include( 'Scripts/Moose/Functional/PseudoATC.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Range.lua' ) __Moose.Include( 'Scripts/Moose/Functional/RAT.lua' ) @@ -79,10 +80,10 @@ __Moose.Include( 'Scripts/Moose/Functional/Sead.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Shorad.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Suppression.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Warehouse.lua' ) -__Moose.Include( 'Scripts/Moose/Functional/ZoneCaptureCoalition.lua' ) -__Moose.Include( 'Scripts/Moose/Functional/ZoneGoal.lua' ) -__Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCargo.lua' ) -__Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCoalition.lua' ) +--__Moose.Include( 'Scripts/Moose/Functional/ZoneCaptureCoalition.lua' ) +--__Moose.Include( 'Scripts/Moose/Functional/ZoneGoal.lua' ) +--__Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCargo.lua' ) +--__Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCoalition.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' ) __Moose.Include( 'Scripts/Moose/Ops/AirWing.lua' ) @@ -115,42 +116,42 @@ __Moose.Include( 'Scripts/Moose/Ops/RescueHelo.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Squadron.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Target.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Air_Patrol.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Air_Engage.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Patrol.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Cap.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Gci.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Dispatcher.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_A2G_BAI.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_A2G_CAS.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_A2G_SEAD.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_A2G_Dispatcher.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Patrol.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cap.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cas.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Bai.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Formation.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Escort.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Request.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher_Request.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Airplane.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Ship.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_APC.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua' ) -__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Ship.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Air_Patrol.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Air_Engage.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Patrol.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Cap.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Gci.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Dispatcher.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_A2G_BAI.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_A2G_CAS.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_A2G_SEAD.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_A2G_Dispatcher.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Patrol.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cap.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cas.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Bai.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Formation.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Escort.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Request.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher_Request.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Airplane.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Ship.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_APC.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua' ) +--__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Ship.lua' ) -__Moose.Include( 'Scripts/Moose/Actions/Act_Assign.lua' ) -__Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' ) -__Moose.Include( 'Scripts/Moose/Actions/Act_Account.lua' ) -__Moose.Include( 'Scripts/Moose/Actions/Act_Assist.lua' ) +--__Moose.Include( 'Scripts/Moose/Actions/Act_Assign.lua' ) +--__Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' ) +--__Moose.Include( 'Scripts/Moose/Actions/Act_Account.lua' ) +--__Moose.Include( 'Scripts/Moose/Actions/Act_Assist.lua' ) __Moose.Include( 'Scripts/Moose/Sound/Radio.lua' ) __Moose.Include( 'Scripts/Moose/Sound/RadioQueue.lua' ) @@ -159,21 +160,21 @@ __Moose.Include( 'Scripts/Moose/Sound/SoundOutput.lua' ) __Moose.Include( 'Scripts/Moose/Sound/SRS.lua' ) __Moose.Include( 'Scripts/Moose/Sound/UserSound.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/CommandCenter.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Mission.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/TaskInfo.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_Manager.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/DetectionManager.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_A2G_Dispatcher.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_A2G.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_A2A_Dispatcher.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_A2A.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_Transport.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_CSAR.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_Dispatcher.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_Capture_Zone.lua' ) -__Moose.Include( 'Scripts/Moose/Tasking/Task_Capture_Dispatcher.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/CommandCenter.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Mission.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/TaskInfo.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_Manager.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/DetectionManager.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_A2G_Dispatcher.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_A2G.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_A2A_Dispatcher.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_A2A.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_Transport.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_CSAR.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_Dispatcher.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_Capture_Zone.lua' ) +--__Moose.Include( 'Scripts/Moose/Tasking/Task_Capture_Dispatcher.lua' ) __Moose.Include( 'Scripts/Moose/Globals.lua' ) From da93126d58489be679cab59dad63b04c35dd1b16 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 7 Feb 2023 11:05:54 +0100 Subject: [PATCH 208/603] Update Moose.files --- Moose Setup/Moose.files | 66 +---------------------------------------- 1 file changed, 1 insertion(+), 65 deletions(-) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 3e64293d4..6caa7f179 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -3,7 +3,6 @@ Utilities/Routines.lua Utilities/Utils.lua Utilities/Enums.lua Utilities/Profiler.lua -Utilities/Templates.lua Utilities/STTS.lua Utilities/FiFo.lua Utilities/Socket.lua @@ -28,7 +27,6 @@ Core/Fsm.lua Core/Spawn.lua Core/SpawnStatic.lua Core/Timer.lua -Core/Goal.lua Core/Spot.lua Core/MarkerOps_Base.lua Core/Astar.lua @@ -46,27 +44,19 @@ Wrapper/Airbase.lua Wrapper/Scenery.lua Wrapper/Marker.lua Wrapper/Weapon.lua - -Cargo/Cargo.lua -Cargo/CargoUnit.lua -Cargo/CargoSlingload.lua -Cargo/CargoCrate.lua -Cargo/CargoGroup.lua +Wrapper/Net.lua Functional/Scoring.lua Functional/CleanUp.lua Functional/Movement.lua Functional/Sead.lua Functional/Escort.lua -Functional/MissileTrainer.lua Functional/ATC_Ground.lua Functional/Detection.lua Functional/DetectionZones.lua Functional/Designate.lua Functional/RAT.lua Functional/Range.lua -Functional/ZoneGoal.lua -Functional/ZoneGoalCoalition.lua Functional/ZoneCaptureCoalition.lua Functional/Artillery.lua Functional/Suppression.lua @@ -106,43 +96,6 @@ Ops/FlightControl.lua Ops/PlayerTask.lua Ops/PlayerRecce.lua -AI/AI_Balancer.lua -AI/AI_Air.lua -AI/AI_Air_Patrol.lua -AI/AI_Air_Engage.lua -AI/AI_A2A_Patrol.lua -AI/AI_A2A_Cap.lua -AI/AI_A2A_Gci.lua -AI/AI_A2A_Dispatcher.lua -AI/AI_A2G_BAI.lua -AI/AI_A2G_CAS.lua -AI/AI_A2G_SEAD.lua -AI/AI_A2G_Dispatcher.lua -AI/AI_Patrol.lua -AI/AI_CAP.lua -AI/AI_CAS.lua -AI/AI_BAI.lua -AI/AI_Formation.lua -AI/AI_Escort.lua -AI/AI_Escort_Request.lua -AI/AI_Escort_Dispatcher.lua -AI/AI_Escort_Dispatcher_Request.lua -AI/AI_Cargo.lua -AI/AI_Cargo_APC.lua -AI/AI_Cargo_Helicopter.lua -AI/AI_Cargo_Airplane.lua -AI/AI_Cargo_Ship.lua -AI/AI_Cargo_Dispatcher.lua -AI/AI_Cargo_Dispatcher_APC.lua -AI/AI_Cargo_Dispatcher_Helicopter.lua -AI/AI_Cargo_Dispatcher_Airplane.lua -AI/AI_Cargo_Dispatcher_Ship.lua - -Actions/Act_Assign.lua -Actions/Act_Route.lua -Actions/Act_Account.lua -Actions/Act_Assist.lua - Sound/UserSound.lua Sound/SoundOutput.lua Sound/Radio.lua @@ -150,21 +103,4 @@ Sound/RadioQueue.lua Sound/RadioSpeech.lua Sound/SRS.lua -Tasking/CommandCenter.lua -Tasking/Mission.lua -Tasking/Task.lua -Tasking/TaskInfo.lua -Tasking/Task_Manager.lua -Tasking/DetectionManager.lua -Tasking/Task_A2G_Dispatcher.lua -Tasking/Task_A2G.lua -Tasking/Task_A2A_Dispatcher.lua -Tasking/Task_A2A.lua -Tasking/Task_CARGO.lua -Tasking/Task_Cargo_Transport.lua -Tasking/Task_Cargo_CSAR.lua -Tasking/Task_Cargo_Dispatcher.lua -Tasking/Task_Capture_Zone.lua -Tasking/Task_Capture_Dispatcher.lua - Globals.lua From 387aef6626047fdd3977be5f76be5a73cecaa5e9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 9 Feb 2023 11:53:45 +0100 Subject: [PATCH 209/603] fxi --- Moose Setup/Moose_Create.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Setup/Moose_Create.lua b/Moose Setup/Moose_Create.lua index 4b3e635ec..3008117bb 100644 --- a/Moose Setup/Moose_Create.lua +++ b/Moose Setup/Moose_Create.lua @@ -1,4 +1,4 @@ --- This routine is called from the LDT environment to create the Moose.lua file stub for use in .miz files. +--- This routine is called from the LDT environment to create the Moose.lua file stub for use in .miz files. local MooseDynamicStatic = arg[1] local MooseCommitHash = arg[2] From e44f61181a552dd46485de30a9678c2465c2b198 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 9 Feb 2023 11:57:41 +0100 Subject: [PATCH 210/603] #AICSAR * Fixed issue with underlying OpsTransport * Added FSM Transition OnAfterPilotUnloaded() * Added options for SRS TTS output - no sound files * Added voice options for downed pilot and operator - these can have different voices --- Moose Development/Moose/Functional/AICSAR.lua | 206 ++++++++++++++++-- 1 file changed, 188 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index 5db982982..279cd2029 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -147,7 +147,7 @@ -- -- Switch on radio transmissions via **either** SRS **or** "normal" DCS radio e.g. like so: -- --- my_aicsar:SetSRSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",270,radio.modulation.AM,5002) +-- my_aicsar:SetSRSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",270,radio.modulation.AM,nil,5002) -- -- or -- @@ -161,7 +161,7 @@ -- @field #AICSAR AICSAR = { ClassName = "AICSAR", - version = "0.0.8", + version = "0.1.9", lid = "", coalition = coalition.side.BLUE, template = "", @@ -173,7 +173,7 @@ AICSAR = { pilotqueue = {}, pilotindex = 0, helos = {}, - verbose = true, + verbose = false, rescuezoneradius = 200, rescued = {}, autoonoff = true, @@ -194,6 +194,13 @@ AICSAR = { helonumber = 3, gettext = nil, locale ="en", -- default text language + SRSTTSRadio = false, + SRSGoogle = false, + SRSQ = nil, + SRSPilot = nil, + SRSPilotVoice = false, + SRSOperator = nil, + SRSOperatorVoice = false, } -- TODO Messages @@ -203,7 +210,7 @@ AICSAR.Messages = { EN = { INITIALOK = "Roger, Pilot, we hear you. Stay where you are, a helo is on the way!", INITIALNOTOK = "Sorry, Pilot. You're behind maximum operational distance! Good Luck!", - PILOTDOWN = "Pilot down at ", + PILOTDOWN = "Mayday, mayday, mayday! Pilot down at ", PILOTKIA = "Pilot KIA!", HELODOWN = "CSAR Helo Down!", PILOTRESCUED = "Pilot rescued!", @@ -212,7 +219,7 @@ AICSAR.Messages = { DE = { INITIALOK = "Copy, Pilot, wir hören Sie. Bleiben Sie, wo Sie sind!\nEin Hubschrauber sammelt Sie auf!", INITIALNOTOK = "Verstehe, Pilot. Sie sind zu weit weg von uns.\nViel Glück!", - PILOTDOWN = "Pilot abgestürzt: ", + PILOTDOWN = "Mayday, mayday, mayday! Pilot abgestürzt: ", PILOTKIA = "Pilot gefallen!", HELODOWN = "CSAR Hubschrauber verloren!", PILOTRESCUED = "Pilot gerettet!", @@ -309,6 +316,9 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) -- Radio self.SRS = nil self.SRSRadio = false + self.SRSTTSRadio = false + self.SRSGoogle = false + self.SRSQ = nil self.SRSFrequency = 243 self.SRSPath = "\\" self.SRSModulation = radio.modulation.AM @@ -343,6 +353,7 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) self:AddTransition("*", "Status", "*") -- CSAR status update. self:AddTransition("*", "PilotDown", "*") -- Pilot down self:AddTransition("*", "PilotPickedUp", "*") -- Pilot in helo + self:AddTransition("*", "PilotUnloaded", "*") -- Pilot Unloaded from helo self:AddTransition("*", "PilotRescued", "*") -- Pilot Rescued self:AddTransition("*", "PilotKIA", "*") -- Pilot dead self:AddTransition("*", "HeloDown", "*") -- Helo dead @@ -404,6 +415,15 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) -- @param #string Event Event. -- @param #string To To state. + --- On after "PilotUnloaded" event. + -- @function [parent=#AICSAR] OnAfterPilotUnloaded + -- @param #AICSAR self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.FlightGroup#FLIGHTGROUP Helo + -- @param Ops.OpsGroup#OPSGROUP OpsGroup + --- On after "PilotKIA" event. -- @function [parent=#AICSAR] OnAfterPilotKIA -- @param #AICSAR self @@ -453,7 +473,7 @@ function AICSAR:InitLocalization() return self end ---- [User] Switch sound output on and use SRS +--- [User] Switch sound output on and use SRS output for sound files. -- @param #AICSAR self -- @param #boolean OnOff Switch on (true) or off (false). -- @param #string Path Path to your SRS Server Component, e.g. "E:\\\\Program Files\\\\DCS-SimpleRadio-Standalone" @@ -464,10 +484,12 @@ end -- @return #AICSAR self function AICSAR:SetSRSRadio(OnOff,Path,Frequency,Modulation,SoundPath,Port) self:T(self.lid .. "SetSRSRadio") - self:T(self.lid .. "SetSRSRadio to "..tostring(OnOff)) self.SRSRadio = OnOff and true + self.SRSTTSRadio = false self.SRSFrequency = Frequency or 243 self.SRSPath = Path or "c:\\" + self.SRS:SetLabel("ACSR") + self.SRS:SetCoalition(self.coalition) self.SRSModulation = Modulation or radio.modulation.AM local soundpath = os.getenv('TMP') .. "\\DCS\\Mission\\l10n\\DEFAULT" -- defaults to "l10n/DEFAULT/", i.e. add messages by "Sound to..." in the ME self.SRSSoundPath = SoundPath or soundpath @@ -479,6 +501,88 @@ function AICSAR:SetSRSRadio(OnOff,Path,Frequency,Modulation,SoundPath,Port) return self end +--- [User] Switch sound output on and use SRS-TTS output. The voice will be used across all outputs, unless you define an extra voice for downed pilots and/or the operator. +-- See `AICSAR:SetPilotTTSVoice()` and `AICSAR:SetOperatorTTSVoice()` +-- @param #AICSAR self +-- @param #boolean OnOff Switch on (true) or off (false). +-- @param #string Path Path to your SRS Server Component, e.g. "E:\\\\Program Files\\\\DCS-SimpleRadio-Standalone" +-- @param #number Frequency (Optional) Defaults to 243 (guard) +-- @param #number Modulation (Optional) Radio modulation. Defaults to radio.modulation.AM +-- @param #number Port (Optional) Port of the SRS, defaults to 5002. +-- @param #string Voice (Optional) The voice to be used. +-- @param #string Culture (Optional) The culture to be used, defaults to "en-GB" +-- @param #string Gender (Optional) The gender to be used, defaults to "male" +-- @param #string GoogleCredentials (Optional) Path to google credentials +-- @return #AICSAR self +function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Culture,Gender,GoogleCredentials) + self:T(self.lid .. "SetSRSTTSRadio") + self.SRSTTSRadio = OnOff and true + self.SRSRadio = false + self.SRSFrequency = Frequency or 243 + self.SRSPath = Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + self.SRSModulation = Modulation or radio.modulation.AM + self.SRSPort = Port or 5002 + if OnOff then + self.SRS = MSRS:New(Path,Frequency,Modulation,1) + self.SRS:SetPort(self.SRSPort) + self.SRS:SetCoalition(self.coalition) + self.SRS:SetLabel("ACSR") + self.SRS:SetVoice(Voice) + self.SRS:SetCulture(Culture) + self.SRS:SetGender(Gender) + if GoogleCredentials then + self.SRS:SetGoogle(GoogleCredentials) + self.SRSGoogle = true + end + self.SRSQ = MSRSQUEUE:New(self.alias) + end + return self +end + +--- [User] Set SRS TTS Voice of downed pilot. `AICSAR:SetSRSTTSRadio()` needs to be set first! +-- @param #AICSAR self +-- @param #string Voice The voice to be used, e.g. `MSRS.Voices.Google.Standard.en_US_Standard_J` for Google or `MSRS.Voices.Microsoft.David` for Microsoft. +-- Specific voices override culture and gender! +-- @param #string Culture (Optional) The culture to be used, defaults to "en-US" +-- @param #string Gender (Optional) The gender to be used, defaults to "male" +-- @return #AICSAR self +function AICSAR:SetPilotTTSVoice(Voice,Culture,Gender) + self:T(self.lid .. "SetPilotTTSVoice") + self.SRSPilotVoice = true + self.SRSPilot = MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation,1) + self.SRSPilot:SetCoalition(self.coalition) + self.SRSPilot:SetVoice(Voice) + self.SRSPilot:SetCulture(Culture or "en-US") + self.SRSPilot:SetGender(Gender or "male") + self.SRSPilot:SetLabel("PILOT") + if self.SRS.google then + self.SRSPilot:SetGoogle(self.SRS.google) + end + return self +end + +--- [User] Set SRS TTS Voice of the rescue operator. `AICSAR:SetSRSTTSRadio()` needs to be set first! +-- @param #AICSAR self +-- @param #string Voice The voice to be used, e.g. `MSRS.Voices.Google.Standard.en_US_Standard_J` for Google or `MSRS.Voices.Microsoft.David` for Microsoft. +-- Specific voices override culture and gender! +-- @param #string Culture (Optional) The culture to be used, defaults to "en-GB" +-- @param #string Gender (Optional) The gender to be used, defaults to "female" +-- @return #AICSAR self +function AICSAR:SetOperatorTTSVoice(Voice,Culture,Gender) + self:T(self.lid .. "SetOperatorTTSVoice") + self.SRSOperatorVoice = true + self.SRSOperator = MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation,1) + self.SRSOperator:SetCoalition(self.coalition) + self.SRSOperator:SetVoice(Voice) + self.SRSOperator:SetCulture(Culture or "en-GB") + self.SRSOperator:SetGender(Gender or "female") + self.SRSPilot:SetLabel("RESCUE") + if self.SRS.google then + self.SRSOperator:SetGoogle(self.SRS.google) + end + return self +end + --- [User] Switch sound output on and use normale (DCS) radio -- @param #AICSAR self -- @param #boolean OnOff Switch on (true) or off (false). @@ -545,13 +649,25 @@ function AICSAR:OnEventLandingAfterEjection(EventData) -- Mayday Message local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale) local text = "" + local setting = {} + setting.MGRS_Accuracy = self.MGRS_Accuracy + local location = _LandingPos:ToStringMGRS(setting) + local msgtxt = Text..location.."!" + location = string.gsub(location,"MGRS ","") + location = string.gsub(location,"%s+","") + location = string.gsub(location,"([%a%d])","%1;") -- "0 5 1 " + location = string.gsub(location,"0","zero") + location = string.gsub(location,"9","niner") + location = "MGRS;"..location + if self.SRSGoogle then + location = string.format("%s",location) + end + text = Text .. location .. "!" + local ttstext = Text .. location .. "! Repeat! "..location if _coalition == self.coalition then if self.verbose then - local setting = {} - setting.MGRS_Accuracy = self.MGRS_Accuracy - local location = _LandingPos:ToStringMGRS(setting) - text = Text .. location .. "!" - MESSAGE:New(text,15,"AICSAR"):ToCoalition(self.coalition) + MESSAGE:New(msgtxt,15,"AICSAR"):ToCoalition(self.coalition) + -- MESSAGE:New(msgtxt,15,"AICSAR"):ToLog() end if self.SRSRadio then local sound = SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength) @@ -559,6 +675,12 @@ function AICSAR:OnEventLandingAfterEjection(EventData) self.SRS:PlaySoundFile(sound,2) elseif self.DCSRadio then self:DCSRadioBroadcast(Soundfile,Soundlength,text) + elseif self.SRSTTSRadio then + if self.SRSPilotVoice then + self.SRSQ:NewTransmission(ttstext,nil,self.SRSPilot,nil,1) + else + self.SRSQ:NewTransmission(ttstext,nil,self.SRS,nil,1) + end end end @@ -634,6 +756,10 @@ function AICSAR:_InitMission(Pilot,Index) self:__HeloDown(2,Helo,Index) end + local function AICHeloUnloaded(Helo,OpsGroup) + self:__PilotUnloaded(2,Helo,OpsGroup) + end + function helo:OnAfterLoadingDone(From,Event,To) AICPickedUp(helo,helo:GetCargoGroups(),Index) end @@ -642,6 +768,10 @@ function AICSAR:_InitMission(Pilot,Index) AICHeloDead(helo,Index) end + function helo:OnAfterUnloaded(From,Event,To,OpsGroupCargo) + AICHeloUnloaded(helo,OpsGroupCargo) + end + self.helos[Index] = helo return self @@ -652,7 +782,7 @@ end -- @param Wrapper.Group#GROUP Pilot The pilot to be rescued. -- @return #boolean outcome function AICSAR:_CheckInMashZone(Pilot) - self:T(self.lid .. "_CheckQueue") + self:T(self.lid .. "_CheckInMashZone") if Pilot:IsInZone(self.farpzone) then return true else @@ -696,8 +826,9 @@ end --- [Internal] Check pilot queue for next mission -- @param #AICSAR self +-- @param Ops.OpsGroup#OPSGROUP OpsGroup -- @return #AICSAR self -function AICSAR:_CheckQueue() +function AICSAR:_CheckQueue(OpsGroup) self:T(self.lid .. "_CheckQueue") for _index, _pilot in pairs(self.pilotqueue) do local classname = _pilot.ClassName and _pilot.ClassName or "NONE" @@ -709,8 +840,12 @@ function AICSAR:_CheckQueue() local flightgroup = self.helos[_index] -- Ops.FlightGroup#FLIGHTGROUP -- rescued? if self:_CheckInMashZone(_pilot) then - self:T("Pilot" .. _pilot.GroupName .. " rescued!") - _pilot:Destroy(false) + self:T("Pilot" .. _pilot.GroupName .. " rescued!") + if OpsGroup then + OpsGroup:Despawn(10) + else + _pilot:Destroy(true,10) + end self.pilotqueue[_index] = nil self.rescued[_index] = true self:__PilotRescued(2) @@ -767,7 +902,7 @@ end -- @return #AICSAR self function AICSAR:onafterStatus(From, Event, To) self:T({From, Event, To}) - self:_CheckQueue() + --self:_CheckQueue() self:_CheckHelos() self:__Status(30) return self @@ -814,6 +949,12 @@ function AICSAR:onafterPilotDown(From, Event, To, Coordinate, InReach) self.SRS:PlaySoundFile(sound,2) elseif self.DCSRadio then self:DCSRadioBroadcast(Soundfile,Soundlength,text) + elseif self.SRSTTSRadio then + if self.SRSOperatorVoice then + self.SRSQ:NewTransmission(text,nil,self.SRSOperator,nil,1) + else + self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1) + end end else local text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("INITIALNOTOK",self.locale) @@ -828,8 +969,15 @@ function AICSAR:onafterPilotDown(From, Event, To, Coordinate, InReach) self.SRS:PlaySoundFile(sound,2) elseif self.DCSRadio then self:DCSRadioBroadcast(Soundfile,Soundlength,text) + elseif self.SRSTTSRadio then + if self.SRSOperatorVoice then + self.SRSQ:NewTransmission(text,nil,self.SRSOperator,nil,1) + else + self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1) + end end end + self:_CheckQueue() return self end @@ -851,7 +999,9 @@ function AICSAR:onafterPilotKIA(From, Event, To) self.SRS:PlaySoundFile(sound,2) elseif self.DCSRadio then self:DCSRadioBroadcast(Soundfile,Soundlength,text) - end + elseif self.SRSTTSRadio then + self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1) + end return self end @@ -875,6 +1025,8 @@ function AICSAR:onafterHeloDown(From, Event, To, Helo, Index) self.SRS:PlaySoundFile(sound,2) elseif self.DCSRadio then self:DCSRadioBroadcast(Soundfile,Soundlength,text) + elseif self.SRSTTSRadio then + self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1) end local findex = 0 local fhname = Helo:GetName() @@ -927,10 +1079,26 @@ function AICSAR:onafterPilotRescued(From, Event, To) self.SRS:PlaySoundFile(sound,2) elseif self.DCSRadio then self:DCSRadioBroadcast(Soundfile,Soundlength,text) + elseif self.SRSTTSRadio then + self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1) end return self end +--- [Internal] onafterPilotUnloaded +-- @param #AICSAR self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Ops.FlightGroup#FLIGHTGROUP Helo +-- @param Ops.OpsGroup#OPSGROUP OpsGroup +-- @return #AICSAR self +function AICSAR:onafterPilotUnloaded(From, Event, To, Helo, OpsGroup) + self:T({From, Event, To}) + self:_CheckQueue(OpsGroup) + return self +end + --- [Internal] onafterPilotPickedUp -- @param #AICSAR self -- @param #string From @@ -952,6 +1120,8 @@ function AICSAR:onafterPilotPickedUp(From, Event, To, Helo, CargoTable, Index) self.SRS:PlaySoundFile(sound,2) elseif self.DCSRadio then self:DCSRadioBroadcast(Soundfile,Soundlength,text) + elseif self.SRSTTSRadio then + self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1) end local findex = 0 local fhname = Helo:GetName() From 292bc790eed92443d73a4afa700c6372590a4c83 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 9 Feb 2023 13:17:34 +0100 Subject: [PATCH 211/603] #AICSAR * Docu additions --- Moose Development/Moose/Functional/AICSAR.lua | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index 279cd2029..b1ee470ca 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -11,6 +11,7 @@ -- * Dedicated MASH zone -- * Some FSM functions to include in your mission scripts -- * Limit number of available helos +-- * SRS voice output via TTS or soundfiles -- -- === -- @@ -20,8 +21,8 @@ -- -- === -- --- ### Author: **applevangelist** --- Last Update April 2022 +-- ### Author: **Applevangelist** +-- Last Update February 2022 -- -- === -- @module Functional.AICSAR @@ -87,10 +88,11 @@ -- my_aicsar.rescuezoneradius -- landing zone around downed pilot. Defaults to 200m -- my_aicsar.autoonoff -- stop operations when human helicopter pilots are around. Defaults to true. -- my_aicsar.verbose -- text messages to own coalition about ongoing operations. Defaults to true. --- my_aicsarlimithelos -- limit available number of helos going on mission (defaults to true) +-- my_aicsar.limithelos -- limit available number of helos going on mission (defaults to true) -- my_aicsar.helonumber -- number of helos available (default: 3) +-- my_aicsar.verbose -- boolean, set to `true`for message output on-screen -- --- ## Radio options +-- ## Radio output options -- -- Radio messages, soundfile names and (for SRS) lengths are defined in three enumerators, so you can customize, localize messages and soundfiles to your liking: -- @@ -100,7 +102,7 @@ -- EN = { -- INITIALOK = "Roger, Pilot, we hear you. Stay where you are, a helo is on the way!", -- INITIALNOTOK = "Sorry, Pilot. You're behind maximum operational distance! Good Luck!", --- PILOTDOWN = "Pilot down at ", -- note that this will be appended with the position +-- PILOTDOWN = "Mayday, mayday, mayday! Pilot down at ", -- note that this will be appended with the position in MGRS -- PILOTKIA = "Pilot KIA!", -- HELODOWN = "CSAR Helo Down!", -- PILOTRESCUED = "Pilot rescued!", @@ -136,8 +138,31 @@ -- }, -- } -- +-- ## Radio output via SRS and Text-To-Speech (TTS) +-- +-- Radio output can be done via SRS and Text-To-Speech. No extra sound files required! +-- [Initially, Have a look at the guide on setting up SRS TTS for Moose](https://github.com/FlightControl-Master/MOOSE_GUIDES/blob/master/documents/Moose%20TTS%20Setup%20Guide.pdf). +-- The text from the `AICSAR.Messages` table above is converted on the fly to an .ogg-file, which is then played back via SRS on the selected frequency and mdulation. +-- Hint - the small black window popping up shortly is visible in Single-Player only. +-- +-- To set up AICSAR for SRS TTS output, add e.g. the following to your script: +-- +-- -- setup for google TTS, radio 243 AM, SRS server port 5002 with a google standard-quality voice (google cloud account required) +-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",243,radio.modulation.AM,5002,MSRS.Voices.Google.Standard.en_US_Standard_D,"en-US","female","C:\\Program Files\\DCS-SimpleRadio-Standalone\\google.json") +-- +-- -- alternatively for MS Desktop TTS (voices need to be installed locally first!) +-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",243,radio.modulation.AM,5002,MSRS.Voices.Microsoft.Hazel,"en-GB","female") +-- +-- -- define a different voice for the downed pilot(s) +-- my_aicsar:SetPilotTTSVoice(MSRS.Voices.Google.Standard.en_AU_Standard_D,"en-AU","male") +-- +-- -- define another voice for the operator +-- my_aicsar:SetOperatorTTSVoice(MSRS.Voices.Google.Standard.en_GB_Standard_A,"en-GB","female") +-- +-- ## Radio output via preproduced soundfiles +-- -- The easiest way to add a soundfile to your mission is to use the "Sound to..." trigger in the mission editor. This will effectively --- save your sound file inside of the .miz mission file. +-- save your sound file inside of the .miz mission file. [Example soundfiles are located on github](https://github.com/FlightControl-Master/MOOSE_SOUND/tree/master/AICSAR) -- -- To customize or localize your texts and sounds, you can take e.g. the following approach to add a German language version: -- @@ -422,7 +447,7 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) -- @param #string Event Event. -- @param #string To To state. -- @param Ops.FlightGroup#FLIGHTGROUP Helo - -- @param Ops.OpsGroup#OPSGROUP OpsGroup + -- @param Ops.OpsGroup#OPSGROUP OpsGroup --- On after "PilotKIA" event. -- @function [parent=#AICSAR] OnAfterPilotKIA @@ -1026,7 +1051,11 @@ function AICSAR:onafterHeloDown(From, Event, To, Helo, Index) elseif self.DCSRadio then self:DCSRadioBroadcast(Soundfile,Soundlength,text) elseif self.SRSTTSRadio then + if self.SRSOperatorVoice then + self.SRSQ:NewTransmission(text,nil,self.SRSOperator,nil,1) + else self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1) + end end local findex = 0 local fhname = Helo:GetName() From cb00a3fa382ea4873dae949f432d0c06447a7ab7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 9 Feb 2023 16:11:59 +0100 Subject: [PATCH 212/603] #DETECTION * Docu fixes --- Moose Development/Moose/Functional/Detection.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index dfafcae01..b72d42d81 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -474,7 +474,7 @@ do -- DETECTION_BASE -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. - -- @param #table DetectedItem The DetectedItem. + -- @param #DetectedItem DetectedItem The DetectedItem data structure. self:AddTransition( "*", "Stop", "Stopped" ) @@ -2478,14 +2478,14 @@ do -- DETECTION_AREAS --- DETECTION_AREAS constructor. -- @param #DETECTION_AREAS self -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role. - -- @param DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @param #number DetectionZoneRange The range in meters within which targets are grouped upon the first detected target. Default 5000m. -- @return #DETECTION_AREAS function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) -- Inherits from DETECTION_BASE local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) - self.DetectionZoneRange = DetectionZoneRange + self.DetectionZoneRange = DetectionZoneRange or 5000 self._SmokeDetectedUnits = false self._FlareDetectedUnits = false From c1ab6967743a52ca027c968fd6329679175bb20a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 10 Feb 2023 10:41:59 +0100 Subject: [PATCH 213/603] #NET --- Moose Development/Moose/Wrapper/Net.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index e5f3b068e..e822a176b 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -246,6 +246,15 @@ function NET:ForceSlot(Client,SideID,SlotID) end end +--- Force a client back to spectators. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @return #boolean Succes +function NET:ReturnToSpectators(Client) + local outcome = self:ForceSlot(Client,0) + return outcome +end + --- Converts a lua value to a JSON string. -- @param #string Lua Anything lua -- @return #table Json From 0823eb1ed0b80c4cca0d4b9d0bbe83fec8eee743 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 10 Feb 2023 11:37:51 +0100 Subject: [PATCH 214/603] #fix --- Moose Development/Moose/Core/Set.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 224ebc077..3920315a0 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -903,7 +903,7 @@ end do -- SET_GROUP - --- @type SET_GROUP + --- @type SET_GROUP #SET_GROUP -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain: From 0ec039bb3950af37e874c751a75bab8f48c4adc0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 10 Feb 2023 11:38:45 +0100 Subject: [PATCH 215/603] #PLAYERTASKCONTROLLER * Added `AddAgentSet()` --- Moose Development/Moose/Ops/PlayerTask.lua | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index bc8bd2bb8..94901b479 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -96,7 +96,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.12" +PLAYERTASK.version="0.1.14" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -3526,6 +3526,23 @@ function PLAYERTASKCONTROLLER:AddAgent(Recce) return self end +--- [User] Add agent SET_GROUP to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Set#SET_GROUP RecceSet SET_GROUP of agents. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:AddAgentSet(RecceSet) + self:T(self.lid.."AddAgentSet") + if self.Intel then + local Set = RecceSet:GetAliveSet() + for _,_Recce in pairs(Set) do + self.Intel:AddAgent(_Recce) + end + else + self:E(self.lid.."*****NO detection has been set up (yet)!") + end + return self +end + --- [User] Set up detection of STATIC objects. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff Set to `true`for on and `false`for off. From 055cbf4f7ae0142e6e50f8fb82d419ace9dc150e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 13 Feb 2023 17:12:25 +0100 Subject: [PATCH 216/603] DB --- Moose Development/Moose/Core/Database.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 1b8bfeb0d..7d49ffc45 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -89,6 +89,7 @@ DATABASE = { FLIGHTGROUPS = {}, FLIGHTCONTROLS = {}, OPSZONES = {}, + PATHLINES = {}, } local _DATABASECoalition = From bf50adaa224dbfeadc31a7091f9d7ab11a866057 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 14 Feb 2023 12:58:39 +0100 Subject: [PATCH 217/603] #PLAYERTASKCONTROLLER * Added ship detail types --- Moose Development/Moose/Ops/PlayerTask.lua | 37 +++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 94901b479..e4afe4b7a 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1369,6 +1369,13 @@ PLAYERTASKCONTROLLER.Messages = { AIRDEFENSE = "Airdefense", SAM = "SAM", GROUP = "Group", + UNARMEDSHIP = "Merchant", + LIGHTARMEDSHIP = "Light Boat", + CORVETTE = "Corvette", + FRIGATE = "Frigate", + CRUISER = "Cruiser", + DESTROYER = "Destroyer", + CARRIER = "Aircraft Carrier", }, DE = { TASKABORT = "Auftrag abgebrochen!", @@ -1441,12 +1448,19 @@ PLAYERTASKCONTROLLER.Messages = { AIRDEFENSE = "Flak", SAM = "Luftabwehr", GROUP = "Einheit", + UNARMEDSHIP = "Handelsschiff", + LIGHTARMEDSHIP = "Tender", + CORVETTE = "Korvette", + FRIGATE = "Fregatte", + CRUISER = "Kreuzer", + DESTROYER = "Zerstörer", + CARRIER = "Flugzeugträger", }, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.56" +PLAYERTASKCONTROLLER.version="0.1.57" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2262,6 +2276,27 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue() --self:T(self.lid.."Target TypeName = "..target.TypeName) end + if self.UseTypeNames and object:IsShip() then + local threat = object:GetThreatLevel() + local typekey = "UNARMEDSHIP" + if threat == 1 then + typekey = "LIGHTARMEDSHIP" + elseif threat == 2 then + typekey = "CORVETTE" + elseif threat == 3 or threat == 4 then + typekey = "FRIGATE" + elseif threat == 5 or threat == 6 then + typekey = "CRUISER" + elseif threat == 7 or threat == 8 then + typekey = "DESTROYER" + elseif threat >= 9 then + typekey = "CARRIER" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + target.TypeName = typename + --self:T(self.lid.."Target TypeName = "..target.TypeName) + end + self:_AddTask(target) end return self From b649ad73a8a811db48dd4392949155f009923283 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Feb 2023 10:31:40 +0100 Subject: [PATCH 218/603] Fixes --- Moose Development/Moose/Core/Spot.lua | 4 +- Moose Development/Moose/Ops/PlayerTask.lua | 102 +++++++++++---------- Moose Development/Moose/Wrapper/Group.lua | 2 +- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index 2384df073..f543a6bd7 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -5,7 +5,7 @@ -- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: -- -- * Spot for a defined duration. --- * Updates of laer spot position every 0.2 seconds for moving targets. +-- * Updates of laser spot position every 0.2 seconds for moving targets. -- * Wiggle the spot at the target. -- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Implement a status machine, LaseOn, LaseOff. @@ -57,7 +57,7 @@ do -- -- ## 1. SPOT constructor -- - -- * @{#SPOT.New}(..\Presentations\SPOT\Dia2.JPG): Creates a new SPOT object. + -- * @{#SPOT.New}(): Creates a new SPOT object. -- -- ## 2. SPOT is a FSM -- diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index e4afe4b7a..b5ee9e527 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1460,7 +1460,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.57" +PLAYERTASKCONTROLLER.version="0.1.58" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2245,56 +2245,60 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue() end end - if self.UseTypeNames and object:IsGround() then - -- * Threat level 0: Unit is unarmed. - -- * Threat level 1: Unit is infantry. - -- * Threat level 2: Unit is an infantry vehicle. - -- * Threat level 3: Unit is ground artillery. - -- * Threat level 4: Unit is a tank. - -- * Threat level 5: Unit is a modern tank or ifv with ATGM. - -- * Threat level 6: Unit is a AAA. - -- * Threat level 7: Unit is a SAM or manpad, IR guided. - -- * Threat level 8: Unit is a Short Range SAM, radar guided. - -- * Threat level 9: Unit is a Medium Range SAM, radar guided. - -- * Threat level 10: Unit is a Long Range SAM, radar guided. - local threat = object:GetThreatLevel() - local typekey = "INFANTRY" - if threat == 0 or threat == 2 then - typekey = "TECHNICAL" - elseif threat == 3 then - typekey = "ARTILLERY" - elseif threat == 4 or threat == 5 then - typekey = "TANKS" - elseif threat == 6 or threat == 7 then - typekey = "AIRDEFENSE" - elseif threat >= 8 then - typekey = "SAM" - end - local typename = self.gettext:GetEntry(typekey,self.locale) - local gname = self.gettext:GetEntry("GROUP",self.locale) - target.TypeName = string.format("%s %s",typename,gname) - --self:T(self.lid.."Target TypeName = "..target.TypeName) - end + if object:IsInstanceOf("UNIT") or object:IsInstanceOf("GROUP") then - if self.UseTypeNames and object:IsShip() then - local threat = object:GetThreatLevel() - local typekey = "UNARMEDSHIP" - if threat == 1 then - typekey = "LIGHTARMEDSHIP" - elseif threat == 2 then - typekey = "CORVETTE" - elseif threat == 3 or threat == 4 then - typekey = "FRIGATE" - elseif threat == 5 or threat == 6 then - typekey = "CRUISER" - elseif threat == 7 or threat == 8 then - typekey = "DESTROYER" - elseif threat >= 9 then - typekey = "CARRIER" + if self.UseTypeNames and object:IsGround() then + -- * Threat level 0: Unit is unarmed. + -- * Threat level 1: Unit is infantry. + -- * Threat level 2: Unit is an infantry vehicle. + -- * Threat level 3: Unit is ground artillery. + -- * Threat level 4: Unit is a tank. + -- * Threat level 5: Unit is a modern tank or ifv with ATGM. + -- * Threat level 6: Unit is a AAA. + -- * Threat level 7: Unit is a SAM or manpad, IR guided. + -- * Threat level 8: Unit is a Short Range SAM, radar guided. + -- * Threat level 9: Unit is a Medium Range SAM, radar guided. + -- * Threat level 10: Unit is a Long Range SAM, radar guided. + local threat = object:GetThreatLevel() + local typekey = "INFANTRY" + if threat == 0 or threat == 2 then + typekey = "TECHNICAL" + elseif threat == 3 then + typekey = "ARTILLERY" + elseif threat == 4 or threat == 5 then + typekey = "TANKS" + elseif threat == 6 or threat == 7 then + typekey = "AIRDEFENSE" + elseif threat >= 8 then + typekey = "SAM" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + local gname = self.gettext:GetEntry("GROUP",self.locale) + target.TypeName = string.format("%s %s",typename,gname) + --self:T(self.lid.."Target TypeName = "..target.TypeName) end - local typename = self.gettext:GetEntry(typekey,self.locale) - target.TypeName = typename - --self:T(self.lid.."Target TypeName = "..target.TypeName) + + if self.UseTypeNames and object:IsShip() then + local threat = object:GetThreatLevel() + local typekey = "UNARMEDSHIP" + if threat == 1 then + typekey = "LIGHTARMEDSHIP" + elseif threat == 2 then + typekey = "CORVETTE" + elseif threat == 3 or threat == 4 then + typekey = "FRIGATE" + elseif threat == 5 or threat == 6 then + typekey = "CRUISER" + elseif threat == 7 or threat == 8 then + typekey = "DESTROYER" + elseif threat >= 9 then + typekey = "CARRIER" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + target.TypeName = typename + --self:T(self.lid.."Target TypeName = "..target.TypeName) + end + end self:_AddTask(target) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 0f60e1ebe..99125e66a 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2810,7 +2810,7 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) if self:IsAlive() then local IsPlayer = self:IsPlayer() local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1 - local callsignroot = string.match(shortcallsign, '(%a+)') -- Uzi + local callsignroot = string.match(shortcallsign, '(%a+)') or "Ghost" -- Uzi --self:I("CallSign = " .. callsignroot) local groupname = self:GetName() local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91 From 250b12c0412497ffb43adf6e223340963871a8a1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Feb 2023 12:16:46 +0100 Subject: [PATCH 219/603] fix --- Moose Development/Moose/Wrapper/Group.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 99125e66a..3c81c1b45 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1,4 +1,4 @@ ------ **Wrapper** - GROUP wraps the DCS Class Group objects. +--- **Wrapper** - GROUP wraps the DCS Class Group objects. -- -- === -- From 65d3c3f2772ab11eed0ec8ffea83cdebaedd0eb4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Feb 2023 12:17:49 +0100 Subject: [PATCH 220/603] #SPOT - minor fixes --- Moose Development/Moose/Core/Spot.lua | 29 ++++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index f543a6bd7..60e58b18e 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -13,18 +13,8 @@ -- === -- -- # Demo Missions --- --- ### [SPOT Demo Missions source code]() --- --- ### [SPOT Demo Missions, only for beta testers]() -- --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) --- --- === --- --- # YouTube Channel --- --- ### [SPOT YouTube Channel]() +-- ### [Demo Missions on GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS) -- -- === -- @@ -50,7 +40,7 @@ do --- Implements the target spotting or marking functionality, but adds additional luxury to be able to: -- -- * Mark targets for a defined duration. - -- * Updates of laer spot position every 0.2 seconds for moving targets. + -- * Updates of laser spot position every 0.2 seconds for moving targets. -- * Wiggle the spot at the target. -- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Implement a status machine, LaseOn, LaseOff. @@ -217,6 +207,8 @@ do self.Recce = Recce + + self.RecceName = self.Recce:GetName() self.LaseScheduler = SCHEDULER:New( self ) @@ -243,6 +235,9 @@ do end self.Target = Target + + self.TargetName = Target:GetName() + self.LaserCode = LaserCode self.Lasing = true @@ -302,12 +297,18 @@ do function SPOT:OnEventDead(EventData) self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } ) if self.Target then - if EventData.IniDCSUnitName == self.Target:GetName() then - self:F( {"Target dead ", self.Target:GetName() } ) + if EventData.IniDCSUnitName == self.TargetName then + self:F( {"Target dead ", self.TargetName } ) self:Destroyed() self:LaseOff() end end + if self.Recce then + if EventData.IniDCSUnitName == self.RecceName then + self:F( {"Recce dead ", self.RecceName } ) + self:LaseOff() + end + end end --- @param #SPOT self From a3f92194608d02d4d74cbf092d9c71b40ffc98db Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Feb 2023 12:19:55 +0100 Subject: [PATCH 221/603] fx --- Moose Development/Moose/Core/Spot.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index 60e58b18e..d87606937 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -40,7 +40,7 @@ do --- Implements the target spotting or marking functionality, but adds additional luxury to be able to: -- -- * Mark targets for a defined duration. - -- * Updates of laser spot position every 0.2 seconds for moving targets. + -- * Updates of laser spot position every 0.25 seconds for moving targets. -- * Wiggle the spot at the target. -- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Implement a status machine, LaseOn, LaseOff. From 44b6315361de5bc76d99dbd643dbd3631691d2fa Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 Feb 2023 11:42:06 +0100 Subject: [PATCH 222/603] #NET * Added event management for clients and block/unblock functions --- Moose Development/Moose/Ops/CSAR.lua | 2 +- Moose Development/Moose/Wrapper/Net.lua | 230 +++++++++++++++++++++++- 2 files changed, 228 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 425af74b0..abbe3980d 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -316,7 +316,7 @@ function CSAR:New(Coalition, Template, Alias) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #CSAR - BASE:T({Coalition, Prefixes, Alias}) + BASE:T({Coalition, Template, Alias}) --set Coalition if Coalition and type(Coalition)=="string" then diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index e822a176b..d2ac28285 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -16,14 +16,27 @@ do -- @type NET -- @field #string ClassName -- @field #string Version --- @extends Core.Base#BASE +-- @field #string lid +-- @field #number BlockTime +-- @field #table BlockedPilots +-- @field #table KnownPilots +-- @field #string BlockMessage +-- @field #string UnblockMessage +-- @extends Core.Fsm#FSM --- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) +-- with some added FSM functions and options to block/unblock players in MP environments. -- -- @field #NET NET = { ClassName = "NET", - Version = "0.0.2" + Version = "0.0.3", + BlockTime = 600, + BlockedPilots = {}, + KnownPilots = {}, + BlockMessage = nil, + UnblockMessage = nil, + lid = nil, } --- Instantiate a new NET object. @@ -31,7 +44,218 @@ NET = { -- @return #NET self function NET:New() -- Inherit base. - local self = BASE:Inherit(self, BASE:New()) -- #NET + local self = BASE:Inherit(self, FSM:New()) -- #NET + + self.BlockTime = 600 + self.BlockedPilots = {} + self.KnownPilots = {} + self:SetBlockMessage() + self:SetUnblockMessage() + + self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) + self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler) + self:HandleEvent(EVENTS.PilotDead,self._EventHandler) + self:HandleEvent(EVENTS.Ejection,self._EventHandler) + self:HandleEvent(EVENTS.Crash,self._EventHandler) + self:HandleEvent(EVENTS.SelfKillPilot,self._EventHandler) + + -- Start State. + self:SetStartState("Running") + + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("*", "Run", "Running") -- Start FSM. + self:AddTransition("*", "PlayerJoined", "*") + self:AddTransition("*", "PlayerLeft", "*") + self:AddTransition("*", "PlayerDied", "*") + self:AddTransition("*", "PlayerEjected", "*") + self:AddTransition("*", "PlayerBlocked", "*") + self:AddTransition("*", "PlayerUnblocked", "*") + + self.lid = string.format("NET %s | ",self.Version) + + --- FSM Function OnAfterPlayerJoined. + -- @function [parent=#NET] OnAfterPlayerJoined + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param #string Name Name of joining Pilot. + -- @return #NET self + + --- FSM Function OnAfterPlayerLeft. + -- @function [parent=#NET] OnAfterPlayerLeft + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param #string Name Name of leaving Pilot. + -- @return #NET self + + --- FSM Function OnAfterPlayerEjected. + -- @function [parent=#NET] OnAfterPlayerEjected + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param #string Name Name of leaving Pilot. + -- @return #NET self + + --- FSM Function OnAfterPlayerDied. + -- @function [parent=#NET] OnAfterPlayerDied + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param #string Name Name of dead Pilot. + -- @return #NET self + + --- FSM Function OnAfterPlayerBlocked. + -- @function [parent=#NET] OnAfterPlayerBlocked + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client Client Object. + -- @param #string Name Name of blocked Pilot. + -- @param #number Seconds Blocked for this number of seconds + -- @return #NET self + + --- FSM Function OnAfterPlayerUnblocked. + -- @function [parent=#NET] OnAfterPlayerUnblocked + -- @param #NET self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client Client Object. + -- @param #string Name Name of unblocked Pilot. + -- @return #NET self + + return self +end + +--- [Internal] Event Handler +-- @param #NET self +-- @param Core.Event#EVENTDATA EventData +-- @return #NET self +function NET:_EventHandler(EventData) + self:T(self.lid .. " _EventHandler") + self:T2({Event = EventData.id}) + local data = EventData -- Core.Event#EVENTDATA EventData + if data.id and data.IniUnit and (data.IniPlayerName or data.IniUnit:GetPlayerName()) then + -- Get PlayerName + local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName() + self:T(self.lid.."Event for: "..name) + -- Joining + if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then + -- Check for known pilots + local TNow = timer.getTime() + if self.BlockedPilots[name] and TNow < self.BlockedPilots[name] then + -- block pilot + self:ReturnToSpectators(data.IniUnit) + else + self.KnownPilots[name] = true + self.BlockedPilots[name] = nil + self:__PlayerJoined(1,data.IniUnit,name) + return self + end + end + -- Leaving + if data.id == EVENTS.PlayerLeaveUnit and self.KnownPilots[name] then + self:__PlayerLeft(1,data.IniUnit,name) + self.KnownPilots[name] = false + return self + end + -- Ejected + if data.id == EVENTS.Ejection and self.KnownPilots[name] then + self:__PlayerEjected(1,data.IniUnit,name) + self.KnownPilots[name] = false + return self + end + -- Dead, Crash, Suicide + if (data.id == EVENTS.PilotDead or data.id == EVENTS.SelfKillPilot or data.id == EVENTS.Crash) and self.KnownPilots[name] then + self:__PlayerDied(1,data.IniUnit,name) + self.KnownPilots[name] = false + return self + end + end + return self +end + +--- Block a player. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client CLIENT object. +-- @param #string PlayerName (optional) Name of the player. +-- @param #number Seconds (optional) Number of seconds the player has to wait before rejoining. +-- @param #string Message (optional) Message to be sent via chat. +-- @return #NET self +function NET:BlockPlayer(Client,PlayerName,Seconds,Message) + local name + if Client then + name = CLIENT:GetPlayerName() + elseif PlayerName then + name = PlayerName + else + self:F(self.lid.."Block: No PlayerName given or not found!") + return self + end + local addon = Seconds or self.BlockTime + self.BlockedPilots[name] = timer.getTime()+addon + local message = Message or self.BlockMessage + if Client then + self:SendChatToPlayer(message,Client) + else + self:SendChat(name..": "..message) + end + self:__PlayerBlocked(1,Client,name,Seconds) + self:ReturnToSpectators(Client) + return self +end + +--- Unblock a player. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client CLIENT object +-- @param #string PlayerName (optional) Name of the player. +-- @param #string Message (optional) Message to be sent via chat. +-- @return #NET self +function NET:UnblockPlayer(Client,PlayerName,Message) + local name + if Client then + name = CLIENT:GetPlayerName() + elseif PlayerName then + name = PlayerName + else + self:F(self.lid.."Unblock: No PlayerName given or not found!") + return self + end + self.BlockedPilots[name] = nil + local message = Message or self.UnblockMessage + if Client then + self:SendChatToPlayer(message,Client) + else + self:SendChat(name..": "..message) + end + self:__PlayerUnblocked(1,Client,name) + return self +end + +function NET:SetBlockMessage(Text) + self.BlockMessage = Text or "You are blocked from joining. Wait time is: "..self.BlockTime.." seconds!" + return self +end + +function NET:SetBlockTime(Seconds) + self.BlockTime = Seconds or 600 + return self +end + +function NET:SetUnblockMessage(Text) + self.UnblockMessage = Text or "You are unblocked now and can join again." return self end From 578b68f635a384586439655e0b2d5f6b8402ada2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 Feb 2023 18:22:26 +0100 Subject: [PATCH 223/603] #AICSAR --- Moose Development/Moose/Functional/AICSAR.lua | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index b1ee470ca..0780b6814 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -52,6 +52,7 @@ -- @field Core.Set#SET_CLIENT playerset Track if alive heli pilots are available. -- @field #boolean limithelos limit available number of helos going on mission (defaults to true) -- @field #number helonumber number of helos available (default: 3) +-- @field Utilities.FiFo#FIFO PilotStore -- @extends Core.Fsm#FSM @@ -186,7 +187,7 @@ -- @field #AICSAR AICSAR = { ClassName = "AICSAR", - version = "0.1.9", + version = "0.1.10", lid = "", coalition = coalition.side.BLUE, template = "", @@ -226,6 +227,7 @@ AICSAR = { SRSPilotVoice = false, SRSOperator = nil, SRSOperatorVoice = false, + PilotStore = nil, } -- TODO Messages @@ -369,6 +371,9 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) -- Set some string id for output to DCS.log file. self.lid=string.format("%s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") + --Pilot Store + self.PilotStore = FIFO:New() + -- Start State. self:SetStartState("Stopped") @@ -385,6 +390,7 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:HandleEvent(EVENTS.LandingAfterEjection) + self:HandleEvent(EVENTS.Ejection) self:__Start(math.random(2,5)) @@ -438,7 +444,8 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) -- @param #AICSAR self -- @param #string From From state. -- @param #string Event Event. - -- @param #string To To state. + -- @param #string To To state. + -- @param #string PilotName --- On after "PilotUnloaded" event. -- @function [parent=#AICSAR] OnAfterPilotUnloaded @@ -648,6 +655,19 @@ function AICSAR:DCSRadioBroadcast(Soundfile,Duration,Subtitle) return self end +--- [Internal] Catch the ejection and save the pilot name +-- @param #AICSAR self +-- @param Core.Event#EVENTDATA EventData +-- @return #AICSAR self +function AICSAR:OnEventEjection(EventData) + local _event = EventData -- Core.Event#EVENTDATA + if _event.IniPlayerName then + self.PilotStore:Push(_event.IniPlayerName) + self:T(self.lid.."Pilot Ejected: ".._event.IniPlayerName) + end + return self +end + --- [Internal] Catch the landing after ejection and spawn a pilot in situ. -- @param #AICSAR self -- @param Core.Event#EVENTDATA EventData @@ -667,7 +687,7 @@ function AICSAR:OnEventLandingAfterEjection(EventData) local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p) local _country = _event.initiator:getCountry() local _coalition = coalition.getCountryCoalition( _country ) - + -- DONE: add distance check local distancetofarp = _LandingPos:Get2DDistance(self.farp:GetCoordinate()) @@ -858,6 +878,7 @@ function AICSAR:_CheckQueue(OpsGroup) for _index, _pilot in pairs(self.pilotqueue) do local classname = _pilot.ClassName and _pilot.ClassName or "NONE" local name = _pilot.GroupName and _pilot.GroupName or "NONE" + local playername = "John Doe" local helocount = self:_CountHelos() --self:T("Looking at " .. classname .. " " .. name) -- find one w/o mission @@ -873,7 +894,10 @@ function AICSAR:_CheckQueue(OpsGroup) end self.pilotqueue[_index] = nil self.rescued[_index] = true - self:__PilotRescued(2) + if self.PilotStore:Count() > 0 then + playername = self.PilotStore:Pull() + end + self:__PilotRescued(2,playername) if flightgroup then flightgroup.AICSARReserved = false end @@ -1095,8 +1119,9 @@ end -- @param #string From -- @param #string Event -- @param #string To +-- @param #string PilotName -- @return #AICSAR self -function AICSAR:onafterPilotRescued(From, Event, To) +function AICSAR:onafterPilotRescued(From, Event, To, PilotName) self:T({From, Event, To}) local text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTRESCUED",self.locale) if self.verbose then From 985adc0267579cba3d01c41b134948e56534fc2e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Feb 2023 08:54:33 +0100 Subject: [PATCH 224/603] Fixes --- Moose Development/Moose/Functional/AICSAR.lua | 33 +++++++++++++++++-- Moose Development/Moose/Wrapper/Net.lua | 10 +++--- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index 0780b6814..45b6f9a4e 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -53,6 +53,8 @@ -- @field #boolean limithelos limit available number of helos going on mission (defaults to true) -- @field #number helonumber number of helos available (default: 3) -- @field Utilities.FiFo#FIFO PilotStore +-- @field #number Altitude Default altitude setting for the helicopter FLIGHTGROUP 1500ft. +-- @field #number Speed Default speed setting for the helicopter FLIGHTGROUP is 100kn. -- @extends Core.Fsm#FSM @@ -78,7 +80,7 @@ -- -- @param #string Alias Name of this instance. -- -- @param #number Coalition Coalition as in coalition.side.BLUE, can also be passed as "blue", "red" or "neutral" -- -- @param #string Pilottemplate Pilot template name. --- -- @param #string Helotemplate Helicopter template name. +-- -- @param #string Helotemplate Helicopter template name. Set the template to "cold start". Hueys work best. -- -- @param Wrapper.Airbase#AIRBASE FARP FARP object or Airbase from where to start. -- -- @param Core.Zone#ZONE MASHZone Zone where to drop pilots after rescue. -- local my_aicsar=AICSAR:New("Luftrettung",coalition.side.BLUE,"Downed Pilot","Rescue Helo",AIRBASE:FindByName("Test FARP"),ZONE:New("MASH")) @@ -187,7 +189,7 @@ -- @field #AICSAR AICSAR = { ClassName = "AICSAR", - version = "0.1.10", + version = "0.1.11", lid = "", coalition = coalition.side.BLUE, template = "", @@ -228,6 +230,8 @@ AICSAR = { SRSOperator = nil, SRSOperatorVoice = false, PilotStore = nil, + Speed = 100, + Altitude = 1500, } -- TODO Messages @@ -785,10 +789,15 @@ function AICSAR:_InitMission(Pilot,Index) -- Cargo transport assignment. local opstransport=OPSTRANSPORT:New(Pilot, pickupzone, self.farpzone) + local helo = self:_GetFlight() -- inject reservation helo.AICSARReserved = true + helo:SetDefaultAltitude(self.Altitude or 1500) + + helo:SetDefaultSpeed(self.Speed or 100) + -- Cargo transport assignment to first Huey group. helo:AddOpsTransport(opstransport) @@ -835,6 +844,26 @@ function AICSAR:_CheckInMashZone(Pilot) end end +--- [User] Set default helo speed. Note - AI might have other ideas. Defaults to 100kn. +-- @param #AICSAR self +-- @param #number Knots Speed in knots. +-- @return #AICSAR self +function AICSAR:SetDefaultSpeed(Knots) + self:T(self.lid .. "SetDefaultSpeed") + self.Speed = Knots or 100 + return self +end + +--- [User] Set default helo altitudeAGL. Note - AI might have other ideas. Defaults to 1500ft. +-- @param #AICSAR self +-- @param #number Feet AGL set in feet. +-- @return #AICSAR self +function AICSAR:SetDefaultAltitude(Feet) + self:T(self.lid .. "SetDefaultAltitude") + self.Altitude = Feet or 1500 + return self +end + --- [Internal] Check helo queue -- @param #AICSAR self -- @return #AICSAR self diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index d2ac28285..34c7bdbdf 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -91,7 +91,7 @@ function NET:New() -- @param #string From State. -- @param #string Event Trigger. -- @param #string To State. - -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param Wrapper.Unit#UNIT Client Unit Object, might be nil. -- @param #string Name Name of leaving Pilot. -- @return #NET self @@ -101,7 +101,7 @@ function NET:New() -- @param #string From State. -- @param #string Event Trigger. -- @param #string To State. - -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param Wrapper.Unit#UNIT Client Unit Object, might be nil. -- @param #string Name Name of leaving Pilot. -- @return #NET self @@ -111,7 +111,7 @@ function NET:New() -- @param #string From State. -- @param #string Event Trigger. -- @param #string To State. - -- @param Wrapper.Unit#UNIT Client Unit Object. + -- @param Wrapper.Unit#UNIT Client Unit Object, might be nil. -- @param #string Name Name of dead Pilot. -- @return #NET self @@ -121,7 +121,7 @@ function NET:New() -- @param #string From State. -- @param #string Event Trigger. -- @param #string To State. - -- @param Wrapper.Client#CLIENT Client Client Object. + -- @param Wrapper.Client#CLIENT Client Client Object, might be nil. -- @param #string Name Name of blocked Pilot. -- @param #number Seconds Blocked for this number of seconds -- @return #NET self @@ -132,7 +132,7 @@ function NET:New() -- @param #string From State. -- @param #string Event Trigger. -- @param #string To State. - -- @param Wrapper.Client#CLIENT Client Client Object. + -- @param Wrapper.Client#CLIENT Client Client Object, might be nil. -- @param #string Name Name of unblocked Pilot. -- @return #NET self From d132447af8aa657df34455da9b1d550a6f6b1a08 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Feb 2023 13:22:39 +0100 Subject: [PATCH 225/603] #Net --- Moose Development/Moose/Wrapper/Net.lua | 47 +++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index 34c7bdbdf..1e3845979 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -1,6 +1,6 @@ --- **Wrapper** - DCS net functions. -- --- Encapsules **multiplayer** environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) +-- Encapsules **multiplayer server** environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) -- -- === -- @@ -22,6 +22,7 @@ do -- @field #table KnownPilots -- @field #string BlockMessage -- @field #string UnblockMessage +-- @field #table BlockedUCIDs -- @extends Core.Fsm#FSM --- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) @@ -30,9 +31,10 @@ do -- @field #NET NET = { ClassName = "NET", - Version = "0.0.3", + Version = "0.0.4", BlockTime = 600, BlockedPilots = {}, + BlockedUCIDs = {}, KnownPilots = {}, BlockMessage = nil, UnblockMessage = nil, @@ -150,17 +152,22 @@ function NET:_EventHandler(EventData) if data.id and data.IniUnit and (data.IniPlayerName or data.IniUnit:GetPlayerName()) then -- Get PlayerName local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName() - self:T(self.lid.."Event for: "..name) + local ucid = self:GetPlayerUCID(nil,name) + self:T(self.lid.."Event for: "..name.." | UCID: "..ucid) -- Joining if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then -- Check for known pilots local TNow = timer.getTime() if self.BlockedPilots[name] and TNow < self.BlockedPilots[name] then - -- block pilot + -- block pilot by name + self:ReturnToSpectators(data.IniUnit) + elseif self.BlockedUCIDs[ucid] and TNow < self.BlockedUCIDs[ucid] then + -- block pilot by ucid self:ReturnToSpectators(data.IniUnit) else self.KnownPilots[name] = true self.BlockedPilots[name] = nil + self.BlockedUCIDs[ucid] = nil self:__PlayerJoined(1,data.IniUnit,name) return self end @@ -204,8 +211,10 @@ function NET:BlockPlayer(Client,PlayerName,Seconds,Message) self:F(self.lid.."Block: No PlayerName given or not found!") return self end + local ucid = self:GetPlayerUCID(Client,name) local addon = Seconds or self.BlockTime self.BlockedPilots[name] = timer.getTime()+addon + self.BlockedUCIDs[ucid] = timer.getTime()+addon local message = Message or self.BlockMessage if Client then self:SendChatToPlayer(message,Client) @@ -233,7 +242,9 @@ function NET:UnblockPlayer(Client,PlayerName,Message) self:F(self.lid.."Unblock: No PlayerName given or not found!") return self end + local ucid = self:GetPlayerUCID(Client,name) self.BlockedPilots[name] = nil + self.BlockedUCIDs[ucid] = nil local message = Message or self.UnblockMessage if Client then self:SendChatToPlayer(message,Client) @@ -244,16 +255,28 @@ function NET:UnblockPlayer(Client,PlayerName,Message) return self end +--- Set block chat message. +-- @param #NET self +-- @param #string Text The message +-- @return #NET self function NET:SetBlockMessage(Text) self.BlockMessage = Text or "You are blocked from joining. Wait time is: "..self.BlockTime.." seconds!" return self end +--- Set block time in seconds. +-- @param #NET self +-- @param #number Seconds Numnber of seconds this block will last. Defaults to 600. +-- @return #NET self function NET:SetBlockTime(Seconds) self.BlockTime = Seconds or 600 return self end +--- Set unblock chat message. +-- @param #NET self +-- @param #string Text The message +-- @return #NET self function NET:SetUnblockMessage(Text) self.UnblockMessage = Text or "You are unblocked now and can join again." return self @@ -275,7 +298,7 @@ end -- @param #NET self -- @param #string Name The player name whose ID to find -- @return #number PlayerID or nil -function NET:GetPlayerIdByName(Name) +function NET:GetPlayerIDByName(Name) local playerList = self:GetPlayerList() for i=1,#playerList do local playerName = net.get_name(i) @@ -292,7 +315,7 @@ end -- @return #number PlayerID or nil function NET:GetPlayerIDFromClient(Client) local name = Client:GetPlayerName() - local id = self:GetPlayerIdByName(name) + local id = self:GetPlayerIDByName(name) return id end @@ -384,6 +407,18 @@ function NET:GetPlayerInfo(Client,Attribute) end end + +--- Get player UCID from player CLIENT object or player name. Provide either one. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client object to be used. +-- @param #string Name Player name to be used. +-- @return #boolean success +function NET:GetPlayerUCID(Client,Name) + local PlayerID = self:GetPlayerIDByName(Name) or self:GetPlayerIDFromClient(Client) + local ucid = net.get_player_info(tonumber(PlayerID), 'ucid') + return ucid +end + --- Kicks a player from the server. Can display a message to the user. -- @param #NET self -- @param Wrapper.Client#CLIENT Client The client From 8c083cccc8c662d26d12b5ddab7ccd8905fb973f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Feb 2023 14:32:17 +0100 Subject: [PATCH 226/603] #NET - bug fix --- Moose Development/Moose/Wrapper/Net.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index 1e3845979..eafa6a640 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -208,7 +208,7 @@ function NET:BlockPlayer(Client,PlayerName,Seconds,Message) elseif PlayerName then name = PlayerName else - self:F(self.lid.."Block: No PlayerName given or not found!") + self:F(self.lid.."Block: No Client or PlayerName given or nothing found!") return self end local ucid = self:GetPlayerUCID(Client,name) @@ -414,7 +414,14 @@ end -- @param #string Name Player name to be used. -- @return #boolean success function NET:GetPlayerUCID(Client,Name) - local PlayerID = self:GetPlayerIDByName(Name) or self:GetPlayerIDFromClient(Client) + local PlayerID = nil + if Client then + PlayerID = self:GetPlayerIDFromClient(Client) + elseif Name then + PlayerID = self:GetPlayerIDByName(Name) + else + self:E(self.lid.."Neither client nor name provided!") + end local ucid = net.get_player_info(tonumber(PlayerID), 'ucid') return ucid end From 6acf0515394c62a9703599476026e9301c758468 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Feb 2023 15:02:03 +0100 Subject: [PATCH 227/603] #Net Fixes --- Moose Development/Moose/Wrapper/Net.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index eafa6a640..b7c61ed86 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -204,7 +204,7 @@ end function NET:BlockPlayer(Client,PlayerName,Seconds,Message) local name if Client then - name = CLIENT:GetPlayerName() + name = Client:GetPlayerName() elseif PlayerName then name = PlayerName else @@ -235,7 +235,7 @@ end function NET:UnblockPlayer(Client,PlayerName,Message) local name if Client then - name = CLIENT:GetPlayerName() + name = Client:GetPlayerName() elseif PlayerName then name = PlayerName else From 2b02f6659ac7fb08f87584a8d6900e6f51683a8e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Feb 2023 15:41:10 +0100 Subject: [PATCH 228/603] #Net --- Moose Development/Moose/Wrapper/Net.lua | 39 +++++++++++++++++++------ 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index b7c61ed86..87840fd78 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -216,8 +216,8 @@ function NET:BlockPlayer(Client,PlayerName,Seconds,Message) self.BlockedPilots[name] = timer.getTime()+addon self.BlockedUCIDs[ucid] = timer.getTime()+addon local message = Message or self.BlockMessage - if Client then - self:SendChatToPlayer(message,Client) + if name then + self:SendChatToPlayer(message,name) else self:SendChat(name..": "..message) end @@ -246,8 +246,8 @@ function NET:UnblockPlayer(Client,PlayerName,Message) self.BlockedPilots[name] = nil self.BlockedUCIDs[ucid] = nil local message = Message or self.UnblockMessage - if Client then - self:SendChatToPlayer(message,Client) + if name then + self:SendChatToPlayer(message,name) else self:SendChat(name..": "..message) end @@ -314,18 +314,22 @@ end -- @param Wrapper.Client#CLIENT Client The client -- @return #number PlayerID or nil function NET:GetPlayerIDFromClient(Client) - local name = Client:GetPlayerName() - local id = self:GetPlayerIDByName(name) - return id + if Client then + local name = Client:GetPlayerName() + local id = self:GetPlayerIDByName(name) + return id + else + return nil + end end ---- Send chat message to a specific player. +--- Send chat message to a specific player using the CLIENT object. -- @param #NET self -- @param #string Message The text message -- @param Wrapper.Client#CLIENT ToClient Client receiving the message -- @param Wrapper.Client#CLIENT FromClient (Optional) Client sending the message -- @return #NET self -function NET:SendChatToPlayer(Message, ToClient, FromClient) +function NET:SendChatToClient(Message, ToClient, FromClient) local PlayerId = self:GetPlayerIDFromClient(ToClient) local FromId = self:GetPlayerIDFromClient(FromClient) if Message and PlayerId and FromId then @@ -336,6 +340,23 @@ function NET:SendChatToPlayer(Message, ToClient, FromClient) return self end +--- Send chat message to a specific player using the player name +-- @param #NET self +-- @param #string Message The text message +-- @param #string ToPlayer Player receiving the message +-- @param #string FromPlayer(Optional) Player sending the message +-- @return #NET self +function NET:SendChatToPlayer(Message, ToPlayer, FromPlayer) + local PlayerId = self:GetPlayerIDByName(ToPlayer) + local FromId = self:GetPlayerIDByName(FromPlayer) + if Message and PlayerId and FromId then + net.send_chat_to(Message, tonumber(PlayerId) , tonumber(FromId)) + elseif Message and PlayerId then + net.send_chat_to(Message, tonumber(PlayerId)) + end + return self +end + --- Load a specific mission. -- @param #NET self -- @param #string Path and Mission From ffb6cbfb171e1ca6adbb9d25e7b825fbcb37ac70 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Feb 2023 16:23:16 +0100 Subject: [PATCH 229/603] NET --- Moose Development/Moose/Wrapper/Net.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index 87840fd78..f98f1f93f 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -31,7 +31,7 @@ do -- @field #NET NET = { ClassName = "NET", - Version = "0.0.4", + Version = "0.0.5", BlockTime = 600, BlockedPilots = {}, BlockedUCIDs = {}, @@ -202,7 +202,7 @@ end -- @param #string Message (optional) Message to be sent via chat. -- @return #NET self function NET:BlockPlayer(Client,PlayerName,Seconds,Message) - local name + local name = PlayerName if Client then name = Client:GetPlayerName() elseif PlayerName then @@ -222,7 +222,10 @@ function NET:BlockPlayer(Client,PlayerName,Seconds,Message) self:SendChat(name..": "..message) end self:__PlayerBlocked(1,Client,name,Seconds) - self:ReturnToSpectators(Client) + local PlayerID = self:GetPlayerIDByName(name) + if PlayerID and tonumber(PlayerID) ~= 1 then + local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) + end return self end @@ -233,7 +236,7 @@ end -- @param #string Message (optional) Message to be sent via chat. -- @return #NET self function NET:UnblockPlayer(Client,PlayerName,Message) - local name + local name = PlayerName if Client then name = Client:GetPlayerName() elseif PlayerName then @@ -299,6 +302,7 @@ end -- @param #string Name The player name whose ID to find -- @return #number PlayerID or nil function NET:GetPlayerIDByName(Name) + if not Name then return nil end local playerList = self:GetPlayerList() for i=1,#playerList do local playerName = net.get_name(i) @@ -526,8 +530,8 @@ end -- @return #boolean Success function NET:ForceSlot(Client,SideID,SlotID) local PlayerID = self:GetPlayerIDFromClient(Client) - if PlayerID then - return net.force_player_slot(tonumber(PlayerID), SideID, SlotID ) + if PlayerID and tonumber(PlayerID) ~= 1 then + return net.force_player_slot(tonumber(PlayerID), SideID, SlotID or '' ) else return false end From 7959f650d5e215607375ccd0eb01cbb25eb20ab7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Feb 2023 16:42:25 +0100 Subject: [PATCH 230/603] NET --- Moose Development/Moose/Wrapper/Net.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index f98f1f93f..a12d580ca 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -160,10 +160,16 @@ function NET:_EventHandler(EventData) local TNow = timer.getTime() if self.BlockedPilots[name] and TNow < self.BlockedPilots[name] then -- block pilot by name - self:ReturnToSpectators(data.IniUnit) + local PlayerID = self:GetPlayerIDByName(name) + if PlayerID and tonumber(PlayerID) ~= 1 then + local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) + end elseif self.BlockedUCIDs[ucid] and TNow < self.BlockedUCIDs[ucid] then -- block pilot by ucid - self:ReturnToSpectators(data.IniUnit) + local PlayerID = self:GetPlayerIDByName(name) + if PlayerID and tonumber(PlayerID) ~= 1 then + local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) + end else self.KnownPilots[name] = true self.BlockedPilots[name] = nil From 9b62710d64187554d0c30b5b9dfe308a9ee8d726 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 18 Feb 2023 11:04:37 +0100 Subject: [PATCH 231/603] #AICSAR * Named functions for the events --- Moose Development/Moose/Functional/AICSAR.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index 45b6f9a4e..a09d0e1bd 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -189,7 +189,7 @@ -- @field #AICSAR AICSAR = { ClassName = "AICSAR", - version = "0.1.11", + version = "0.1.12", lid = "", coalition = coalition.side.BLUE, template = "", @@ -393,8 +393,8 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) self:AddTransition("*", "HeloDown", "*") -- Helo dead self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. - self:HandleEvent(EVENTS.LandingAfterEjection) - self:HandleEvent(EVENTS.Ejection) + self:HandleEvent(EVENTS.LandingAfterEjection,self._EventHandler) + self:HandleEvent(EVENTS.Ejection,self._EventHandlerEject) self:__Start(math.random(2,5)) @@ -663,7 +663,7 @@ end -- @param #AICSAR self -- @param Core.Event#EVENTDATA EventData -- @return #AICSAR self -function AICSAR:OnEventEjection(EventData) +function AICSAR:_EventHandlerEject(EventData) local _event = EventData -- Core.Event#EVENTDATA if _event.IniPlayerName then self.PilotStore:Push(_event.IniPlayerName) @@ -676,7 +676,7 @@ end -- @param #AICSAR self -- @param Core.Event#EVENTDATA EventData -- @return #AICSAR self -function AICSAR:OnEventLandingAfterEjection(EventData) +function AICSAR:_EventHandler(EventData) self:T(self.lid .. "OnEventLandingAfterEjection ID=" .. EventData.id) -- autorescue on off? From 55a055a0472dba1bc0a84e564e8bd89699b972a8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 18 Feb 2023 15:03:07 +0100 Subject: [PATCH 232/603] #NET - further ado --- Moose Development/Moose/Wrapper/Net.lua | 32 ++++++++++++++++++------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index a12d580ca..300775eb9 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -31,7 +31,7 @@ do -- @field #NET NET = { ClassName = "NET", - Version = "0.0.5", + Version = "0.0.6", BlockTime = 600, BlockedPilots = {}, BlockedUCIDs = {}, @@ -153,45 +153,56 @@ function NET:_EventHandler(EventData) -- Get PlayerName local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName() local ucid = self:GetPlayerUCID(nil,name) - self:T(self.lid.."Event for: "..name.." | UCID: "..ucid) + local PlayerID = self:GetPlayerIDByName(name) or "none" + local TNow = timer.getTime() + self:I(self.lid.."Event for: "..name.." | UCID: "..ucid) + if self.BlockedPilots[name] then + self:I(self.lid.."Pilot "..name.." ID "..PlayerID.." Blocked for another "..self.BlockedPilots[name]-timer.getTime().." seconds!") + end + if self.BlockedUCIDs[ucid] then + self:I(self.lid.."Pilot "..name.." ID "..PlayerID.." Blocked for another "..self.BlockedUCIDs[ucid]-timer.getTime().." seconds!") + end -- Joining if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then + self:I(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid) -- Check for known pilots - local TNow = timer.getTime() if self.BlockedPilots[name] and TNow < self.BlockedPilots[name] then - -- block pilot by name - local PlayerID = self:GetPlayerIDByName(name) + -- block pilot by name if PlayerID and tonumber(PlayerID) ~= 1 then local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) end elseif self.BlockedUCIDs[ucid] and TNow < self.BlockedUCIDs[ucid] then -- block pilot by ucid - local PlayerID = self:GetPlayerIDByName(name) if PlayerID and tonumber(PlayerID) ~= 1 then local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) end else self.KnownPilots[name] = true - self.BlockedPilots[name] = nil - self.BlockedUCIDs[ucid] = nil + if (self.BlockedUCIDs[ucid] and TNow >= self.BlockedUCIDs[ucid]) or (self.BlockedPilots[name] and TNow >= self.BlockedPilots[name]) then + self.BlockedPilots[name] = nil + self.BlockedUCIDs[ucid] = nil + end self:__PlayerJoined(1,data.IniUnit,name) return self end end -- Leaving if data.id == EVENTS.PlayerLeaveUnit and self.KnownPilots[name] then + self:I(self.lid.."Pilot Leaving: "..name.." | UCID: "..ucid) self:__PlayerLeft(1,data.IniUnit,name) self.KnownPilots[name] = false return self end -- Ejected if data.id == EVENTS.Ejection and self.KnownPilots[name] then + self:I(self.lid.."Pilot Ejecting: "..name.." | UCID: "..ucid) self:__PlayerEjected(1,data.IniUnit,name) self.KnownPilots[name] = false return self end -- Dead, Crash, Suicide if (data.id == EVENTS.PilotDead or data.id == EVENTS.SelfKillPilot or data.id == EVENTS.Crash) and self.KnownPilots[name] then + self:I(self.lid.."Pilot Dead: "..name.." | UCID: "..ucid) self:__PlayerDied(1,data.IniUnit,name) self.KnownPilots[name] = false return self @@ -208,8 +219,9 @@ end -- @param #string Message (optional) Message to be sent via chat. -- @return #NET self function NET:BlockPlayer(Client,PlayerName,Seconds,Message) + self:I({PlayerName,Seconds,Message}) local name = PlayerName - if Client then + if Client and (not PlayerName) then name = Client:GetPlayerName() elseif PlayerName then name = PlayerName @@ -310,8 +322,10 @@ end function NET:GetPlayerIDByName(Name) if not Name then return nil end local playerList = self:GetPlayerList() + self:I({playerList}) for i=1,#playerList do local playerName = net.get_name(i) + self:I({playerName}) if playerName == Name then return playerList[i] end From 70386811e6a9788bd7e70bedc81c9789f86f44e6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 19 Feb 2023 12:31:57 +0100 Subject: [PATCH 233/603] AICSAR --- Moose Development/Moose/Functional/AICSAR.lua | 95 ++++++++++++++++++- Moose Development/Moose/Wrapper/Client.lua | 16 ++++ Moose Development/Moose/Wrapper/Net.lua | 31 +++--- 3 files changed, 126 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index a09d0e1bd..c9ec13298 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -55,6 +55,8 @@ -- @field Utilities.FiFo#FIFO PilotStore -- @field #number Altitude Default altitude setting for the helicopter FLIGHTGROUP 1500ft. -- @field #number Speed Default speed setting for the helicopter FLIGHTGROUP is 100kn. +-- @field #boolean UseEventEject In case Event LandingAfterEjection isn't working, use set this to true. +-- @field #number Delay In case of UseEventEject wait this long until we spawn a landed pilot. -- @extends Core.Fsm#FSM @@ -189,7 +191,7 @@ -- @field #AICSAR AICSAR = { ClassName = "AICSAR", - version = "0.1.12", + version = "0.1.14", lid = "", coalition = coalition.side.BLUE, template = "", @@ -232,6 +234,8 @@ AICSAR = { PilotStore = nil, Speed = 100, Altitude = 1500, + UseEventEject = false, + Delay = 100, } -- TODO Messages @@ -343,6 +347,8 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) self.farp = FARP self.farpzone = MASHZone self.playerset = SET_CLIENT:New():FilterActive(true):FilterCategories("helicopter"):FilterStart() + self.UseEventEject = false + self.Delay = 300 -- Radio self.SRS = nil @@ -394,7 +400,7 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:HandleEvent(EVENTS.LandingAfterEjection,self._EventHandler) - self:HandleEvent(EVENTS.Ejection,self._EventHandlerEject) + self:HandleEvent(EVENTS.Ejection,self._EjectEventHandler) self:__Start(math.random(2,5)) @@ -663,20 +669,99 @@ end -- @param #AICSAR self -- @param Core.Event#EVENTDATA EventData -- @return #AICSAR self -function AICSAR:_EventHandlerEject(EventData) +function AICSAR:_EjectEventHandler(EventData) local _event = EventData -- Core.Event#EVENTDATA if _event.IniPlayerName then self.PilotStore:Push(_event.IniPlayerName) self:T(self.lid.."Pilot Ejected: ".._event.IniPlayerName) + if self.UseEventEject then + -- get position and spawn in a template pilot + local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p) + local _country = _event.initiator:getCountry() + local _coalition = coalition.getCountryCoalition( _country ) + local data = UTILS.DeepCopy(EventData) + Unit.destroy(_event.initiator) -- shagrat remove static Pilot model + self:ScheduleOnce(self.Delay,self._DelayedSpawnPilot,self,_LandingPos,_coalition) + end end return self end +--- [Internal] Spawn a pilot +-- @param #AICSAR self +-- @param Core.Point#COORDINATE _LandingPos Landing Postion +-- @param #number _coalition Coalition side +-- @return #AICSAR self +function AICSAR:_DelayedSpawnPilot(_LandingPos,_coalition) + + local distancetofarp = _LandingPos:Get2DDistance(self.farp:GetCoordinate()) + -- Mayday Message + local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale) + local text = "" + local setting = {} + setting.MGRS_Accuracy = self.MGRS_Accuracy + local location = _LandingPos:ToStringMGRS(setting) + local msgtxt = Text..location.."!" + location = string.gsub(location,"MGRS ","") + location = string.gsub(location,"%s+","") + location = string.gsub(location,"([%a%d])","%1;") -- "0 5 1 " + location = string.gsub(location,"0","zero") + location = string.gsub(location,"9","niner") + location = "MGRS;"..location + if self.SRSGoogle then + location = string.format("%s",location) + end + text = Text .. location .. "!" + local ttstext = Text .. location .. "! Repeat! "..location + if _coalition == self.coalition then + if self.verbose then + MESSAGE:New(msgtxt,15,"AICSAR"):ToCoalition(self.coalition) + -- MESSAGE:New(msgtxt,15,"AICSAR"):ToLog() + end + if self.SRSRadio then + local sound = SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength) + sound:SetPlayWithSRS(true) + self.SRS:PlaySoundFile(sound,2) + elseif self.DCSRadio then + self:DCSRadioBroadcast(Soundfile,Soundlength,text) + elseif self.SRSTTSRadio then + if self.SRSPilotVoice then + self.SRSQ:NewTransmission(ttstext,nil,self.SRSPilot,nil,1) + else + self.SRSQ:NewTransmission(ttstext,nil,self.SRS,nil,1) + end + end + end + + -- further processing + if _coalition == self.coalition and distancetofarp <= self.maxdistance then + -- in reach + self:T(self.lid .. "Spawning new Pilot") + self.pilotindex = self.pilotindex + 1 + local newpilot = SPAWN:NewWithAlias(self.template,string.format("%s-AICSAR-%d",self.template, self.pilotindex)) + newpilot:InitDelayOff() + newpilot:OnSpawnGroup( + function (grp) + self.pilotqueue[self.pilotindex] = grp + end + ) + newpilot:SpawnFromCoordinate(_LandingPos) + + self:__PilotDown(2,_LandingPos,true) + elseif _coalition == self.coalition and distancetofarp > self.maxdistance then + -- out of reach, apologies, too far off + self:T(self.lid .. "Pilot out of reach") + self:__PilotDown(2,_LandingPos,false) + end + return self +end + --- [Internal] Catch the landing after ejection and spawn a pilot in situ. -- @param #AICSAR self -- @param Core.Event#EVENTDATA EventData +-- @param #boolean FromEject -- @return #AICSAR self -function AICSAR:_EventHandler(EventData) +function AICSAR:_EventHandler(EventData, FromEject) self:T(self.lid .. "OnEventLandingAfterEjection ID=" .. EventData.id) -- autorescue on off? @@ -686,6 +771,8 @@ function AICSAR:_EventHandler(EventData) end end + if self.UseEventEject and (not FromEject) then return self end + local _event = EventData -- Core.Event#EVENTDATA -- get position and spawn in a template pilot local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p) diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 12f2baa13..576e61db8 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -95,6 +95,22 @@ function CLIENT:Find(DCSUnit, Error) end end +--- Finds a CLIENT from the _DATABASE using the relevant player name. +-- @param #CLIENT self +-- @param #string Name Name of the player +-- @return #CLIENT or nil if not found +function CLIENT:FindByPlayerName(Name) + + local foundclient = nil + _DATABASE:ForEachClient( + function(client) + if client:GetPlayerName() == Name then + foundclient = client + end + end + ) + return foundclient +end --- Finds a CLIENT from the _DATABASE using the relevant Client Unit Name. -- As an optional parameter, a briefing text can be given also. diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index 300775eb9..4f28e8a6d 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -31,7 +31,7 @@ do -- @field #NET NET = { ClassName = "NET", - Version = "0.0.6", + Version = "0.0.7", BlockTime = 600, BlockedPilots = {}, BlockedUCIDs = {}, @@ -154,17 +154,18 @@ function NET:_EventHandler(EventData) local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName() local ucid = self:GetPlayerUCID(nil,name) local PlayerID = self:GetPlayerIDByName(name) or "none" + local PlayerSide, PlayerSlot = self:GetSlot(data.IniUnit) local TNow = timer.getTime() - self:I(self.lid.."Event for: "..name.." | UCID: "..ucid) + self:T(self.lid.."Event for: "..name.." | UCID: "..ucid) if self.BlockedPilots[name] then - self:I(self.lid.."Pilot "..name.." ID "..PlayerID.." Blocked for another "..self.BlockedPilots[name]-timer.getTime().." seconds!") + self:T(self.lid.."Pilot "..name.." ID "..PlayerID.." Blocked for another "..self.BlockedPilots[name]-timer.getTime().." seconds!") end if self.BlockedUCIDs[ucid] then - self:I(self.lid.."Pilot "..name.." ID "..PlayerID.." Blocked for another "..self.BlockedUCIDs[ucid]-timer.getTime().." seconds!") + self:T(self.lid.."Pilot "..name.." ID "..PlayerID.." Blocked for another "..self.BlockedUCIDs[ucid]-timer.getTime().." seconds!") end -- Joining if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then - self:I(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid) + self:T(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid) -- Check for known pilots if self.BlockedPilots[name] and TNow < self.BlockedPilots[name] then -- block pilot by name @@ -177,7 +178,13 @@ function NET:_EventHandler(EventData) local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) end else - self.KnownPilots[name] = true + self.KnownPilots[name] = { + name = name, + ucid = ucid, + id = PlayerID, + side = PlayerSide, + slot = PlayerSlot, + } if (self.BlockedUCIDs[ucid] and TNow >= self.BlockedUCIDs[ucid]) or (self.BlockedPilots[name] and TNow >= self.BlockedPilots[name]) then self.BlockedPilots[name] = nil self.BlockedUCIDs[ucid] = nil @@ -188,21 +195,21 @@ function NET:_EventHandler(EventData) end -- Leaving if data.id == EVENTS.PlayerLeaveUnit and self.KnownPilots[name] then - self:I(self.lid.."Pilot Leaving: "..name.." | UCID: "..ucid) + self:T(self.lid.."Pilot Leaving: "..name.." | UCID: "..ucid) self:__PlayerLeft(1,data.IniUnit,name) self.KnownPilots[name] = false return self end -- Ejected if data.id == EVENTS.Ejection and self.KnownPilots[name] then - self:I(self.lid.."Pilot Ejecting: "..name.." | UCID: "..ucid) + self:T(self.lid.."Pilot Ejecting: "..name.." | UCID: "..ucid) self:__PlayerEjected(1,data.IniUnit,name) self.KnownPilots[name] = false return self end -- Dead, Crash, Suicide if (data.id == EVENTS.PilotDead or data.id == EVENTS.SelfKillPilot or data.id == EVENTS.Crash) and self.KnownPilots[name] then - self:I(self.lid.."Pilot Dead: "..name.." | UCID: "..ucid) + self:T(self.lid.."Pilot Dead: "..name.." | UCID: "..ucid) self:__PlayerDied(1,data.IniUnit,name) self.KnownPilots[name] = false return self @@ -219,7 +226,7 @@ end -- @param #string Message (optional) Message to be sent via chat. -- @return #NET self function NET:BlockPlayer(Client,PlayerName,Seconds,Message) - self:I({PlayerName,Seconds,Message}) + self:T({PlayerName,Seconds,Message}) local name = PlayerName if Client and (not PlayerName) then name = Client:GetPlayerName() @@ -322,10 +329,10 @@ end function NET:GetPlayerIDByName(Name) if not Name then return nil end local playerList = self:GetPlayerList() - self:I({playerList}) + self:T({playerList}) for i=1,#playerList do local playerName = net.get_name(i) - self:I({playerName}) + self:T({playerName}) if playerName == Name then return playerList[i] end From 93c92d49f7aeb564f66b4db358f54d86f3d34eb9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 19 Feb 2023 17:15:35 +0100 Subject: [PATCH 234/603] NET --- Moose Development/Moose/Wrapper/Net.lua | 287 +++++++++++++++++++++--- 1 file changed, 252 insertions(+), 35 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index 4f28e8a6d..7fb03dd7a 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -5,7 +5,8 @@ -- === -- -- ### Author: **applevangelist** --- +-- # Last Update Feb 2023 +-- -- === -- -- @module Wrapper.Net @@ -23,18 +24,30 @@ do -- @field #string BlockMessage -- @field #string UnblockMessage -- @field #table BlockedUCIDs +-- @field #table BlockedSlots +-- @field #table BlockedSides -- @extends Core.Fsm#FSM +--- +-- @type NET.PlayerData +-- @field #string name +-- @field #string ucid +-- @field #number id +-- @field #number side +-- @field #number slot + --- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) -- with some added FSM functions and options to block/unblock players in MP environments. -- -- @field #NET NET = { ClassName = "NET", - Version = "0.0.7", + Version = "0.1.0", BlockTime = 600, BlockedPilots = {}, BlockedUCIDs = {}, + BlockedSides = {}, + BlockedSlots = {}, KnownPilots = {}, BlockMessage = nil, UnblockMessage = nil, @@ -54,26 +67,20 @@ function NET:New() self:SetBlockMessage() self:SetUnblockMessage() - self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler) - self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) - self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler) - self:HandleEvent(EVENTS.PilotDead,self._EventHandler) - self:HandleEvent(EVENTS.Ejection,self._EventHandler) - self:HandleEvent(EVENTS.Crash,self._EventHandler) - self:HandleEvent(EVENTS.SelfKillPilot,self._EventHandler) - -- Start State. - self:SetStartState("Running") + self:SetStartState("Stopped") -- Add FSM transitions. -- From State --> Event --> To State - self:AddTransition("*", "Run", "Running") -- Start FSM. + self:AddTransition("Stopped", "Run", "Running") -- Start FSM. self:AddTransition("*", "PlayerJoined", "*") self:AddTransition("*", "PlayerLeft", "*") self:AddTransition("*", "PlayerDied", "*") self:AddTransition("*", "PlayerEjected", "*") self:AddTransition("*", "PlayerBlocked", "*") self:AddTransition("*", "PlayerUnblocked", "*") + self:AddTransition("*", "Status", "*") + self:AddTransition("*", "Stop", "Stopped") self.lid = string.format("NET %s | ",self.Version) @@ -138,9 +145,45 @@ function NET:New() -- @param #string Name Name of unblocked Pilot. -- @return #NET self + self:Run() + return self end +--- [Internal] Check any blockers +-- @param #NET self +-- @param #string UCID +-- @param #string Name +-- @param #number PlayerID +-- @param #number PlayerSide +-- @param #string PlayerSlot +-- @return #boolean IsBlocked +function NET:IsAnyBlocked(UCID,Name,PlayerID,PlayerSide,PlayerSlot) + local blocked = false + local TNow = timer.getTime() + -- UCID + if UCID and self.BlockedUCIDs[UCID] and TNow < self.BlockedUCIDs[UCID] then + return true + end + -- ID/Name + if PlayerID and not Name then + Name = self:GetPlayerIDByName(Name) + end + -- Name + if Name and self.BlockedPilots[Name] and TNow < self.BlockedPilots[Name] then + return true + end + -- Side + if PlayerSide and self.BlockedSides[PlayerSide] and TNow < self.BlockedSides[PlayerSide] then + return true + end + -- Slot + if PlayerSlot and self.BlockedSlots[PlayerSlot] and TNow < self.BlockedSlots[PlayerSlot] then + return true + end + return blocked +end + --- [Internal] Event Handler -- @param #NET self -- @param Core.Event#EVENTDATA EventData @@ -150,33 +193,25 @@ function NET:_EventHandler(EventData) self:T2({Event = EventData.id}) local data = EventData -- Core.Event#EVENTDATA EventData if data.id and data.IniUnit and (data.IniPlayerName or data.IniUnit:GetPlayerName()) then - -- Get PlayerName + + -- Get Player Data local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName() local ucid = self:GetPlayerUCID(nil,name) local PlayerID = self:GetPlayerIDByName(name) or "none" local PlayerSide, PlayerSlot = self:GetSlot(data.IniUnit) local TNow = timer.getTime() + self:T(self.lid.."Event for: "..name.." | UCID: "..ucid) - if self.BlockedPilots[name] then - self:T(self.lid.."Pilot "..name.." ID "..PlayerID.." Blocked for another "..self.BlockedPilots[name]-timer.getTime().." seconds!") - end - if self.BlockedUCIDs[ucid] then - self:T(self.lid.."Pilot "..name.." ID "..PlayerID.." Blocked for another "..self.BlockedUCIDs[ucid]-timer.getTime().." seconds!") - end + -- Joining if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then self:T(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid) - -- Check for known pilots - if self.BlockedPilots[name] and TNow < self.BlockedPilots[name] then - -- block pilot by name - if PlayerID and tonumber(PlayerID) ~= 1 then - local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) - end - elseif self.BlockedUCIDs[ucid] and TNow < self.BlockedUCIDs[ucid] then - -- block pilot by ucid - if PlayerID and tonumber(PlayerID) ~= 1 then - local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) - end + -- Check for blockages + local blocked = self:IsAnyBlocked(ucid,name,PlayerID,PlayerSide,PlayerSlot) + + if blocked and PlayerID and tonumber(PlayerID) ~= 1 then + -- block pilot + local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) else self.KnownPilots[name] = { name = name, @@ -185,14 +220,11 @@ function NET:_EventHandler(EventData) side = PlayerSide, slot = PlayerSlot, } - if (self.BlockedUCIDs[ucid] and TNow >= self.BlockedUCIDs[ucid]) or (self.BlockedPilots[name] and TNow >= self.BlockedPilots[name]) then - self.BlockedPilots[name] = nil - self.BlockedUCIDs[ucid] = nil - end self:__PlayerJoined(1,data.IniUnit,name) return self end end + -- Leaving if data.id == EVENTS.PlayerLeaveUnit and self.KnownPilots[name] then self:T(self.lid.."Pilot Leaving: "..name.." | UCID: "..ucid) @@ -200,6 +232,7 @@ function NET:_EventHandler(EventData) self.KnownPilots[name] = false return self end + -- Ejected if data.id == EVENTS.Ejection and self.KnownPilots[name] then self:T(self.lid.."Pilot Ejecting: "..name.." | UCID: "..ucid) @@ -207,6 +240,7 @@ function NET:_EventHandler(EventData) self.KnownPilots[name] = false return self end + -- Dead, Crash, Suicide if (data.id == EVENTS.PilotDead or data.id == EVENTS.SelfKillPilot or data.id == EVENTS.Crash) and self.KnownPilots[name] then self:T(self.lid.."Pilot Dead: "..name.." | UCID: "..ucid) @@ -215,6 +249,7 @@ function NET:_EventHandler(EventData) return self end end + return self end @@ -254,6 +289,108 @@ function NET:BlockPlayer(Client,PlayerName,Seconds,Message) return self end +--- Block a SET_CLIENT of players +-- @param #NET self +-- @param Core.Set#SET_CLIENT PlayerSet The SET to block. +-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining. +-- @param #string Message (optional) Message to be sent via chat. +-- @return #NET self +function NET:BlockPlayerSet(PlayerSet,Seconds,Message) + self:T({PlayerSet.Set,Seconds,Message}) + local addon = Seconds or self.BlockTime + local message = Message or self.BlockMessage + for _,_client in pairs(PlayerSet.Set) do + local name = _client:GetPlayerName() + self:BlockPlayer(_client,name,addon,message) + end + return self +end + +--- Unblock a SET_CLIENT of players +-- @param #NET self +-- @param Core.Set#SET_CLIENT PlayerSet The SET to unblock. +-- @param #string Message (optional) Message to be sent via chat. +-- @return #NET self +function NET:UnblockPlayerSet(PlayerSet,Message) + self:T({PlayerSet.Set,Seconds,Message}) + local message = Message or self.UnblockMessage + for _,_client in pairs(PlayerSet.Set) do + local name = _client:GetPlayerName() + self:UnblockPlayer(_client,name,message) + end + return self +end + +--- Block a specific UCID of a player, does NOT automatically kick the player with the UCID if already joined. +-- @param #NET self +-- @param #string ucid +-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining. +-- @return #NET self +function NET:BlockUCID(ucid,Seconds) + self:T({ucid,Seconds}) + local addon = Seconds or self.BlockTime + self.BlockedUCIDs[ucid] = timer.getTime()+addon + return self +end + +--- Unblock a specific UCID of a player +-- @param #NET self +-- @param #string ucid +-- @return #NET self +function NET:UnblockUCID(ucid) + self:T({ucid}) + self.BlockedUCIDs[ucid] = nil + return self +end + +--- Block a specific coalition side, does NOT automatically kick all players of that side or kick out joined players +-- @param #NET self +-- @param #number side The side to block - 1 : Red, 2 : Blue +-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining. +-- @return #NET self +function NET:BlockSide(Side,Seconds) + self:T({Side,Seconds}) + local addon = Seconds or self.BlockTime + if Side == 1 or Side == 2 then + self.BlockedSides[Side] = timer.getTime()+addon + end + return self +end + +--- Unblock a specific coalition side. Does NOT unblock specifically blocked playernames or UCIDs. +-- @param #number side The side to block - 1 : Red, 2 : Blue +-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining. +-- @return #NET self +function NET:UnblockSide(Side,Seconds) + self:T({Side,Seconds}) + local addon = Seconds or self.BlockTime + if Side == 1 or Side == 2 then + self.BlockedSides[Side] = nil + end + return self +end + +--- Block a specific player slot, does NOT automatically kick a player in that slot or kick out joined players +-- @param #NET self +-- @param #string slot The slot to block +-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining. +-- @return #NET self +function NET:BlockSlot(Slot,Seconds) + self:T({Slot,Seconds}) + local addon = Seconds or self.BlockTime + self.BlockedSlots[Slot] = timer.getTime()+addon + return self +end + +--- Unblock a specific slot. +-- @param #string slot The slot to block +-- @return #NET self +function NET:UnblockSlot(Slot) + self:T({Slot}) + self.BlockedSlots[Slot] = nil + return self +end + --- Unblock a player. -- @param #NET self -- @param Wrapper.Client#CLIENT Client CLIENT object @@ -521,7 +658,7 @@ function NET:GetPlayerStatistic(Client,StatisticID) end end ---- Return the name of a given client. Same a CLIENT:GetPlayerName(). +--- Return the name of a given client. Effectively the same as CLIENT:GetPlayerName(). -- @param #NET self -- @param Wrapper.Client#CLIENT Client The client -- @return #string Name or nil if not obtainable @@ -610,6 +747,86 @@ function NET:Log(Message) return self end +--- Get some data of pilots who have currently joined +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client Provide either the client object whose data to find **or** +-- @param #string Name The player name whose data to find +-- @return #table Table of #NET.PlayerData or nil if not found +function NET:GetKnownPilotData(Client,Name) + local name = Name + if Client and not Name then + name = Client:GetPlayerName() + end + if name then + return self.KnownPilots[name] + else + return nil + end +end + +--- Status - housekeeping +-- @param #NET self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #NET self +function NET:onafterStatus(From,Event,To) + self:I({From,Event,To}) + + local function HouseHold(tavolo) + local TNow = timer.getTime() + for _,entry in pairs (tavolo) do + if entry >= TNow then entry = nil end + end + end + + HouseHold(self.BlockedPilots) + HouseHold(self.BlockedSides) + HouseHold(self.BlockedSlots) + HouseHold(self.BlockedUCIDs) + + if self:Is("Running") then + self:__Status(-60) + end + + return self +end + +--- Stop the event functions +-- @param #NET self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #NET self +function NET:onafterRun(From,Event,To) + self:I({From,Event,To}) + self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) + self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler) + self:HandleEvent(EVENTS.PilotDead,self._EventHandler) + self:HandleEvent(EVENTS.Ejection,self._EventHandler) + self:HandleEvent(EVENTS.Crash,self._EventHandler) + self:HandleEvent(EVENTS.SelfKillPilot,self._EventHandler) + self:__Status(-30) +end + +--- Stop the event functions +-- @param #NET self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #NET self +function NET:onafterStop(From,Event,To) + self:T({From,Event,To}) + self:UnHandleEvent(EVENTS.PlayerEnterUnit) + self:UnHandleEvent(EVENTS.PlayerEnterAircraft) + self:UnHandleEvent(EVENTS.PlayerLeaveUnit) + self:UnHandleEvent(EVENTS.PilotDead) + self:UnHandleEvent(EVENTS.Ejection) + self:UnHandleEvent(EVENTS.Crash) + self:UnHandleEvent(EVENTS.SelfKillPilot) + return self +end ------------------------------------------------------------------------------- -- End of NET ------------------------------------------------------------------------------- From 0b692d045fcab8e8f330047e69e198d04515bf16 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 21 Feb 2023 11:38:24 +0100 Subject: [PATCH 235/603] #CTLD logic correction for loading --- Moose Development/Moose/Ops/CTLD.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index db2afa9c0..9524e67b0 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update Jan 2023 +-- Last Update Feb 2023 do @@ -1196,7 +1196,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.30" +CTLD.version="1.0.31" --- Instantiate a new CTLD. -- @param #CTLD self @@ -2609,7 +2609,7 @@ function CTLD:_LoadCratesNearby(Group, Unit) crateind = _crate:GetID() end else - if not _crate:HasMoved() and _crate:WasDropped() and _crate:GetID() > crateind then + if not _crate:HasMoved() and not _crate:WasDropped() and _crate:GetID() > crateind then crateind = _crate:GetID() end end From 3c2a4dcc708128d7688908fbc54f7cc7d82d5ee9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 23 Feb 2023 11:11:48 +0100 Subject: [PATCH 236/603] #AWACS - do not convert speed to IAS, as already done in AUFTRAG --- Moose Development/Moose/Ops/Awacs.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index e087ae1c4..0407a445d 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -499,7 +499,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.53", -- #string + version = "0.2.54", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -1062,7 +1062,8 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station local speed = 250 self.SpeedBase = speed - self.Speed = UTILS.KnotsToAltKIAS(speed,self.AwacsAngels*1000) + --self.Speed = UTILS.KnotsToAltKIAS(speed,self.AwacsAngels*1000) + self.Speed = speed self.Heading = 0 -- north self.Leg = 50 -- nm @@ -1843,7 +1844,8 @@ function AWACS:SetAwacsDetails(CallSign,CallSignNo,Angels,Speed,Heading,Leg) self.AwacsAngels = Angels or 25 local speed = Speed or 250 self.SpeedBase = speed - self.Speed = UTILS.KnotsToAltKIAS(speed,self.AwacsAngels*1000) + --self.Speed = UTILS.KnotsToAltKIAS(speed,self.AwacsAngels*1000) + self.Speed = speed self.Heading = Heading or 0 self.Leg = Leg or 25 return self From edf1e1a3e3a6405de661008d26bbc21423b32f0d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 28 Feb 2023 07:59:01 +0100 Subject: [PATCH 237/603] #SPAWN --- Moose Development/Moose/Core/Spawn.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 491bac853..dd37f4a64 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1146,7 +1146,8 @@ do -- Delay methods return self end - --- Turns the Delay On for the @{Wrapper.Group} when spawning. + --- Turns the Delay On for the @{Wrapper.Group} when spawning with @{SpawnScheduled}(). In effect then the 1st group will only be spawned + -- after the number of seconds given in SpawnScheduled as arguments, and not immediately. -- @param #SPAWN self -- @return #SPAWN The SPAWN object function SPAWN:InitDelayOn() @@ -1443,6 +1444,8 @@ end -- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups. -- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn. -- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval. +-- @param #boolen WithDelay Do not spawn the **first** group immediately, but delay the spawn as per the calculation below. +-- Effectively the same as @{InitDelayOn}(). -- @return #SPAWN self -- @usage -- -- NATO helicopters engaging in the battle field. @@ -1453,12 +1456,15 @@ end -- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750 -- -- Between these two values, a random amount of seconds will be chosen for each new spawn of the helicopters. -- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):SpawnScheduled( 600, 0.5 ) -function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation ) +function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation, WithDelay ) self:F( { SpawnTime, SpawnTimeVariation } ) - + + local SpawnTime = SpawnTime or 60 + local SpawnTimeVariation = SpawnTimeVariation or 0.5 + if SpawnTime ~= nil and SpawnTimeVariation ~= nil then local InitialDelay = 0 - if self.DelayOnOff == true then + if WithDelay or self.DelayOnOff == true then InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation ) end self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation ) From 125ecb44c5e97a08d99d12c275588d2af273007f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 5 Mar 2023 11:03:47 +0100 Subject: [PATCH 238/603] #GROUP * Callsign translation refactor --- Moose Development/Moose/Wrapper/Group.lua | 69 ++++++++++++----------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 99125e66a..5d1c895d4 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2785,11 +2785,16 @@ end -- @param #GROUP self -- @param #boolean ShortCallsign Return a shortened customized callsign, i.e. "Ghostrider 9" and not "Ghostrider 9 1" -- @param #boolean Keepnumber (Player only) Return customized callsign, incl optional numbers at the end, e.g. "Aerial 1-1#Ghostrider 109" results in "Ghostrider 109", if you want to e.g. use historical US Navy Callsigns --- @param #table CallsignTranslations Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized +-- @param #table CallsignTranslations Table to translate between DCS standard callsigns and bespoke ones. Overrides personal/parsed callsigns if set -- callsigns from playername or group name. -- @return #string Callsign -- @usage --- -- Set Custom CAP Flight Callsigns for use with TTS +-- -- suppose there are three groups with one (client) unit each: +-- -- Slot 1 -- with mission editor callsign Enfield-1 +-- -- Slot 2 # Apollo 403 -- with mission editor callsign Enfield-2 +-- -- Slot 3 | Apollo -- with mission editor callsign Enfield-3 +-- -- Slot 4 | Apollo -- with mission editor callsign Devil-4 +-- -- and suppose these Custom CAP Flight Callsigns for use with TTS are set -- mygroup:GetCustomCallSign(true,false,{ -- Devil = 'Bengal', -- Snake = 'Winder', @@ -2797,12 +2802,12 @@ end -- Enfield = 'Victory', -- Uzi = 'Evil Eye' -- }) --- --- results in this outcome if the group has Callsign "Enfield 9 1" on the 1st #UNIT of the group: --- --- 'Victory 9' --- --- +-- -- then GetCustomCallsign will return +-- -- Enfield-1 for Slot 1 +-- -- Apollo for Slot 2 or Apollo 403 if Keepnumber is set +-- -- Apollo for Slot 3 +-- -- Bengal-4 for Slot 4 + function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) --self:I("GetCustomCallSign") @@ -2817,7 +2822,11 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9 local callnumberminor = string.char(string.byte(callnumber,2)) -- 1 local personalized = false - if IsPlayer and string.find(groupname,"#") then + + -- prioritize bespoke callsigns over parsing, prefer parsing over default callsigns + if CallsignTranslations and CallsignTranslations[callsignroot] then + callsignroot = CallsignTranslations[callsignroot] + elseif IsPlayer and string.find(groupname,"#") then -- personalized flight name in group naming if Keepnumber then shortcallsign = string.match(groupname,"#(.+)") or "Ghost 111" -- Ghostrider 219 @@ -2830,32 +2839,28 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) shortcallsign = string.match(self:GetPlayerName(),"|%s*([%a]+)") or string.match(self:GetPlayerName(),"|%s*([%d]+)") or "Ghost" -- Ghostrider personalized = true end - - if (not personalized) and CallsignTranslations and CallsignTranslations[callsignroot] then - callsignroot = CallsignTranslations[callsignroot] + + if personalized then + -- player personalized callsign + -- remove trailing/leading spaces + shortcallsign=string.gsub(shortcallsign,"^%s*","") + shortcallsign=string.gsub(shortcallsign,"%s*$","") + if Keepnumber then + return shortcallsign -- Ghostrider 219 + elseif ShortCallsign then + callsign = shortcallsign.." "..callnumbermajor -- Ghostrider 9 + else + callsign = shortcallsign.." "..callnumbermajor.." "..callnumberminor -- Ghostrider 9 1 + end + return callsign end - - if personalized then - -- player personalized callsign - -- remove trailing/leading spaces - shortcallsign=string.gsub(shortcallsign,"^%s*","") - shortcallsign=string.gsub(shortcallsign,"%s*$","") - if Keepnumber then - return shortcallsign -- Ghostrider 219 - elseif ShortCallsign then - callsign = shortcallsign.." "..callnumbermajor -- Ghostrider 9 + + -- AI or not personalized + if ShortCallsign then + callsign = callsignroot.." "..callnumbermajor -- Uzi/Victory 9 else - callsign = shortcallsign.." "..callnumbermajor.." "..callnumberminor -- Ghostrider 9 1 + callsign = callsignroot.." "..callnumbermajor.." "..callnumberminor -- Uzi/Victory 9 1 end - return callsign - end - - -- AI or not personalized - if ShortCallsign then - callsign = callsignroot.." "..callnumbermajor -- Uzi/Victory 9 - else - callsign = callsignroot.." "..callnumbermajor.." "..callnumberminor -- Uzi/Victory 9 1 - end --self:I("Generated Callsign = " .. callsign) end From 7409afb11f4cc071ea2ed9f29a90ebbb62ef6001 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 7 Mar 2023 10:56:08 +0100 Subject: [PATCH 239/603] UTILS.PlotRacetrack --- Moose Development/Moose/Utilities/Utils.lua | 42 +++++++++++++++++++++ Moose Development/Moose/Wrapper/Group.lua | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 73cddf307..b2098037e 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -2846,3 +2846,45 @@ function UTILS.IsAnyInTable(Table, Objects, Key) return false end + +--- Helper function to plot a racetrack on the F10 Map - curtesy of Buur. +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Altitude Altitude in feet +-- @param #number Speed Speed in knots +-- @param #number Heading Heading in degrees +-- @param #number Leg Leg in NM +-- @param #number Coalition Coalition side, e.g. coaltion.side.RED or coaltion.side.BLUE +-- @param #table Color Color of the line in RGB, e.g. {1,0,0} for red +-- @param #number Alpha Transparency factor, between 0.1 and 1 +-- @param #number LineType Line type to be used, line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. +-- @param #boolean ReadOnly +function UTILS.PlotRacetrack(Coordinate, Altitude, Speed, Heading, Leg, Coalition, Color, Alpha, LineType, ReadOnly) + local fix_coordinate = Coordinate + local altitude = Altitude + local speed = Speed or 350 + local heading = Heading or 270 + local leg_distance = Leg or 10 + + local coalition = Coalition or -1 + local color = Color or {1,0,0} + local alpha = Alpha or 1 + local lineType = LineType or 1 + + + speed = UTILS.IasToTas(speed, UTILS.FeetToMeters(altitude), oatcorr) + + local turn_radius = 0.0211 * speed -3.01 + + local point_two = fix_coordinate:Translate(UTILS.NMToMeters(leg_distance), heading, true, false) + local point_three = point_two:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false) + local point_four = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false) + local circle_center_fix_four = point_two:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false) + local circle_center_two_three = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false) + + + fix_coordinate:LineToAll(point_two, coalition, color, alpha, lineType) + point_four:LineToAll(point_three, coalition, color, alpha, lineType) + circle_center_fix_four:CircleToAll(UTILS.NMToMeters(turn_radius), coalition, color, alpha, nil, 0, lineType)--, ReadOnly, Text) + circle_center_two_three:CircleToAll(UTILS.NMToMeters(turn_radius), coalition, color, alpha, nil, 0, lineType)--, ReadOnly, Text) + +end \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 5d1c895d4..60561e603 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1,4 +1,4 @@ ------ **Wrapper** - GROUP wraps the DCS Class Group objects. +--- **Wrapper** - GROUP wraps the DCS Class Group objects. -- -- === -- From 62bca72c6ddfd81ce82c136ad4e196c89e245bda Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 9 Mar 2023 08:49:24 +0100 Subject: [PATCH 240/603] #GROUP * Fix for GetMaxHeight() --- Moose Development/Moose/Utilities/Utils.lua | 35 ++++++++++++++++++--- Moose Development/Moose/Wrapper/Group.lua | 4 +-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index b2098037e..b2daf3229 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -667,7 +667,10 @@ function UTILS.Round( num, idp ) return math.floor( num * mult + 0.5 ) / mult end --- porting in Slmod's dostring +--- Porting in Slmod's dostring - execute a string as LUA code with error handling. +-- @param #string s The code as string to be executed +-- @return #boolean success If true, code was successfully executed, else false +-- @return #string Outcome Code outcome if successful or error string if not successful function UTILS.DoString( s ) local f, err = loadstring( s ) if f then @@ -677,7 +680,15 @@ function UTILS.DoString( s ) end end --- Here is a customized version of pairs, which I called spairs because it iterates over the table in a sorted order. +--- Here is a customized version of pairs, which I called spairs because it iterates over the table in a sorted order. +-- @param #table t The table +-- @param #string order (Optional) The sorting function +-- @return #string key The index key +-- @return #string value The value at the indexed key +-- @usage +-- for key,value in UTILS.spairs(mytable) do +-- -- your code here +-- end function UTILS.spairs( t, order ) -- collect the keys local keys = {} @@ -702,7 +713,16 @@ function UTILS.spairs( t, order ) end --- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first. +--- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first. +-- @param #table t The table +-- @param #string getkey The function to determine the keys for sorting +-- @param #string order (Optional) The sorting function itself +-- @return #string key The index key +-- @return #string value The value at the indexed key +-- @usage +-- for key,value in UTILS.kpairs(mytable, getkeyfunc) do +-- -- your code here +-- end function UTILS.kpairs( t, getkey, order ) -- collect the keys local keys = {} @@ -727,7 +747,14 @@ function UTILS.kpairs( t, getkey, order ) end end --- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order. +--- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order. +-- @param #table t The table +-- @return #string key The index key +-- @return #string value The value at the indexed key +-- @usage +-- for key,value in UTILS.rpairs(mytable) do +-- -- your code here +-- end function UTILS.rpairs( t ) -- collect the keys diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 60561e603..fdc6c8f1e 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1653,7 +1653,7 @@ function GROUP:GetMinHeight() return nil end ---- Returns the current maximum height of the group. +--- Returns the current maximum height of the group, i.e. the highest unit height of that group. -- Each unit within the group gets evaluated, and the maximum height (= the unit which is the highest elevated) is returned. -- @param #GROUP self -- @return #number Maximum height found. @@ -1668,7 +1668,7 @@ function GROUP:GetMaxHeight() for Index, UnitData in pairs( DCSGroup:getUnits() ) do local UnitData = UnitData -- DCS#Unit - local UnitHeight = UnitData:getPoint() + local UnitHeight = UnitData:getPoint().p.y -- Height -- found by @Heavydrinker if UnitHeight > GroupHeightMax then GroupHeightMax = UnitHeight From 9d3e0690908b805852f526e1c4cf4d4d37d75baa Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 10 Mar 2023 10:07:58 +0100 Subject: [PATCH 241/603] #CTLD * Added/completed add/get/set stock functions --- Moose Development/Moose/Ops/CTLD.lua | 149 ++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 9524e67b0..0068d3045 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update Feb 2023 +-- Last Update Mar 2023 do @@ -288,7 +288,8 @@ CTLD_ENGINEERING = { end -do +do + ------------------------------------------------------ --- **CTLD_CARGO** class, extends Core.Base#BASE -- @type CTLD_CARGO @@ -494,7 +495,7 @@ CTLD_CARGO = { --- Add Stock. -- @param #CTLD_CARGO self - -- @param #number Number to add, one if nil. + -- @param #number Number to add, none if nil. -- @return #CTLD_CARGO self function CTLD_CARGO:AddStock(Number) if self.Stock then -- Stock nil? @@ -506,7 +507,7 @@ CTLD_CARGO = { --- Remove Stock. -- @param #CTLD_CARGO self - -- @param #number Number to reduce, one if nil. + -- @param #number Number to reduce, none if nil. -- @return #CTLD_CARGO self function CTLD_CARGO:RemoveStock(Number) if self.Stock then -- Stock nil? @@ -517,6 +518,15 @@ CTLD_CARGO = { return self end + --- Set Stock. + -- @param #CTLD_CARGO self + -- @param #number Number to set, nil means unlimited. + -- @return #CTLD_CARGO self + function CTLD_CARGO:SetStock(Number) + self.Stock = Number + return self + end + --- Query crate type for REPAIR -- @param #CTLD_CARGO self -- @param #boolean @@ -1196,7 +1206,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.31" +CTLD.version="1.0.32" --- Instantiate a new CTLD. -- @param #CTLD self @@ -4478,6 +4488,7 @@ end _troop:AddStock(number) end end + return self end --- User - function to add stock of a certain crates type @@ -4495,6 +4506,115 @@ end _troop:AddStock(number) end end + return self + end + + --- User - function to add stock of a certain crates type + -- @param #CTLD self + -- @param #string Name Name as defined in the generic cargo. + -- @param #number Number Number of units/groups to add. + -- @return #CTLD self + function CTLD:AddStockStatics(Name, Number) + local name = Name or "none" + local number = Number or 1 + -- find right generic type + local gentroops = self.Cargo_Statics + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO + if _troop.Name == name then + _troop:AddStock(number) + end + end + return self + end + + --- User - function to set the stock of a certain crates type + -- @param #CTLD self + -- @param #string Name Name as defined in the generic cargo. + -- @param #number Number Number of units/groups to be available. Nil equals unlimited + -- @return #CTLD self + function CTLD:SetStockCrates(Name, Number) + local name = Name or "none" + local number = Number + -- find right generic type + local gentroops = self.Cargo_Crates + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO + if _troop.Name == name then + _troop:SetStock(number) + end + end + return self + end + + --- User - function to set the stock of a certain troops type + -- @param #CTLD self + -- @param #string Name Name as defined in the generic cargo. + -- @param #number Number Number of units/groups to be available. Nil equals unlimited + -- @return #CTLD self + function CTLD:SetStockTroops(Name, Number) + local name = Name or "none" + local number = Number + -- find right generic type + local gentroops = self.Cargo_Troops + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO + if _troop.Name == name then + _troop:SetStock(number) + end + end + return self + end + + --- User - function to set the stock of a certain statics type + -- @param #CTLD self + -- @param #string Name Name as defined in the generic cargo. + -- @param #number Number Number of units/groups to be available. Nil equals unlimited + -- @return #CTLD self + function CTLD:SetStockStatics(Name, Number) + local name = Name or "none" + local number = Number + -- find right generic type + local gentroops = self.Cargo_Statics + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO + if _troop.Name == name then + _troop:SetStock(number) + end + end + return self + end + + --- User - function to get a table of crates in stock + -- @param #CTLD self + -- @return #table Table Table of Stock, indexed by cargo type name + function CTLD:GetStockCrates() + local Stock = {} + local gentroops = self.Cargo_Crates + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO + table.insert(Stock,_troop.Name,_troop.Stock or -1) + end + return Stock + end + + --- User - function to get a table of troops in stock + -- @param #CTLD self + -- @return #table Table Table of Stock, indexed by cargo type name + function CTLD:GetStockTroops() + local Stock = {} + local gentroops = self.Cargo_Troops + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO + table.insert(Stock,_troop.Name,_troop.Stock or -1) + end + return Stock + end + + --- User - function to get a table of statics cargo in stock + -- @param #CTLD self + -- @return #table Table Table of Stock, indexed by cargo type name + function CTLD:GetStockStatics() + local Stock = {} + local gentroops = self.Cargo_Statics + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO + table.insert(Stock,_troop.Name,_troop.Stock or -1) + end + return Stock end --- User - function to remove stock of a certain troops type @@ -4512,6 +4632,7 @@ end _troop:RemoveStock(number) end end + return self end --- User - function to remove stock of a certain crates type @@ -4532,6 +4653,24 @@ end return self end + --- User - function to remove stock of a certain statics type + -- @param #CTLD self + -- @param #string Name Name as defined in the generic cargo. + -- @param #number Number Number of units/groups to add. + -- @return #CTLD self + function CTLD:RemoveStockStatics(Name, Number) + local name = Name or "none" + local number = Number or 1 + -- find right generic type + local gentroops = self.Cargo_Statics + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO + if _troop.Name == name then + _troop:RemoveStock(number) + end + end + return self + end + --- (Internal) Check on engineering teams -- @param #CTLD self -- @return #CTLD self From 0315530f2c4e252ff8993c5a769ffcdde04489ab Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 Mar 2023 08:44:31 +0100 Subject: [PATCH 242/603] #AIRBASE * Added/changed: ["Hipico_Flying_Club"] = "Hipico Flying Club", ["Aeropuerto_de_Gobernador_Gregores"] = "Aeropuerto de Gobernador Gregores", ["Aerodromo_O_Higgins"] = "Aerodromo O'Higgins", ["Cullen_Airport"] = "Cullen Airport", ["Gull_Point"] = "Gull Point", --- Moose Development/Moose/Wrapper/Airbase.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 6c283d002..5e4a2d0e3 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -540,8 +540,12 @@ AIRBASE.SouthAtlantic={ ["Rio_Chico"] = "Rio Chico", ["Franco_Bianco"] = "Franco Bianco", ["Goose_Green"] = "Goose Green", - ["Hipico"] = "Hipico", + ["Hipico_Flying_Club"] = "Hipico Flying Club", ["CaletaTortel"] = "CaletaTortel", + ["Aeropuerto_de_Gobernador_Gregores"] = "Aeropuerto de Gobernador Gregores", + ["Aerodromo_O_Higgins"] = "Aerodromo O'Higgins", + ["Cullen_Airport"] = "Cullen Airport", + ["Gull_Point"] = "Gull Point", } --- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". From fd562a1d9ec0c113661ea3c98897be759cb35d34 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 23 Mar 2023 09:22:13 +0100 Subject: [PATCH 243/603] Typo fix --- Moose Development/Moose/Ops/RecoveryTanker.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/RecoveryTanker.lua b/Moose Development/Moose/Ops/RecoveryTanker.lua index be56c16e4..0489ccb3d 100644 --- a/Moose Development/Moose/Ops/RecoveryTanker.lua +++ b/Moose Development/Moose/Ops/RecoveryTanker.lua @@ -198,7 +198,7 @@ -- The first parameter *callsignname* defines the name (1=Texaco, 2=Arco, 3=Shell). The second (optional) parameter specifies the first number and has to be between 1-9. -- Also see [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns) and [DCS_command_setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign). -- --- TexacoStennis:SetCAllsign(CALLSIGN.Tanker.Arco) +-- TexacoStennis:SetCallsign(CALLSIGN.Tanker.Arco) -- -- For convenience, MOOSE has a CALLSIGN enumerator introduced. -- From 08c72df1e5cfb25ec34c3a45cb937513dd886abb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 28 Mar 2023 11:02:42 +0200 Subject: [PATCH 244/603] SET_STATIC - Added GetClosestStatic() --- Moose Development/Moose/Core/Set.lua | 69 ++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 3920315a0..7382ffe09 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1900,24 +1900,6 @@ do -- SET_GROUP return MGroupInclude end - --- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit. - -- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level. - -- @param #SET_GROUP self - -- @usage - -- -- Set the default cargo bay weight limits of the carrier units. - -- local MySetGroup = SET_GROUP:New() - -- MySetGroup:SetCargoBayWeightLimit() - function SET_GROUP:SetCargoBayWeightLimit() - local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - for UnitName, UnitData in pairs( GroupData:GetUnits() ) do - -- local UnitData = UnitData -- Wrapper.Unit#UNIT - UnitData:SetCargoBayWeightLimit() - end - end - end - - --- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search. -- @param #SET_GROUP self -- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined. @@ -1952,6 +1934,23 @@ do -- SET_GROUP return gmin, dmin end + --- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit. + -- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level. + -- @param #SET_GROUP self + -- @usage + -- -- Set the default cargo bay weight limits of the carrier units. + -- local MySetGroup = SET_GROUP:New() + -- MySetGroup:SetCargoBayWeightLimit() + function SET_GROUP:SetCargoBayWeightLimit() + local Set = self:GetSet() + for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP + for UnitName, UnitData in pairs( GroupData:GetUnits() ) do + -- local UnitData = UnitData -- Wrapper.Unit#UNIT + UnitData:SetCargoBayWeightLimit() + end + end + end + end do -- SET_UNIT @@ -3794,6 +3793,40 @@ do -- SET_STATIC return TypeReport:Text( Delimiter ) end + --- Get the closest static of the set with respect to a given reference coordinate. Optionally, only statics of given coalitions are considered in the search. + -- @param #SET_STATIC self + -- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest static is determined. + -- @return Wrapper.Static#STATIC The closest static (if any). + -- @return #number Distance in meters to the closest static. + function SET_STATIC:GetClosestStatic(Coordinate, Coalitions) + + local Set = self:GetSet() + + local dmin=math.huge + local gmin=nil + + for GroupID, GroupData in pairs( Set ) do -- For each STATIC in SET_STATIC + local group=GroupData --Wrapper.Static#STATIC + + if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then + + local coord=group:GetCoord() + + -- Distance between ref. coordinate and group coordinate. + local d=UTILS.VecDist3D(Coordinate, coord) + + if d Date: Thu, 30 Mar 2023 09:23:51 +0200 Subject: [PATCH 245/603] Small fix for SET_UNIT if used in capture zone coalition with a polygon zone --- Moose Development/Moose/Core/Set.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 7382ffe09..091276242 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2113,10 +2113,12 @@ do -- SET_UNIT self:F2( Unit:GetName() ) self:Add( Unit:GetName(), Unit ) - - -- Set the default cargo bay limit each time a new unit is added to the set. - Unit:SetCargoBayWeightLimit() - + + if Unit:IsInstanceOf("UNIT") then + -- Set the default cargo bay limit each time a new unit is added to the set. + Unit:SetCargoBayWeightLimit() + end + return self end From 42d5421cd864f466393504d33460f680cb5cd501 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 30 Mar 2023 12:22:30 +0200 Subject: [PATCH 246/603] Added Init Methods to set Unit Positions on Spawn --- Moose Development/Moose/Core/Point.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 79 +++++++++++++++++++-- Moose Development/Moose/Wrapper/Scenery.lua | 5 +- 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 18254b033..6ea45c8ba 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -540,7 +540,7 @@ do -- COORDINATE local gotscenery=false local function EvaluateZone(ZoneObject) - BASE:T({ZoneObject}) + if ZoneObject then -- Get category of scanned object. diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 6f3b1c873..6b5e9bb55 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -772,10 +772,8 @@ end -- @usage -- -- -- NATO helicopters engaging in the battle field. --- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP). --- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter. --- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeRoute( 2, 2, 2000 ) +-- -- UNIT positions of this group will be randomized around the base unit #1 in a circle of 50 to 500 meters. +-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeUnits( true, 500, 50 ) -- function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius ) self:F( { self.SpawnTemplatePrefix, RandomizeUnits, OuterRadius, InnerRadius } ) @@ -791,6 +789,46 @@ function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius ) return self end +--- Spawn the UNITs of this group with individual relative positions to unit #1 and individual headings. +-- @param #SPAWN self +-- @param #table Positions Table of positions, needs to one entry per unit in the group(!). The table contains one table each for each unit, with x,y, and optionally z +-- relative positions, and optionally an individual heading. +-- @return #SPAWN +-- @usage +-- +-- -- NATO helicopter group of three units engaging in the battle field. +-- local Positions = { [1] = {x = 0, y = 0, heading = 0}, [2] = {x = 50, y = 50, heading = 90}, [3] = {x = -50, y = 50, heading = 180} } +-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitSetUnitRelativePositions(Positions) +-- +function SPAWN:InitSetUnitRelativePositions(Positions) + self:F({self.SpawnTemplatePrefix, Positions}) + + self.SpawnUnitsWithRelativePositions = true + self.UnitsRelativePositions = Positions + + return self +end + +--- Spawn the UNITs of this group with individual absolute positions and individual headings. +-- @param #SPAWN self +-- @param #table Positions Table of positions, needs to one entry per unit in the group(!). The table contains one table each for each unit, with x,y, and optionally z +-- absolute positions, and optionally an individual heading. +-- @return #SPAWN +-- @usage +-- +-- -- NATO helicopter group of three units engaging in the battle field. +-- local Positions = { [1] = {x = 0, y = 0, heading = 0}, [2] = {x = 50, y = 50, heading = 90}, [3] = {x = -50, y = 50, heading = 180} } +-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitSetUnitAbsolutePositions(Positions) +-- +function SPAWN:InitSetUnitAbsolutePositions(Positions) + self:F({self.SpawnTemplatePrefix, Positions}) + + self.SpawnUnitsWithAbsolutePositions = true + self.UnitsAbsolutePositions = Positions + + return self +end + --- This method is rather complicated to understand. But I'll try to explain. -- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor, -- but they will all follow the same Template route and have the same prefix name. @@ -1343,7 +1381,38 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth ) SpawnTemplate.units[UnitID].psi = -SpawnTemplate.units[UnitID].heading end end - + + -- Individual relative unit positions + heading + if self.SpawnUnitsWithRelativePositions and self.UnitsRelativePositions then + local BaseX = SpawnTemplate.units[1].x or 0 + local BaseY = SpawnTemplate.units[1].y or 0 + local BaseZ = SpawnTemplate.units[1].z or 0 + for UnitID = 1, #SpawnTemplate.units do + if self.UnitsRelativePositions[UnitID].heading then + SpawnTemplate.units[UnitID].heading = math.rad(self.UnitsRelativePositions[UnitID].heading or 0) + end + SpawnTemplate.units[UnitID].x = BaseX + (self.UnitsRelativePositions[UnitID].x or 0) + SpawnTemplate.units[UnitID].y = BaseY + (self.UnitsRelativePositions[UnitID].y or 0) + if self.UnitsRelativePositions[UnitID].z then + SpawnTemplate.units[UnitID].z = BaseZ + (self.UnitsRelativePositions[UnitID].z or 0) + end + end + end + + -- Individual asbolute unit positions + heading + if self.SpawnUnitsWithAbsolutePositions and self.UnitsAbsolutePositions then + for UnitID = 1, #SpawnTemplate.units do + if self.UnitsAbsolutePositions[UnitID].heading then + SpawnTemplate.units[UnitID].heading = math.rad(self.UnitsAbsolutePositions[UnitID].heading or 0) + end + SpawnTemplate.units[UnitID].x = self.UnitsAbsolutePositions[UnitID].x or 0 + SpawnTemplate.units[UnitID].y = self.UnitsAbsolutePositions[UnitID].y or 0 + if self.UnitsAbsolutePositions[UnitID].z then + SpawnTemplate.units[UnitID].z = self.UnitsAbsolutePositions[UnitID].z or 0 + end + end + end + -- Set livery. if self.SpawnInitLivery then for UnitID = 1, #SpawnTemplate.units do diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 8fe17ecfe..bc64dfd15 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -110,11 +110,13 @@ end --@param #number Radius (optional) Search radius around coordinate, defaults to 100 --@return #SCENERY Scenery Object or `nil` if it cannot be found function SCENERY:FindByName(Name, Coordinate, Radius) - + local radius = Radius or 100 local name = Name or "unknown" local scenery = nil + BASE:T({name, radius, Coordinate:GetVec2()}) + --- -- @param Core.Point#COORDINATE coordinate -- @param #number radius @@ -170,6 +172,7 @@ function SCENERY:FindByZoneName( ZoneName ) zone = ZONE:FindByName(ZoneName) end local _id = zone:GetProperty('OBJECT ID') + BASE:T("Object ID ".._id) if not _id then -- this zone has no object ID BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) From 7ae9fa3225ed9dbe6162e486ccdef519a0fadc86 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 3 Apr 2023 12:15:49 +0200 Subject: [PATCH 247/603] #NET * Fix for ucid can be nil --- Moose Development/Moose/Wrapper/Net.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index 7fb03dd7a..b9b587028 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -42,7 +42,7 @@ do -- @field #NET NET = { ClassName = "NET", - Version = "0.1.0", + Version = "0.1.1", BlockTime = 600, BlockedPilots = {}, BlockedUCIDs = {}, @@ -196,7 +196,7 @@ function NET:_EventHandler(EventData) -- Get Player Data local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName() - local ucid = self:GetPlayerUCID(nil,name) + local ucid = self:GetPlayerUCID(nil,name) or "none" local PlayerID = self:GetPlayerIDByName(name) or "none" local PlayerSide, PlayerSlot = self:GetSlot(data.IniUnit) local TNow = timer.getTime() From 17e49b6a2f1350223710f9a60f9437dc4e62d3b7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 4 Apr 2023 10:32:38 +0200 Subject: [PATCH 248/603] #DATABASE * Small fix for CLIENT:FindByName() --- Moose Development/Moose/Core/Database.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 251fc1804..b2c91dba2 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1640,10 +1640,10 @@ end -- @param #DATABASE self -- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter. -- @return #DATABASE self -function DATABASE:ForEachClient( IteratorFunction, ... ) +function DATABASE:ForEachClient( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self.CLIENTS ) + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.CLIENTS ) return self end @@ -1652,10 +1652,10 @@ end -- @param #DATABASE self -- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter. -- @return #DATABASE self -function DATABASE:ForEachCargo( IteratorFunction, ... ) +function DATABASE:ForEachCargo( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self.CARGOS ) + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.CARGOS ) return self end From a6af53d78ccc5196a3cf5978e693a336e077dae1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 13 Apr 2023 15:49:21 +0200 Subject: [PATCH 249/603] #CTLD * Allow loadbacks with preciser coordinates --- Moose Development/Moose/Ops/CTLD.lua | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 8da145b64..891afc964 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -987,6 +987,7 @@ do -- my_ctld.filename = "missionsave.csv" -- example filename -- my_ctld.filepath = "C:\\Users\\myname\\Saved Games\\DCS\Missions\\MyMission" -- example path -- my_ctld.eventoninject = true -- fire OnAfterCratesBuild and OnAfterTroopsDeployed events when loading (uses Inject functions) +-- my_ctld.useprecisecoordloads = true -- Instead if slightly varyiing the group position, try to maintain it as is -- -- Then use an initial load at the beginning of your mission: -- @@ -1219,7 +1220,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.32" +CTLD.version="1.0.33" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1312,6 +1313,7 @@ function CTLD:New(Coalition, Prefixes, Alias) self.droppedBeacons = {} self.droppedbeaconref = {} self.droppedbeacontimeout = 600 + self.useprecisecoordloads = true -- Cargo self.Cargo_Crates = {} @@ -4794,6 +4796,8 @@ end -- @param #CTLD self -- @param Core.Zone#ZONE Zone The zone where to drop the troops. -- @param Ops.CTLD#CTLD_CARGO Cargo The #CTLD_CARGO object to spawn. + -- @param #table Surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type! + -- @param #boolean PreciseLocation (Optional) Don't try to get a random position in the zone but use the dead center. Caution not to stack up stuff on another! -- @return #CTLD self -- @usage Use this function to pre-populate the field with Vehicles or FOB at a random coordinate in a zone: -- -- create a matching #CTLD_CARGO type @@ -4802,7 +4806,7 @@ end -- local dropzone = ZONE:New("InjectZone") -- Core.Zone#ZONE -- -- and go: -- my_ctld:InjectVehicles(dropzone,InjectVehicleType) - function CTLD:InjectVehicles(Zone,Cargo) + function CTLD:InjectVehicles(Zone,Cargo,Surfacetypes,PreciseLocation) self:T(self.lid.." InjectVehicles") local cargo = Cargo -- #CTLD_CARGO @@ -4834,7 +4838,10 @@ end local temptable = cargo:GetTemplates() or {} local factor = 1.5 local zone = Zone - local randomcoord = zone:GetRandomCoordinate(10,30*factor):GetVec2() + local randomcoord = zone:GetRandomCoordinate(10,30*factor,Surfacetypes):GetVec2() + if PreciseLocation then + randomcoord = zone:GetCoordinate():GetVec2() + end cargo:SetWasDropped(true) local canmove = false if type == CTLD_CARGO.Enum.VEHICLE then canmove = true end @@ -5463,10 +5470,10 @@ end local dropzone = ZONE_RADIUS:New("DropZone",vec2,20) if cargotype == CTLD_CARGO.Enum.VEHICLE or cargotype == CTLD_CARGO.Enum.FOB then local injectvehicle = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) - self:InjectVehicles(dropzone,injectvehicle) + self:InjectVehicles(dropzone,injectvehicle,self.surfacetypes,self.useprecisecoordloads) elseif cargotype == CTLD_CARGO.Enum.TROOPS or cargotype == CTLD_CARGO.Enum.ENGINEERS then local injecttroops = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) - self:InjectTroops(dropzone,injecttroops,self.surfacetypes) + self:InjectTroops(dropzone,injecttroops,self.surfacetypes,self.useprecisecoordloads) end elseif (type(groupname) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR then local cargotemplates = dataset[6] From d5eb97863bc109616ee5c5a8f38540a61cac3ad7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 16 Apr 2023 16:09:58 +0200 Subject: [PATCH 250/603] #CTLD * Update saving/loading to include structure --- Moose Development/Moose/Ops/CTLD.lua | 133 +++++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index de81e6604..394a22138 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1220,7 +1220,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.34" +CTLD.version="1.0.35" --- Instantiate a new CTLD. -- @param #CTLD self @@ -4731,6 +4731,7 @@ end -- @param Ops.CTLD#CTLD_CARGO Cargo The #CTLD_CARGO object to spawn. -- @param #table Surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type! -- @param #boolean PreciseLocation (Optional) Don't try to get a random position in the zone but use the dead center. Caution not to stack up stuff on another! + -- @param #string Structure (Optional) String object describing the current structure of the injected group; mainly for load/save to keep current state setup. -- @return #CTLD self -- @usage Use this function to pre-populate the field with Troops or Engineers at a random coordinate in a zone: -- -- create a matching #CTLD_CARGO type @@ -4739,7 +4740,7 @@ end -- local dropzone = ZONE:New("InjectZone") -- Core.Zone#ZONE -- -- and go: -- my_ctld:InjectTroops(dropzone,InjectTroopsType,{land.SurfaceType.LAND}) - function CTLD:InjectTroops(Zone,Cargo,Surfacetypes,PreciseLocation) + function CTLD:InjectTroops(Zone,Cargo,Surfacetypes,PreciseLocation,Structure) self:T(self.lid.." InjectTroops") local cargo = Cargo -- #CTLD_CARGO @@ -4757,6 +4758,49 @@ end return match end + local function Cruncher(group,typename,anzahl) + local units = group:GetUnits() + local reduced = 0 + for _,_unit in pairs (units) do + local typo = _unit:GetTypeName() + if typename == typo then + _unit:Destroy(false) + reduced = reduced + 1 + if reduced == anzahl then break end + end + end + end + + local function PostSpawn(args) + local group = args[1] + local structure = args[2] + if structure then + + local loadedstructure = {} + local strcset = UTILS.Split(structure,";") + for _,_data in pairs(strcset) do + local datasplit = UTILS.Split(_data,"==") + loadedstructure[datasplit[1]] = tonumber(datasplit[2]) + end + + local originalstructure = UTILS.GetCountPerTypeName(group) + + for _name,_number in pairs(originalstructure) do + local loadednumber = 0 + if loadedstructure[_name] then + loadednumber = loadedstructure[_name] + end + local reduce = false + if loadednumber < _number then reduce = true end + + if reduce then + Cruncher(group,_name,_number-loadednumber) + end + + end + end + end + if not IsTroopsMatch(cargo) then self.CargoCounter = self.CargoCounter + 1 cargo.ID = self.CargoCounter @@ -4793,6 +4837,11 @@ end local grpname = self.DroppedTroops[self.TroopCounter]:GetName() self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New(name, grpname) end + + if Structure then + BASE:ScheduleOnce(0.5,PostSpawn,{self.DroppedTroops[self.TroopCounter],Structure}) + end + if self.eventoninject then self:__TroopsDeployed(1,nil,nil,self.DroppedTroops[self.TroopCounter],type) end @@ -4806,6 +4855,7 @@ end -- @param Ops.CTLD#CTLD_CARGO Cargo The #CTLD_CARGO object to spawn. -- @param #table Surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type! -- @param #boolean PreciseLocation (Optional) Don't try to get a random position in the zone but use the dead center. Caution not to stack up stuff on another! + -- @param #string Structure (Optional) String object describing the current structure of the injected group; mainly for load/save to keep current state setup. -- @return #CTLD self -- @usage Use this function to pre-populate the field with Vehicles or FOB at a random coordinate in a zone: -- -- create a matching #CTLD_CARGO type @@ -4814,7 +4864,7 @@ end -- local dropzone = ZONE:New("InjectZone") -- Core.Zone#ZONE -- -- and go: -- my_ctld:InjectVehicles(dropzone,InjectVehicleType) - function CTLD:InjectVehicles(Zone,Cargo,Surfacetypes,PreciseLocation) + function CTLD:InjectVehicles(Zone,Cargo,Surfacetypes,PreciseLocation,Structure) self:T(self.lid.." InjectVehicles") local cargo = Cargo -- #CTLD_CARGO @@ -4832,6 +4882,49 @@ end return match end + local function Cruncher(group,typename,anzahl) + local units = group:GetUnits() + local reduced = 0 + for _,_unit in pairs (units) do + local typo = _unit:GetTypeName() + if typename == typo then + _unit:Destroy(false) + reduced = reduced + 1 + if reduced == anzahl then break end + end + end + end + + local function PostSpawn(args) + local group = args[1] + local structure = args[2] + if structure then + + local loadedstructure = {} + local strcset = UTILS.Split(structure,";") + for _,_data in pairs(strcset) do + local datasplit = UTILS.Split(_data,"==") + loadedstructure[datasplit[1]] = tonumber(datasplit[2]) + end + + local originalstructure = UTILS.GetCountPerTypeName(group) + + for _name,_number in pairs(originalstructure) do + local loadednumber = 0 + if loadedstructure[_name] then + loadednumber = loadedstructure[_name] + end + local reduce = false + if loadednumber < _number then reduce = true end + + if reduce then + Cruncher(group,_name,_number-loadednumber) + end + + end + end + end + if not IsVehicMatch(cargo) then self.CargoCounter = self.CargoCounter + 1 cargo.ID = self.CargoCounter @@ -4866,6 +4959,11 @@ end :InitDelayOff() :SpawnFromVec2(randomcoord) end + + if Structure then + BASE:ScheduleOnce(0.5,PostSpawn,{self.DroppedTroops[self.TroopCounter],Structure}) + end + if self.eventoninject then self:__CratesBuild(1,nil,nil,self.DroppedTroops[self.TroopCounter]) end @@ -5250,6 +5348,7 @@ end -- name matching a template in the table local match = false local cargo = nil + name = string.gsub(name,"-"," ") for _ind,_cargo in pairs (table) do local thiscargo = _cargo -- #CTLD_CARGO local template = thiscargo:GetTemplates() @@ -5257,6 +5356,7 @@ end template = { template } end for _,_name in pairs (template) do + _name = string.gsub(_name,"-"," ") if string.find(name,_name) and _cargo:GetType() ~= CTLD_CARGO.Enum.REPAIR then match = true cargo = thiscargo @@ -5269,18 +5369,21 @@ end --local data = "LoadedData = {\n" - local data = "Group,x,y,z,CargoName,CargoTemplates,CargoType,CratesNeeded,CrateMass\n" + local data = "Group,x,y,z,CargoName,CargoTemplates,CargoType,CratesNeeded,CrateMass,Structure\n" local n = 0 for _,_grp in pairs(grouptable) do local group = _grp -- Wrapper.Group#GROUP if group and group:IsAlive() then -- get template name local name = group:GetName() - local template = string.gsub(name,"-(.+)$","") + local template = name + if string.find(template,"#") then template = string.gsub(name,"#(%d+)$","") end + local template = string.gsub(name,"-(%d+)$","") + local match, cargo = FindCargoType(template,cgotable) if not match then match, cargo = FindCargoType(template,cgovehic) @@ -5293,6 +5396,11 @@ end local cgotype = cargo.CargoType local cgoneed = cargo.CratesNeeded local cgomass = cargo.PerCrateMass + local structure = UTILS.GetCountPerTypeName(group) + local strucdata = "" + for typen,anzahl in pairs (structure) do + strucdata = strucdata .. typen .. "=="..anzahl..";" + end if type(cgotemp) == "table" then local templates = "{" @@ -5304,8 +5412,8 @@ end end local location = group:GetVec3() - local txt = string.format("%s,%d,%d,%d,%s,%s,%s,%d,%d\n" - ,template,location.x,location.y,location.z,cgoname,cgotemp,cgotype,cgoneed,cgomass) + local txt = string.format("%s,%d,%d,%d,%s,%s,%s,%d,%d,%s\n" + ,template,location.x,location.y,location.z,cgoname,cgotemp,cgotype,cgoneed,cgomass,strucdata) data = data .. txt end end @@ -5460,7 +5568,7 @@ end for _id,_entry in pairs (loadeddata) do local dataset = UTILS.Split(_entry,",") - -- 1=Group,2=x,3=y,4=z,5=CargoName,6=CargoTemplates,7=CargoType,8=CratesNeeded,9=CrateMass,10=SubCategory + -- 1=Group,2=x,3=y,4=z,5=CargoName,6=CargoTemplates,7=CargoType,8=CratesNeeded,9=CrateMass,10=Structure local groupname = dataset[1] local vec2 = {} vec2.x = tonumber(dataset[2]) @@ -5474,14 +5582,19 @@ end cargotemplates = UTILS.Split(cargotemplates,";") local size = tonumber(dataset[8]) local mass = tonumber(dataset[9]) + local structure = nil + if dataset[10] then + structure = dataset[10] + structure = string.gsub(structure,",","") + end -- inject at Vec2 local dropzone = ZONE_RADIUS:New("DropZone",vec2,20) if cargotype == CTLD_CARGO.Enum.VEHICLE or cargotype == CTLD_CARGO.Enum.FOB then local injectvehicle = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) - self:InjectVehicles(dropzone,injectvehicle,self.surfacetypes,self.useprecisecoordloads) + self:InjectVehicles(dropzone,injectvehicle,self.surfacetypes,self.useprecisecoordloads,structure) elseif cargotype == CTLD_CARGO.Enum.TROOPS or cargotype == CTLD_CARGO.Enum.ENGINEERS then local injecttroops = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) - self:InjectTroops(dropzone,injecttroops,self.surfacetypes,self.useprecisecoordloads) + self:InjectTroops(dropzone,injecttroops,self.surfacetypes,self.useprecisecoordloads,structure) end elseif (type(groupname) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR then local cargotemplates = dataset[6] From 2470632e8423dd57aa3912421944edc374d52a1f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 18 Apr 2023 10:26:43 +0200 Subject: [PATCH 251/603] Fixes for getPlayerName errors --- .../Moose/Functional/PseudoATC.lua | 146 ++++-------------- Moose Development/Moose/Ops/Airboss.lua | 6 +- 2 files changed, 32 insertions(+), 120 deletions(-) diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 5ee714705..16c530cf3 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -26,9 +26,9 @@ -- -- === -- --- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** +-- ### Author: **funkyfranky** -- --- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) +-- ### Contributions: FlightControl, Applevangelist -- -- ==== -- @module Functional.PseudoATC @@ -44,7 +44,7 @@ -- @field #number mrefresh Interval in seconds after which the F10 menu is refreshed. E.g. by the closest airports. Default is 120 sec. -- @field #number talt Interval in seconds between reporting altitude until touchdown. Default 3 sec. -- @field #boolean chatty Display some messages on events like take-off and touchdown. --- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. +-- @field #boolean eventsmoose [Deprecated] If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. -- @field #boolean reportplayername If true, use playername not callsign on callouts -- @extends Core.Base#BASE @@ -100,13 +100,14 @@ PSEUDOATC.id="PseudoATC | " --- PSEUDOATC version. -- @field #number version -PSEUDOATC.version="0.9.5" +PSEUDOATC.version="0.10.5" ----------------------------------------------------------------------------------------------------------------------------------------- -- TODO list -- DONE: Add takeoff event. -- DONE: Add user functions. +-- DONE: Refactor to use Moose event handling only ----------------------------------------------------------------------------------------------------------------------------------------- @@ -131,23 +132,14 @@ function PSEUDOATC:Start() self:F() -- Debug info - self:E(PSEUDOATC.id.."Starting PseudoATC") + self:I(PSEUDOATC.id.."Starting PseudoATC") -- Handle events. - if self.eventsmoose then - self:T(PSEUDOATC.id.."Events are handled by MOOSE.") - self:HandleEvent(EVENTS.Birth, self._OnBirth) - self:HandleEvent(EVENTS.Land, self._PlayerLanded) - self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeOff) - self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft) - self:HandleEvent(EVENTS.Crash, self._PlayerLeft) - --self:HandleEvent(EVENTS.Ejection, self._PlayerLeft) - --self:HandleEvent(EVENTS.PilotDead, self._PlayerLeft) - else - self:T(PSEUDOATC.id.."Events are handled by DCS.") - -- Events are handled directly by DCS. - world.addEventHandler(self) - end + self:HandleEvent(EVENTS.Birth, self._OnBirth) + self:HandleEvent(EVENTS.Land, self._PlayerLanded) + self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeOff) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft) + self:HandleEvent(EVENTS.Crash, self._PlayerLeft) end @@ -199,7 +191,7 @@ function PSEUDOATC:SetMenuRefresh(interval) self.mrefresh=interval or 120 end ---- Enable/disable event handling by MOOSE or DCS. +--- [Deprecated] Enable/disable event handling by MOOSE or DCS. -- @param #PSEUDOATC self -- @param #boolean switch If true, events are handled by MOOSE (default). If false, events are handled directly by DCS. function PSEUDOATC:SetEventsMoose(switch) @@ -216,84 +208,6 @@ end ----------------------------------------------------------------------------------------------------------------------------------------- -- Event Handling ---- Event handler for suppressed groups. ---@param #PSEUDOATC self ---@param #table Event Event data table. Holds event.id, event.initiator and event.target etc. -function PSEUDOATC:onEvent(Event) - if Event == nil or Event.initiator == nil or Unit.getByName(Event.initiator:getName()) == nil then - return true - end - - local DCSiniunit = Event.initiator - local DCSplace = Event.place - local DCSsubplace = Event.subplace - - local EventData={} - local _playerunit=nil - local _playername=nil - - if Event.initiator then - EventData.IniUnitName = Event.initiator:getName() - EventData.IniDCSGroup = Event.initiator:getGroup() - EventData.IniGroupName = Event.initiator:getGroup():getName() - -- Get player unit and name. This returns nil,nil if the event was not fired by a player unit. And these are the only events we are interested in. - _playerunit, _playername = self:_GetPlayerUnitAndName(EventData.IniUnitName) - end - - if Event.place then - EventData.Place=Event.place - EventData.PlaceName=Event.place:getName() - end - if Event.subplace then - EventData.SubPlace=Event.subplace - EventData.SubPlaceName=Event.subplace:getName() - end - - -- Event info. - self:T3(PSEUDOATC.id..string.format("EVENT: Event in onEvent with ID = %s", tostring(Event.id))) - self:T3(PSEUDOATC.id..string.format("EVENT: Ini unit = %s" , tostring(EventData.IniUnitName))) - self:T3(PSEUDOATC.id..string.format("EVENT: Ini group = %s" , tostring(EventData.IniGroupName))) - self:T3(PSEUDOATC.id..string.format("EVENT: Ini player = %s" , tostring(_playername))) - self:T3(PSEUDOATC.id..string.format("EVENT: Place = %s" , tostring(EventData.PlaceName))) - self:T3(PSEUDOATC.id..string.format("EVENT: SubPlace = %s" , tostring(EventData.SubPlaceName))) - - -- Event birth. - if Event.id == world.event.S_EVENT_BIRTH and _playername then - self:_OnBirth(EventData) - end - - -- Event takeoff. - if Event.id == world.event.S_EVENT_TAKEOFF and _playername and EventData.Place then - self:_PlayerTakeOff(EventData) - end - - -- Event land. - if Event.id == world.event.S_EVENT_LAND and _playername and EventData.Place then - self:_PlayerLanded(EventData) - end - - -- Event player left unit - if Event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and _playername then - self:_PlayerLeft(EventData) - end - - -- Event crash ==> player left unit - if Event.id == world.event.S_EVENT_CRASH and _playername then - self:_PlayerLeft(EventData) - end - ---[[ - -- Event eject ==> player left unit - if Event.id == world.event.S_EVENT_EJECTION and _playername then - self:_PlayerLeft(EventData) - end - - -- Event pilot dead ==> player left unit - if Event.id == world.event.S_EVENT_PILOT_DEAD and _playername then - self:_PlayerLeft(EventData) - end -]] -end --- Function called my MOOSE event handler when a player enters a unit. -- @param #PSEUDOATC self @@ -303,7 +217,9 @@ function PSEUDOATC:_OnBirth(EventData) -- Get unit and player. local _unitName=EventData.IniUnitName - local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + --local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + local _unit = EventData.IniUnit + local _playername = EventData.IniPlayerName -- Check if a player entered. if _unit and _playername then @@ -320,7 +236,10 @@ function PSEUDOATC:_PlayerLeft(EventData) -- Get unit and player. local _unitName=EventData.IniUnitName - local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + --local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + + local _unit = EventData.IniUnit + local _playername = EventData.IniPlayerName -- Check if a player left. if _unit and _playername then @@ -335,18 +254,16 @@ function PSEUDOATC:_PlayerLanded(EventData) self:F({EventData=EventData}) -- Get unit, player and place. - local _unitName=EventData.IniUnitName - local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + local _unitName=EventData.IniUnitName + local _unit = EventData.IniUnit + local _playername = EventData.IniPlayerName + --local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) local _base=nil local _baseName=nil if EventData.place then _base=EventData.place _baseName=EventData.place:getName() end --- if EventData.subplace then --- local _subPlace=EventData.subplace --- local _subPlaceName=EventData.subplace:getName() --- end -- Call landed function. if _unit and _playername and _base then @@ -361,8 +278,10 @@ function PSEUDOATC:_PlayerTakeOff(EventData) self:F({EventData=EventData}) -- Get unit, player and place. - local _unitName=EventData.IniUnitName - local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) + local _unitName=EventData.IniUnitName + local _unit = EventData.IniUnit + local _playername = EventData.IniPlayerName + --local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) local _base=nil local _baseName=nil if EventData.place then @@ -450,9 +369,6 @@ function PSEUDOATC:PlayerLanded(unit, place) local group=unit:GetGroup() local GID=group:GetID() local UID=unit:GetDCSObject():getID() - --local PlayerName=self.group[GID].player[UID].playername - --local UnitName=self.group[GID].player[UID].unitname - --local GroupName=self.group[GID].player[UID].groupname local PlayerName = unit:GetPlayerName() or "Ghost" local UnitName = unit:GetName() or "Ghostplane" local GroupName = group:GetName() or "Ghostgroup" @@ -483,12 +399,6 @@ function PSEUDOATC:PlayerTakeOff(unit, place) -- Gather some information. local group=unit:GetGroup() - --local GID=group:GetID() - --local UID=unit:GetDCSObject():getID() - --local PlayerName=self.group[GID].player[UID].playername - --local CallSign=self.group[GID].player[UID].callsign - --local UnitName=self.group[GID].player[UID].unitname - --local GroupName=self.group[GID].player[UID].groupname local PlayerName = unit:GetPlayerName() or "Ghost" local UnitName = unit:GetName() or "Ghostplane" local GroupName = group:GetName() or "Ghostgroup" @@ -926,7 +836,7 @@ function PSEUDOATC:AltitudeTimeStart(GID, UID) self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", UID)) -- Start timer. Altitude is reported every ~3 seconds. - self.group[GID].player[UID].altimer, self.group[GID].player[UID].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, GID, UID, 0.1, true}, 1, 3) + self.group[GID].player[UID].altimer, self.group[GID].player[UID].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, GID, UID, 1, true}, 1, 3) end --- Stop/destroy DCS scheduler function for reporting altitude. diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 21b5caa61..1ac30fe8f 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -8102,7 +8102,7 @@ end -- @param Core.Event#EVENTDATA EventData function AIRBOSS:OnEventBirth( EventData ) self:F3( { eventbirth = EventData } ) - + -- Nil checks. if EventData == nil then self:E( self.lid .. "ERROR: EventData=nil in event BIRTH!" ) @@ -8114,7 +8114,9 @@ function AIRBOSS:OnEventBirth( EventData ) self:E( EventData ) return end - + + if EventData.IniObjectCategory ~= Object.Category.UNIT then return end + local _unitName = EventData.IniUnitName local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) From 65357943e78f7adf6606a8c8ed52b6a2524677df Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 20 Apr 2023 08:04:40 +0200 Subject: [PATCH 252/603] #SET * Active Zone Filtering --- Moose Development/Moose/Core/Set.lua | 168 +++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 091276242..a108b9e77 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -904,6 +904,8 @@ end do -- SET_GROUP --- @type SET_GROUP #SET_GROUP + -- @field Core.Timer#TIMER ZoneTimer + -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain: @@ -1343,6 +1345,25 @@ do -- SET_GROUP end return self end + + --- [Internal] Private function for use of continous zone filter + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:_ContinousZoneFilter() + + local Database = _DATABASE.GROUPS + + for ObjectName, Object in pairs( Database ) do + if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then + self:Add( ObjectName, Object ) + elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + self:Remove(ObjectName) + end + end + + return self + + end --- Builds a set of groups that are only active. -- Only the groups that are active will be included within the set. @@ -1381,10 +1402,46 @@ do -- SET_GROUP self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) + if self.Filter.Zones then + self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) + local timing = self.ZoneTimerInterval or 30 + self.ZoneTimer:Start(timing,timing) + end end return self end + + --- Set filter timer interval for FilterZones if using active filtering with FilterStart(). + -- @param #SET_GROUP self + -- @param #number Seconds Seconds between check intervals, defaults to 30. **Caution** - do not be too agressive with timing! Groups are usually not moving fast enough + -- to warrant a check of below 10 seconds. + -- @return #SET_GROUP self + function SET_GROUP:FilterZoneTimer(Seconds) + self.ZoneTimerInterval = Seconds or 30 + return self + end + + --- Stops the filtering. + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:FilterStop() + + if _DATABASE then + + self:UnHandleEvent(EVENTS.Birth) + self:UnHandleEvent(EVENTS.Dead) + self:UnHandleEvent(EVENTS.Crash) + self:UnHandleEvent(EVENTS.RemoveUnit) + + if self.Filter.Zones and self.ZoneTimer and self.ZoneTimer:IsRunning() then + self.ZoneTimer:Stop() + end + end + + return self + end + --- Handles the OnDead or OnCrash event for alive groups set. -- Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP. @@ -1956,6 +2013,8 @@ end do -- SET_UNIT --- @type SET_UNIT + -- @field Core.Timer#TIMER ZoneTimer + -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE --- Mission designers can use the SET_UNIT class to build sets of units belonging to certain: @@ -2347,7 +2406,56 @@ do -- SET_UNIT return CountU end + + --- [Internal] Private function for use of continous zone filter + -- @param #SET_UNIT self + -- @return #SET_UNIT self + function SET_UNIT:_ContinousZoneFilter() + + local Database = _DATABASE.UNITS + + for ObjectName, Object in pairs( Database ) do + if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then + self:Add( ObjectName, Object ) + elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + self:Remove(ObjectName) + end + end + + return self + + end + + --- Set filter timer interval for FilterZones if using active filtering with FilterStart(). + -- @param #SET_UNIT self + -- @param #number Seconds Seconds between check intervals, defaults to 30. **Caution** - do not be too agressive with timing! Groups are usually not moving fast enough + -- to warrant a check of below 10 seconds. + -- @return #SET_UNIT self + function SET_UNIT:FilterZoneTimer(Seconds) + self.ZoneTimerInterval = Seconds or 30 + return self + end + + --- Stops the filtering. + -- @param #SET_UNIT self + -- @return #SET_UNIT self + function SET_UNIT:FilterStop() + if _DATABASE then + + self:UnHandleEvent(EVENTS.Birth) + self:UnHandleEvent(EVENTS.Dead) + self:UnHandleEvent(EVENTS.Crash) + self:UnHandleEvent(EVENTS.RemoveUnit) + + if self.Filter.Zones and self.ZoneTimer and self.ZoneTimer:IsRunning() then + self.ZoneTimer:Stop() + end + end + + return self + end + --- Starts the filtering. -- @param #SET_UNIT self -- @return #SET_UNIT self @@ -2359,6 +2467,11 @@ do -- SET_UNIT self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) + if self.Filter.Zones then + self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) + local timing = self.ZoneTimerInterval or 30 + self.ZoneTimer:Start(timing,timing) + end end return self @@ -3834,6 +3947,8 @@ end do -- SET_CLIENT --- @type SET_CLIENT + -- @field Core.Timer#TIMER ZoneTimer + -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_CLIENT} class to build sets of units belonging to certain: @@ -4141,6 +4256,54 @@ do -- SET_CLIENT return self end + --- [Internal] Private function for use of continous zone filter + -- @param #SET_CLIENT self + -- @return #SET_CLIENT self + function SET_CLIENT:_ContinousZoneFilter() + + local Database = _DATABASE.CLIENTS + + for ObjectName, Object in pairs( Database ) do + if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then + self:Add( ObjectName, Object ) + elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + self:Remove(ObjectName) + end + end + + return self + + end + + --- Set filter timer interval for FilterZones if using active filtering with FilterStart(). + -- @param #SET_CLIENT self + -- @param #number Seconds Seconds between check intervals, defaults to 30. **Caution** - do not be too agressive with timing! Groups are usually not moving fast enough + -- to warrant a check of below 10 seconds. + -- @return #SET_CLIENT self + function SET_CLIENT:FilterZoneTimer(Seconds) + self.ZoneTimerInterval = Seconds or 30 + return self + end + + --- Stops the filtering. + -- @param #SET_CLIENT self + -- @return #SET_CLIENT self + function SET_CLIENT:FilterStop() + + if _DATABASE then + + self:UnHandleEvent(EVENTS.Birth) + self:UnHandleEvent(EVENTS.Dead) + self:UnHandleEvent(EVENTS.Crash) + + if self.Filter.Zones and self.ZoneTimer and self.ZoneTimer:IsRunning() then + self.ZoneTimer:Stop() + end + end + + return self + end + --- Starts the filtering. -- @param #SET_CLIENT self -- @return #SET_CLIENT self @@ -4151,6 +4314,11 @@ do -- SET_CLIENT self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + if self.Filter.Zones then + self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) + local timing = self.ZoneTimerInterval or 30 + self.ZoneTimer:Start(timing,timing) + end end return self From 96467d79b86d04257165ad90f56e91dc9162134a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 20 Apr 2023 08:22:46 +0200 Subject: [PATCH 253/603] #CTLD --- Moose Development/Moose/Ops/CTLD.lua | 589 +++++++++++++-------------- 1 file changed, 294 insertions(+), 295 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 394a22138..1dca93f05 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -24,6 +24,299 @@ -- Last Update Apr 2023 +do + +------------------------------------------------------ +--- **CTLD_CARGO** class, extends Core.Base#BASE +-- @type CTLD_CARGO +-- @field #string ClassName Class name. +-- @field #number ID ID of this cargo. +-- @field #string Name Name for menu. +-- @field #table Templates Table of #POSITIONABLE objects. +-- @field #string CargoType Enumerator of Type. +-- @field #boolean HasBeenMoved Flag for moving. +-- @field #boolean LoadDirectly Flag for direct loading. +-- @field #number CratesNeeded Crates needed to build. +-- @field Wrapper.Positionable#POSITIONABLE Positionable Representation of cargo in the mission. +-- @field #boolean HasBeenDropped True if dropped from heli. +-- @field #number PerCrateMass Mass in kg. +-- @field #number Stock Number of builds available, -1 for unlimited. +-- @field #string Subcategory Sub-category name. +-- @extends Core.Base#BASE + +--- +-- @field #CTLD_CARGO CTLD_CARGO +CTLD_CARGO = { + ClassName = "CTLD_CARGO", + ID = 0, + Name = "none", + Templates = {}, + CargoType = "none", + HasBeenMoved = false, + LoadDirectly = false, + CratesNeeded = 0, + Positionable = nil, + HasBeenDropped = false, + PerCrateMass = 0, + Stock = nil, + Mark = nil, + } + + --- Define cargo types. + -- @type CTLD_CARGO.Enum + -- @field #string VEHICLE + -- @field #string TROOPS + -- @field #string FOB + -- @field #string CRATE + -- @field #string REPAIR + -- @field #string ENGINEERS + -- @field #string STATIC + CTLD_CARGO.Enum = { + VEHICLE = "Vehicle", -- #string vehicles + TROOPS = "Troops", -- #string troops + FOB = "FOB", -- #string FOB + CRATE = "Crate", -- #string crate + REPAIR = "Repair", -- #string repair + ENGINEERS = "Engineers", -- #string engineers + STATIC = "Static", -- #string statics + } + + --- Function to create new CTLD_CARGO object. + -- @param #CTLD_CARGO self + -- @param #number ID ID of this #CTLD_CARGO + -- @param #string Name Name for menu. + -- @param #table Templates Table of #POSITIONABLE objects. + -- @param #CTLD_CARGO.Enum Sorte Enumerator of Type. + -- @param #boolean HasBeenMoved Flag for moving. + -- @param #boolean LoadDirectly Flag for direct loading. + -- @param #number CratesNeeded Crates needed to build. + -- @param Wrapper.Positionable#POSITIONABLE Positionable Representation of cargo in the mission. + -- @param #boolean Dropped Cargo/Troops have been unloaded from a chopper. + -- @param #number PerCrateMass Mass in kg + -- @param #number Stock Number of builds available, nil for unlimited + -- @param #string Subcategory Name of subcategory, handy if using > 10 types to load. + -- @return #CTLD_CARGO self + function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory) + -- Inherit everything from BASE class. + local self=BASE:Inherit(self, BASE:New()) -- #CTLD_CARGO + self:T({ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped}) + self.ID = ID or math.random(100000,1000000) + self.Name = Name or "none" -- #string + self.Templates = Templates or {} -- #table + self.CargoType = Sorte or "type" -- #CTLD_CARGO.Enum + self.HasBeenMoved = HasBeenMoved or false -- #boolean + self.LoadDirectly = LoadDirectly or false -- #boolean + self.CratesNeeded = CratesNeeded or 0 -- #number + self.Positionable = Positionable or nil -- Wrapper.Positionable#POSITIONABLE + self.HasBeenDropped = Dropped or false --#boolean + self.PerCrateMass = PerCrateMass or 0 -- #number + self.Stock = Stock or nil --#number + self.Mark = nil + self.Subcategory = Subcategory or "Other" + return self + end + + --- Query ID. + -- @param #CTLD_CARGO self + -- @return #number ID + function CTLD_CARGO:GetID() + return self.ID + end + + --- Query Subcategory + -- @param #CTLD_CARGO self + -- @return #string SubCategory + function CTLD_CARGO:GetSubCat() + return self.Subcategory + end + + --- Query Mass. + -- @param #CTLD_CARGO self + -- @return #number Mass in kg + function CTLD_CARGO:GetMass() + return self.PerCrateMass + end + + --- Query Name. + -- @param #CTLD_CARGO self + -- @return #string Name + function CTLD_CARGO:GetName() + return self.Name + end + + --- Query Templates. + -- @param #CTLD_CARGO self + -- @return #table Templates + function CTLD_CARGO:GetTemplates() + return self.Templates + end + + --- Query has moved. + -- @param #CTLD_CARGO self + -- @return #boolean Has moved + function CTLD_CARGO:HasMoved() + return self.HasBeenMoved + end + + --- Query was dropped. + -- @param #CTLD_CARGO self + -- @return #boolean Has been dropped. + function CTLD_CARGO:WasDropped() + return self.HasBeenDropped + end + + --- Query directly loadable. + -- @param #CTLD_CARGO self + -- @return #boolean loadable + function CTLD_CARGO:CanLoadDirectly() + return self.LoadDirectly + end + + --- Query number of crates or troopsize. + -- @param #CTLD_CARGO self + -- @return #number Crates or size of troops. + function CTLD_CARGO:GetCratesNeeded() + return self.CratesNeeded + end + + --- Query type. + -- @param #CTLD_CARGO self + -- @return #CTLD_CARGO.Enum Type + function CTLD_CARGO:GetType() + return self.CargoType + end + + --- Query type. + -- @param #CTLD_CARGO self + -- @return Wrapper.Positionable#POSITIONABLE Positionable + function CTLD_CARGO:GetPositionable() + return self.Positionable + end + + --- Set HasMoved. + -- @param #CTLD_CARGO self + -- @param #boolean moved + function CTLD_CARGO:SetHasMoved(moved) + self.HasBeenMoved = moved or false + end + + --- Query if cargo has been loaded. + -- @param #CTLD_CARGO self + -- @param #boolean loaded + function CTLD_CARGO:Isloaded() + if self.HasBeenMoved and not self.WasDropped() then + return true + else + return false + end + end + + --- Set WasDropped. + -- @param #CTLD_CARGO self + -- @param #boolean dropped + function CTLD_CARGO:SetWasDropped(dropped) + self.HasBeenDropped = dropped or false + end + + --- Get Stock. + -- @param #CTLD_CARGO self + -- @return #number Stock + function CTLD_CARGO:GetStock() + if self.Stock then + return self.Stock + else + return -1 + end + end + + --- Add Stock. + -- @param #CTLD_CARGO self + -- @param #number Number to add, none if nil. + -- @return #CTLD_CARGO self + function CTLD_CARGO:AddStock(Number) + if self.Stock then -- Stock nil? + local number = Number or 1 + self.Stock = self.Stock + number + end + return self + end + + --- Remove Stock. + -- @param #CTLD_CARGO self + -- @param #number Number to reduce, none if nil. + -- @return #CTLD_CARGO self + function CTLD_CARGO:RemoveStock(Number) + if self.Stock then -- Stock nil? + local number = Number or 1 + self.Stock = self.Stock - number + if self.Stock < 0 then self.Stock = 0 end + end + return self + end + + --- Set Stock. + -- @param #CTLD_CARGO self + -- @param #number Number to set, nil means unlimited. + -- @return #CTLD_CARGO self + function CTLD_CARGO:SetStock(Number) + self.Stock = Number + return self + end + + --- Query crate type for REPAIR + -- @param #CTLD_CARGO self + -- @param #boolean + function CTLD_CARGO:IsRepair() + if self.CargoType == "Repair" then + return true + else + return false + end + end + + --- Query crate type for STATIC + -- @param #CTLD_CARGO self + -- @return #boolean + function CTLD_CARGO:IsStatic() + if self.CargoType == "Static" then + return true + else + return false + end + end + + --- Add mark + -- @param #CTLD_CARGO self + -- @return #CTLD_CARGO self + function CTLD_CARGO:AddMark(Mark) + self.Mark = Mark + return self + end + + --- Get mark + -- @param #CTLD_CARGO self + -- @return #string Mark + function CTLD_CARGO:GetMark(Mark) + return self.Mark + end + + --- Wipe mark + -- @param #CTLD_CARGO self + -- @return #CTLD_CARGO self + function CTLD_CARGO:WipeMark() + self.Mark = nil + return self + end + + --- Get overall mass of a cargo object, i.e. crates needed x mass per crate + -- @param #CTLD_CARGO self + -- @return #number mass + function CTLD_CARGO:GetNetMass() + return self.CratesNeeded * self.PerCrateMass + end + +end + do ------------------------------------------------------ @@ -40,7 +333,7 @@ do -- @extends Core.Base#BASE --- --- @field #CTLD_ENGINEERING +-- @field #CTLD_ENGINEERING CTLD_ENGINEERING CTLD_ENGINEERING = { ClassName = "CTLD_ENGINEERING", lid = "", @@ -287,300 +580,6 @@ CTLD_ENGINEERING = { end - -do - ------------------------------------------------------- ---- **CTLD_CARGO** class, extends Core.Base#BASE --- @type CTLD_CARGO --- @field #string ClassName Class name. --- @field #number ID ID of this cargo. --- @field #string Name Name for menu. --- @field #table Templates Table of #POSITIONABLE objects. --- @field #string CargoType Enumerator of Type. --- @field #boolean HasBeenMoved Flag for moving. --- @field #boolean LoadDirectly Flag for direct loading. --- @field #number CratesNeeded Crates needed to build. --- @field Wrapper.Positionable#POSITIONABLE Positionable Representation of cargo in the mission. --- @field #boolean HasBeenDropped True if dropped from heli. --- @field #number PerCrateMass Mass in kg. --- @field #number Stock Number of builds available, -1 for unlimited. --- @field #string Subcategory Sub-category name. --- @extends Core.Base#BASE - ---- --- @field #CTLD_CARGO CTLD_CARGO -CTLD_CARGO = { - ClassName = "CTLD_CARGO", - ID = 0, - Name = "none", - Templates = {}, - CargoType = "none", - HasBeenMoved = false, - LoadDirectly = false, - CratesNeeded = 0, - Positionable = nil, - HasBeenDropped = false, - PerCrateMass = 0, - Stock = nil, - Mark = nil, - } - - --- Define cargo types. - -- @type CTLD_CARGO.Enum - -- @field #string VEHICLE - -- @field #string TROOPS - -- @field #string FOB - -- @field #string CRATE - -- @field #string REPAIR - -- @field #string ENGINEERS - -- @field #string STATIC - CTLD_CARGO.Enum = { - VEHICLE = "Vehicle", -- #string vehicles - TROOPS = "Troops", -- #string troops - FOB = "FOB", -- #string FOB - CRATE = "Crate", -- #string crate - REPAIR = "Repair", -- #string repair - ENGINEERS = "Engineers", -- #string engineers - STATIC = "Static", -- #string statics - } - - --- Function to create new CTLD_CARGO object. - -- @param #CTLD_CARGO self - -- @param #number ID ID of this #CTLD_CARGO - -- @param #string Name Name for menu. - -- @param #table Templates Table of #POSITIONABLE objects. - -- @param #CTLD_CARGO.Enum Sorte Enumerator of Type. - -- @param #boolean HasBeenMoved Flag for moving. - -- @param #boolean LoadDirectly Flag for direct loading. - -- @param #number CratesNeeded Crates needed to build. - -- @param Wrapper.Positionable#POSITIONABLE Positionable Representation of cargo in the mission. - -- @param #boolean Dropped Cargo/Troops have been unloaded from a chopper. - -- @param #number PerCrateMass Mass in kg - -- @param #number Stock Number of builds available, nil for unlimited - -- @param #string Subcategory Name of subcategory, handy if using > 10 types to load. - -- @return #CTLD_CARGO self - function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory) - -- Inherit everything from BASE class. - local self=BASE:Inherit(self, BASE:New()) -- #CTLD - self:T({ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped}) - self.ID = ID or math.random(100000,1000000) - self.Name = Name or "none" -- #string - self.Templates = Templates or {} -- #table - self.CargoType = Sorte or "type" -- #CTLD_CARGO.Enum - self.HasBeenMoved = HasBeenMoved or false -- #boolean - self.LoadDirectly = LoadDirectly or false -- #boolean - self.CratesNeeded = CratesNeeded or 0 -- #number - self.Positionable = Positionable or nil -- Wrapper.Positionable#POSITIONABLE - self.HasBeenDropped = Dropped or false --#boolean - self.PerCrateMass = PerCrateMass or 0 -- #number - self.Stock = Stock or nil --#number - self.Mark = nil - self.Subcategory = Subcategory or "Other" - return self - end - - --- Query ID. - -- @param #CTLD_CARGO self - -- @return #number ID - function CTLD_CARGO:GetID() - return self.ID - end - - --- Query Subcategory - -- @param #CTLD_CARGO self - -- @return #string SubCategory - function CTLD_CARGO:GetSubCat() - return self.Subcategory - end - - --- Query Mass. - -- @param #CTLD_CARGO self - -- @return #number Mass in kg - function CTLD_CARGO:GetMass() - return self.PerCrateMass - end - - --- Query Name. - -- @param #CTLD_CARGO self - -- @return #string Name - function CTLD_CARGO:GetName() - return self.Name - end - - --- Query Templates. - -- @param #CTLD_CARGO self - -- @return #table Templates - function CTLD_CARGO:GetTemplates() - return self.Templates - end - - --- Query has moved. - -- @param #CTLD_CARGO self - -- @return #boolean Has moved - function CTLD_CARGO:HasMoved() - return self.HasBeenMoved - end - - --- Query was dropped. - -- @param #CTLD_CARGO self - -- @return #boolean Has been dropped. - function CTLD_CARGO:WasDropped() - return self.HasBeenDropped - end - - --- Query directly loadable. - -- @param #CTLD_CARGO self - -- @return #boolean loadable - function CTLD_CARGO:CanLoadDirectly() - return self.LoadDirectly - end - - --- Query number of crates or troopsize. - -- @param #CTLD_CARGO self - -- @return #number Crates or size of troops. - function CTLD_CARGO:GetCratesNeeded() - return self.CratesNeeded - end - - --- Query type. - -- @param #CTLD_CARGO self - -- @return #CTLD_CARGO.Enum Type - function CTLD_CARGO:GetType() - return self.CargoType - end - - --- Query type. - -- @param #CTLD_CARGO self - -- @return Wrapper.Positionable#POSITIONABLE Positionable - function CTLD_CARGO:GetPositionable() - return self.Positionable - end - - --- Set HasMoved. - -- @param #CTLD_CARGO self - -- @param #boolean moved - function CTLD_CARGO:SetHasMoved(moved) - self.HasBeenMoved = moved or false - end - - --- Query if cargo has been loaded. - -- @param #CTLD_CARGO self - -- @param #boolean loaded - function CTLD_CARGO:Isloaded() - if self.HasBeenMoved and not self.WasDropped() then - return true - else - return false - end - end - - --- Set WasDropped. - -- @param #CTLD_CARGO self - -- @param #boolean dropped - function CTLD_CARGO:SetWasDropped(dropped) - self.HasBeenDropped = dropped or false - end - - --- Get Stock. - -- @param #CTLD_CARGO self - -- @return #number Stock - function CTLD_CARGO:GetStock() - if self.Stock then - return self.Stock - else - return -1 - end - end - - --- Add Stock. - -- @param #CTLD_CARGO self - -- @param #number Number to add, none if nil. - -- @return #CTLD_CARGO self - function CTLD_CARGO:AddStock(Number) - if self.Stock then -- Stock nil? - local number = Number or 1 - self.Stock = self.Stock + number - end - return self - end - - --- Remove Stock. - -- @param #CTLD_CARGO self - -- @param #number Number to reduce, none if nil. - -- @return #CTLD_CARGO self - function CTLD_CARGO:RemoveStock(Number) - if self.Stock then -- Stock nil? - local number = Number or 1 - self.Stock = self.Stock - number - if self.Stock < 0 then self.Stock = 0 end - end - return self - end - - --- Set Stock. - -- @param #CTLD_CARGO self - -- @param #number Number to set, nil means unlimited. - -- @return #CTLD_CARGO self - function CTLD_CARGO:SetStock(Number) - self.Stock = Number - return self - end - - --- Query crate type for REPAIR - -- @param #CTLD_CARGO self - -- @param #boolean - function CTLD_CARGO:IsRepair() - if self.CargoType == "Repair" then - return true - else - return false - end - end - - --- Query crate type for STATIC - -- @param #CTLD_CARGO self - -- @return #boolean - function CTLD_CARGO:IsStatic() - if self.CargoType == "Static" then - return true - else - return false - end - end - - --- Add mark - -- @param #CTLD_CARGO self - -- @return #CTLD_CARGO self - function CTLD_CARGO:AddMark(Mark) - self.Mark = Mark - return self - end - - --- Get mark - -- @param #CTLD_CARGO self - -- @return #string Mark - function CTLD_CARGO:GetMark(Mark) - return self.Mark - end - - --- Wipe mark - -- @param #CTLD_CARGO self - -- @return #CTLD_CARGO self - function CTLD_CARGO:WipeMark() - self.Mark = nil - return self - end - - --- Get overall mass of a cargo object, i.e. crates needed x mass per crate - -- @param #CTLD_CARGO self - -- @return #number mass - function CTLD_CARGO:GetNetMass() - return self.CratesNeeded * self.PerCrateMass - end - -end - do ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -- TODO CTLD From a41ee3279ea1af28588a6d9ec7425edeb87f5e36 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 24 Apr 2023 16:44:54 +0200 Subject: [PATCH 254/603] #SET_SCENERY * Added functions to count Life0, Life and RelativeLife points of SET_SCENERY --- Moose Development/Moose/Core/Set.lua | 42 +++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 65d2ed608..a3e6232b0 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -7797,7 +7797,7 @@ do -- SET_SCENERY end - --- + --- [Internal] Determine if an object is to be included in the SET -- @param #SET_SCENERY self -- @param Wrapper.Scenery#SCENERY MScenery -- @return #SET_SCENERY self @@ -7805,4 +7805,44 @@ do -- SET_SCENERY self:F2( MScenery ) return true end + + --- Count overall initial (Life0) lifepoints of the SET objects. + -- @param #SET_SCENERY self + -- @return #number LIfe0Points + function SET_SCENERY:GetLife0() + local life0 = 0 + self:ForEachScenery( + function(obj) + local Obj = obj -- Wrapper.Scenery#SCENERY + life0 = life0 + Obj:GetLife0() + end + ) + return life0 + end + + --- Count overall current lifepoints of the SET objects. + -- @param #SET_SCENERY self + -- @return #number LifePoints + function SET_SCENERY:GetLife() + local life = 0 + self:ForEachScenery( + function(obj) + local Obj = obj -- Wrapper.Scenery#SCENERY + life = life + Obj:GetLife() + end + ) + return life + end + + --- Calculate current relative lifepoints of the SET objects, i.e. Life divided by Life0 as percentage value, eg 75 meaning 75% alive. + -- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Be aware that thus the relative life value might be > 100 after a hit. + -- @param #SET_SCENERY self + -- @return #number LifePoints + function SET_SCENERY:GetRelativeLife() + local life0 = self:GetLife0() + local life = self:GetLife() + local rlife = math.floor((life / life0) * 100) + return rlife + end + end From a0219e4a081d5757178f919c600f63d7b92af7ba Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 25 Apr 2023 09:12:47 +0200 Subject: [PATCH 255/603] #SCENERY * Added update of Life0 value if `GetLife()`is called #SET_SCENERY * Added Documentation --- Moose Development/Moose/Core/Set.lua | 7 +++++-- Moose Development/Moose/Wrapper/Scenery.lua | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index a3e6232b0..454d092f9 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -7835,12 +7835,15 @@ do -- SET_SCENERY end --- Calculate current relative lifepoints of the SET objects, i.e. Life divided by Life0 as percentage value, eg 75 meaning 75% alive. - -- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Be aware that thus the relative life value might be > 100 after a hit. + -- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Hence we will adjust the Life0 value to 120% + -- of the last life value if life exceeds life0 ata any point. + -- Thus will will get a smooth percentage decrease, if you use this e.g. as success criteria for a bombing task. -- @param #SET_SCENERY self -- @return #number LifePoints function SET_SCENERY:GetRelativeLife() - local life0 = self:GetLife0() local life = self:GetLife() + local life0 = self:GetLife0() + self:T3(string.format("Set Lifepoints: %d life0 | %d life",life0,life)) local rlife = math.floor((life / life0) * 100) return rlife end diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index bc64dfd15..e66d10586 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -63,12 +63,18 @@ function SCENERY:GetDCSObject() end --- Get current life points from the SCENERY Object. +-- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Hence we will adjust the life0 value to 120% +-- of the last life value if life exceeds life0 (initial life) at any point. Thus will will get a smooth percentage decrease, if you use this e.g. as success +-- criteria for a bombing task. --@param #SCENERY self --@return #number life function SCENERY:GetLife() local life = 0 if self.SceneryObject then life = self.SceneryObject:getLife() + if life > self.Life0 then + self.Life0 = math.floor(life * 1.2) + end end return life end From a4e576e26d7979938f7595e18ba996701aff5e47 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 27 Apr 2023 10:32:16 +0200 Subject: [PATCH 256/603] #AUFTRAG * Bring options for afterburner usage down to AUFTRAG level #OpsGroup (Flightgroup) * Follow setting option for AB usage from mission, if set, generally and for execution phase of the AUFTRAG --- Moose Development/Moose/Ops/Auftrag.lua | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index eefd9cc2c..9edd38838 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -3084,6 +3084,39 @@ function AUFTRAG:SetRequiredCarriers(NcarriersMin, NcarriersMax) return self end +--- Set that (jet) aircraft are generally **not** allowed to use afterburner. Default is use of afterburner is allowed. +-- @param #AUFTRAG self +-- @return #AUFTRAG self +function AUFTRAG:SetProhibitAfterburner() + self.prohibitAB = true + return self +end + +--- Set that (jet) aircraft are generally allowed to use afterburner. Default is use of afterburner is allowed. +-- @param #AUFTRAG self +-- @return #AUFTRAG self +function AUFTRAG:SetAllowAfterburner() + self.prohibitAB = false + return self +end + +--- Set that (jet) aircraft are **not** allowed to use afterburner in mission execution phase. Default is use of afterburner is allowed. +-- @param #AUFTRAG self +-- @return #AUFTRAG self +function AUFTRAG:SetProhibitAfterburnerExecutePhase() + self.prohibitABExecute = true + return self +end + +--- Set that (jet) aircraft are allowed to use afterburner in mission execution phase. Default is use of afterburner is allowed. +-- @param #AUFTRAG self +-- @return #AUFTRAG self +function AUFTRAG:SetAllowAfterburnerExecutePhase() + self.prohibitABExecute = false + return self +end + +-- prohibitABExecute --- **[LEGION, COMMANDER, CHIEF]** Assign a legion cohort to the mission. Only these cohorts will be considered for the job. -- @param #AUFTRAG self From 265fb4a8e81a4667c0e03a2cf041cdb8b1ea82b4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 27 Apr 2023 10:32:32 +0200 Subject: [PATCH 257/603] #AUFTRAG * Bring options for afterburner usage down to AUFTRAG level #OpsGroup (Flightgroup) * Follow setting option for AB usage from mission, if set, generally and for execution phase of the AUFTRAG --- Moose Development/Moose/Ops/OpsGroup.lua | 38 ++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 3f9b36e74..3efa4ecba 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -5346,7 +5346,7 @@ function OPSGROUP:onafterMissionStart(From, Event, To, Mission) --if self.isFlightgroup and Mission.type~=AUFTRAG.Type.ALERT5 then -- FLIGHTGROUP.SetReadyForTakeoff(self, true) --end - + -- Route group to mission zone. if self.speedMax>3.6 or true then @@ -5402,7 +5402,18 @@ function OPSGROUP:onafterMissionExecute(From, Event, To, Mission) if Mission.engagedetectedOn then self:SetEngageDetectedOn(UTILS.MetersToNM(Mission.engagedetectedRmax), Mission.engagedetectedTypes, Mission.engagedetectedEngageZones, Mission.engagedetectedNoEngageZones) end - + + -- Set AB usage for mission execution based on Mission entry, if the option was set in the mission + if self.isFlightgroup then + if Mission.prohibitABExecute == true then + self:SetProhibitAfterburner() + self:I("Set prohibit AB") + elseif Mission.prohibitABExecute == false then + self:SetAllowAfterburner() + self:T2("Set allow AB") + end + end + end --- On after "PauseMission" event. @@ -5699,6 +5710,17 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission) return end + -- Set AB usage based on Mission entry, if the option was set in the mission + if self.isFlightgroup then + if Mission.prohibitAB == true then + self:T2("Setting prohibit AB") + self:SetProhibitAfterburner() + elseif Mission.prohibitAB == false then + self:T2("Setting allow AB") + self:SetAllowAfterburner() + end + end + -- Check if group is done. self:_CheckGroupDone(delay) @@ -6118,7 +6140,19 @@ function OPSGROUP:_SetMissionOptions(mission) if mission.icls then self:SwitchICLS(mission.icls.Channel, mission.icls.Morse, mission.icls.UnitName) end + + -- Set AB usage based on Mission entry, if the option was set in the mission + if self.isFlightgroup then + if mission.prohibitAB == true then + self:SetProhibitAfterburner() + self:T2("Set prohibit AB") + elseif mission.prohibitAB == false then + self:SetAllowAfterburner() + self:T2("Set allow AB") + end + end + return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From a29dcbb9565907ef22142748e4b2dd83482a462d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 5 May 2023 10:31:43 +0200 Subject: [PATCH 258/603] #COORDINATE * Added `IsInSteepArea()`and `IsInFlatArea()` --- Moose Development/Moose/Core/Point.lua | 47 +++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 6ea45c8ba..772de02bf 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -3283,7 +3283,52 @@ do -- COORDINATE return self:GetTemperatureText( nil, Settings ) end - + + + --- Function to check if a coordinate is in a steep (>8% elevation) area of the map + -- @param #COORDINATE self + -- @param #number Radius (Optional) Radius to check around the coordinate, defaults to 50m (100m diameter) + -- @param #number Minelevation (Optional) Elevation from which on a area is defined as steep, defaults to 8% (8m height gain across 100 meters) + -- @return #boolen IsSteep If true, area is steep + -- @return #number MaxElevation Elevation in meters measured over 100m + function COORDINATE:IsInSteepArea(Radius,Minelevation) + local steep = false + local elev = Minelevation or 8 + local bdelta = 0 + local h0 = self:GetLandHeight() + local radius = Radius or 50 + local diam = radius * 2 + for i=0,150,30 do + local polar = math.fmod(i+180,360) + local c1 = self:Translate(radius,i,false,false) + local c2 = self:Translate(radius,polar,false,false) + local h1 = c1:GetLandHeight() + local h2 = c2:GetLandHeight() + local d1 = math.abs(h1-h2) + local d2 = math.abs(h0-h1) + local d3 = math.abs(h0-h2) + local dm = d1 > d2 and d1 or d2 + local dm1 = dm > d3 and dm or d3 + bdelta = dm1 > bdelta and dm1 or bdelta + self:T(string.format("d1=%d, d2=%d, d3=%d, max delta=%d",d1,d2,d3,bdelta)) + end + local steepness = bdelta / (radius / 100) + if steepness >= elev then steep = true end + return steep, math.floor(steepness) + end + + --- Function to check if a coordinate is in a flat (<8% elevation) area of the map + -- @param #COORDINATE self + -- @param #number Radius (Optional) Radius to check around the coordinate, defaults to 50m (100m diameter) + -- @param #number Minelevation (Optional) Elevation from which on a area is defined as steep, defaults to 8% (8m height gain across 100 meters) + -- @return #boolen IsFlat If true, area is flat + -- @return #number MaxElevation Elevation in meters measured over 100m + function COORDINATE:IsInFlatArea(Radius,Minelevation) + local steep, elev = self:IsInSteepArea(Radius,Minelevation) + local flat = not steep + return flat, elev + end + end do -- POINT_VEC3 From bda073652755f74ba63cb75aee9b5a38948c1721 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 9 May 2023 09:48:49 +0200 Subject: [PATCH 259/603] #AI_A2X... * Fixes --- .../Moose/AI/AI_A2A_Dispatcher.lua | 14 +++++++ .../Moose/AI/AI_A2G_Dispatcher.lua | 17 +++++++- .../Moose/AI/AI_Air_Dispatcher.lua | 4 +- Moose Development/Moose/AI/AI_Escort.lua | 42 +++++++++---------- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index d0d1a940a..504d39985 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1687,6 +1687,20 @@ do -- AI_A2A_DISPATCHER return DefenderSquadron end + + --- Get a resource count from a specific squadron + -- @param #AI_A2A_DISPATCHER self + -- @param #string Squadron Name of the squadron. + -- @return #number Number of airframes available or nil if the squadron does not exist + function AI_A2A_DISPATCHER:QuerySquadron(Squadron) + local Squadron = self:GetSquadron(Squadron) + if Squadron.ResourceCount then + self:T2(string.format("%s = %s",Squadron.Name,Squadron.ResourceCount)) + return Squadron.ResourceCount + end + self:F({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount}) + return nil + end --- [DEPRECATED - Might create problems launching planes] Set the Squadron visible before startup of the dispatcher. -- All planes will be spawned as uncontrolled on the parking spot. diff --git a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua index ea2b975e2..cccbdbddc 100644 --- a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua @@ -951,7 +951,7 @@ do -- AI_A2G_DISPATCHER AI_A2G_DISPATCHER.DefenseQueue = {} --- Defense approach types. - -- @type #AI_A2G_DISPATCHER.DefenseApproach + -- @type AI_A2G_DISPATCHER.DefenseApproach AI_A2G_DISPATCHER.DefenseApproach = { Random = 1, Distance = 2, @@ -1806,6 +1806,19 @@ do -- AI_A2G_DISPATCHER return DefenderSquadron end + --- Get a resource count from a specific squadron + -- @param #AI_A2G_DISPATCHER self + -- @param #string Squadron Name of the squadron. + -- @return #number Number of airframes available or nil if the squadron does not exist + function AI_A2G_DISPATCHER:QuerySquadron(Squadron) + local Squadron = self:GetSquadron(Squadron) + if Squadron.ResourceCount then + self:T2(string.format("%s = %s",Squadron.Name,Squadron.ResourceCount)) + return Squadron.ResourceCount + end + self:F({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount}) + return nil + end --- Set the Squadron visible before startup of the dispatcher. -- All planes will be spawned as uncontrolled on the parking spot. @@ -1839,7 +1852,7 @@ do -- AI_A2G_DISPATCHER --- Check if the Squadron is visible before startup of the dispatcher. -- @param #AI_A2G_DISPATCHER self -- @param #string SquadronName The squadron name. - -- @return #bool true if visible. + -- @return #boolean true if visible. -- @usage -- -- -- Set the Squadron visible before startup of dispatcher. diff --git a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua index c63e007d7..7c9c735b5 100644 --- a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua @@ -947,7 +947,7 @@ do -- AI_AIR_DISPATCHER AI_AIR_DISPATCHER.DefenseQueue = {} --- Defense approach types - -- @type #AI_AIR_DISPATCHER.DefenseApproach + -- @type AI_AIR_DISPATCHER.DefenseApproach AI_AIR_DISPATCHER.DefenseApproach = { Random = 1, Distance = 2, @@ -1852,7 +1852,7 @@ do -- AI_AIR_DISPATCHER --- Check if the Squadron is visible before startup of the dispatcher. -- @param #AI_AIR_DISPATCHER self -- @param #string SquadronName The squadron name. - -- @return #bool true if visible. + -- @return #boolean true if visible. -- @usage -- -- -- Set the Squadron visible before startup of dispatcher. diff --git a/Moose Development/Moose/AI/AI_Escort.lua b/Moose Development/Moose/AI/AI_Escort.lua index 0ca7a05eb..e18948790 100644 --- a/Moose Development/Moose/AI/AI_Escort.lua +++ b/Moose Development/Moose/AI/AI_Escort.lua @@ -443,9 +443,9 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. --- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZLevels The amount of levels on the Z-axis. -- @return #AI_ESCORT @@ -493,9 +493,9 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. --- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZLevels The amount of levels on the Z-axis. -- @return #AI_ESCORT @@ -580,7 +580,7 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. -- @return #AI_ESCORT function AI_ESCORT:MenuFormationTrail( XStart, XSpace, YStart ) @@ -594,7 +594,7 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. -- @return #AI_ESCORT function AI_ESCORT:MenuFormationStack( XStart, XSpace, YStart, YSpace ) @@ -609,8 +609,8 @@ end -- This menu will appear under **Formation**. -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. --- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. +-- @param #number ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @return #AI_ESCORT function AI_ESCORT:MenuFormationLeftLine( XStart, YStart, ZStart, ZSpace ) @@ -625,8 +625,8 @@ end -- This menu will appear under **Formation**. -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. --- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. +-- @param #number ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @return #AI_ESCORT function AI_ESCORT:MenuFormationRightLine( XStart, YStart, ZStart, ZSpace ) @@ -642,8 +642,8 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. --- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. +-- @param #number ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @return #AI_ESCORT function AI_ESCORT:MenuFormationLeftWing( XStart, XSpace, YStart, ZStart, ZSpace ) @@ -659,8 +659,8 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. --- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. +-- @param #number ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @return #AI_ESCORT function AI_ESCORT:MenuFormationRightWing( XStart, XSpace, YStart, ZStart, ZSpace ) @@ -676,9 +676,9 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. --- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @return #AI_ESCORT function AI_ESCORT:MenuFormationCenterWing( XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) @@ -694,9 +694,9 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. --- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @return #AI_ESCORT function AI_ESCORT:MenuFormationVic( XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) @@ -712,9 +712,9 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. --- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZStart The start position on the Z-axis in meters for the first group. -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. -- @param #number ZLevels The amount of levels on the Z-axis. -- @return #AI_ESCORT @@ -1471,7 +1471,7 @@ end -- @param #AI_ESCORT self -- @param #number XStart The start position on the X-axis in meters for the first group. -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. --- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YStart The start position on the Y-axis in meters for the first group. -- @return #AI_ESCORT function AI_ESCORT:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart ) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 9ff16c279..31dbe87ab 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -206,7 +206,7 @@ FLIGHTGROUP.PlayerSkill = { --- Player data. -- @type FLIGHTGROUP.PlayerData --- @type #string name Player name. +-- @field #string name Player name. -- @field #boolean subtitles Display subtitles. -- @field #string skill Skill level. From c84b54a21401f6fe447866761c01a3b71b62f2af Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 11 May 2023 15:30:25 +0200 Subject: [PATCH 260/603] #CTLD * placement of dropped crates to better align to unit length --- Moose Development/Moose/Ops/CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 1dca93f05..c623b2c58 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1219,7 +1219,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.35" +CTLD.version="1.0.36" --- Instantiate a new CTLD. -- @param #CTLD self @@ -2335,7 +2335,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) rheading = UTILS.RandomGaussian(0,30,-90,90,100) rheading = math.fmod((heading + rheading + addon), 360) else - local initialSpacing = IsHerc and 16 or 12 -- initial spacing of the first crates + local initialSpacing = IsHerc and 16 or (capabilities.length+2) -- initial spacing of the first crates local crateSpacing = 4 -- further spacing of remaining crates local lateralSpacing = 4 -- lateral spacing of crates local nrSideBySideCrates = 3 -- number of crates that are placed side-by-side From a33ca693683c07e46c3c64ded473320f8b28b9fe Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 17 May 2023 10:26:58 +0200 Subject: [PATCH 261/603] fixes --- Moose Development/Moose/Functional/Warehouse.lua | 5 +++-- Moose Development/Moose/Ops/CTLD.lua | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 4e0c0552b..93239b913 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1798,7 +1798,7 @@ _WAREHOUSEDB = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="1.0.2" +WAREHOUSE.version="1.0.2a" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -4561,7 +4561,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!") return - elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.NAVALCARRIER then + elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.NAVALCARRIER + or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then -- Spawn Ship in port zone spawngroup=self:_SpawnAssetGroundNaval(_alias, _assetitem, Request, self.portzone) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index b590aad62..04e84ee7d 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1221,11 +1221,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version --<<<<<<< HEAD -CTLD.version="1.0.36" -======= CTLD.version="1.0.37" ->>>>>>> origin/master --- Instantiate a new CTLD. -- @param #CTLD self From f1b030f3d599dae9691461b51d9a30c11fa2c621 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 19 May 2023 17:11:26 +0200 Subject: [PATCH 262/603] #Added findcloseststatic --- Moose Development/Moose/Core/Point.lua | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 772de02bf..fb18c6f6b 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -603,6 +603,46 @@ do -- COORDINATE return set end + + --- Scan/find STATICS within a certain radius around the coordinate using the world.searchObjects() DCS API function. + -- @param #COORDINATE self + -- @param #number radius (Optional) Scan radius in meters. Default 100 m. + -- @return Core.Set#SET_UNIT Set of units. + function COORDINATE:ScanStatics(radius) + + local _,_,_,_,statics=self:ScanObjects(radius, false, true, false) + + local set=SET_STATIC:New() + + for _,unit in pairs(statics) do + set:AddUnit(unit) + end + + return set + end + + --- Find the closest static to the COORDINATE within a certain radius. + -- @param #COORDINATE self + -- @param #number radius Scan radius in meters. Default 100 m. + -- @return Wrapper.Static#STATIC The closest static or #nil if no unit is inside the given radius. + function COORDINATE:FindClosestStatic(radius) + + local units=self:ScanStatics(radius) + + local umin=nil --Wrapper.Unit#UNIT + local dmin=math.huge + for _,_unit in pairs(units.Set) do + local unit=_unit --Wrapper.Static#STATIC + local coordinate=unit:GetCoordinate() + local d=self:Get2DDistance(coordinate) + if d Date: Sat, 20 May 2023 12:12:00 +0200 Subject: [PATCH 263/603] Small fix --- Moose Development/Moose/Core/Point.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index fb18c6f6b..d942e49ba 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -614,8 +614,8 @@ do -- COORDINATE local set=SET_STATIC:New() - for _,unit in pairs(statics) do - set:AddUnit(unit) + for _,stat in pairs(statics) do + set:AddStatic(STATIC:Find(stat)) end return set From a69e62a66b0be9c7a9502fd74e390baf8bdd7d7a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 22 May 2023 17:56:53 +0200 Subject: [PATCH 264/603] #SPAWN * Tweaked NewFromTemplate, and gave a better example --- Moose Development/Moose/Core/Spawn.lua | 141 +++++++++++++++++++++---- 1 file changed, 121 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 6b5e9bb55..b170501bc 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -335,7 +335,7 @@ end -- @param #SPAWN self -- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. -- @param #string SpawnAliasPrefix is the name that will be given to the Group at runtime. --- @return #SPAWN +-- @return #SPAWN self -- @usage -- -- NATO helicopters engaging in the battle field. -- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' ) @@ -385,32 +385,129 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) return self end ---- Creates a new SPAWN instance to create new groups based on the provided template. +--- Creates a new SPAWN instance to create new groups based on the provided template. This will also register the template for future use. -- @param #SPAWN self --- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure! --- @param #string SpawnTemplatePrefix is the name of the Group that will be given at each spawn. --- @param #string SpawnAliasPrefix is the name that will be given to the Group at runtime. --- @return #SPAWN +-- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure - see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_func_addGroup)! +-- @param #string SpawnTemplatePrefix [Mandatory] is the name of the template and the prefix of the GROUP on spawn. +-- @param #string SpawnAliasPrefix [Optional] is the prefix that will be given to the GROUP on spawn. +-- @param #boolean MooseNaming [Optional] If false, skip the Moose naming additions (like groupname#001-01) - you need to ensure yourself no duplicate group names exist! +-- @return #SPAWN self -- @usage --- -- Create a new SPAWN object based on a Group Template defined from scratch. --- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' ) --- @usage --- --- -- Create a new CSAR_Spawn object based on a normal Group Template to spawn a soldier. --- local CSAR_Spawn = SPAWN:NewWithFromTemplate( Template, "CSAR", "Pilot" ) --- -function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } ) - if SpawnAliasPrefix == nil or SpawnAliasPrefix == "" then - BASE:I( "ERROR: in function NewFromTemplate, required parameter SpawnAliasPrefix is not set" ) +-- -- Spawn a P51 Mustang from scratch +-- local ttemp = +-- { +-- ["modulation"] = 0, +-- ["tasks"] = +-- { +-- }, -- end of ["tasks"] +-- ["task"] = "Reconnaissance", +-- ["uncontrolled"] = false, +-- ["route"] = +-- { +-- ["points"] = +-- { +-- [1] = +-- { +-- ["alt"] = 2000, +-- ["action"] = "Turning Point", +-- ["alt_type"] = "BARO", +-- ["speed"] = 125, +-- ["task"] = +-- { +-- ["id"] = "ComboTask", +-- ["params"] = +-- { +-- ["tasks"] = +-- { +-- }, -- end of ["tasks"] +-- }, -- end of ["params"] +-- }, -- end of ["task"] +-- ["type"] = "Turning Point", +-- ["ETA"] = 0, +-- ["ETA_locked"] = true, +-- ["y"] = 666285.71428571, +-- ["x"] = -312000, +-- ["formation_template"] = "", +-- ["speed_locked"] = true, +-- }, -- end of [1] +-- }, -- end of ["points"] +-- }, -- end of ["route"] +-- ["groupId"] = 1, +-- ["hidden"] = false, +-- ["units"] = +-- { +-- [1] = +-- { +-- ["alt"] = 2000, +-- ["alt_type"] = "BARO", +-- ["livery_id"] = "USAF 364th FS", +-- ["skill"] = "High", +-- ["speed"] = 125, +-- ["type"] = "TF-51D", +-- ["unitId"] = 1, +-- ["psi"] = 0, +-- ["y"] = 666285.71428571, +-- ["x"] = -312000, +-- ["name"] = "P51-1-1", +-- ["payload"] = +-- { +-- ["pylons"] = +-- { +-- }, -- end of ["pylons"] +-- ["fuel"] = 340.68, +-- ["flare"] = 0, +-- ["chaff"] = 0, +-- ["gun"] = 100, +-- }, -- end of ["payload"] +-- ["heading"] = 0, +-- ["callsign"] = +-- { +-- [1] = 1, +-- [2] = 1, +-- ["name"] = "Enfield11", +-- [3] = 1, +-- }, -- end of ["callsign"] +-- ["onboard_num"] = "010", +-- }, -- end of [1] +-- }, -- end of ["units"] +-- ["y"] = 666285.71428571, +-- ["x"] = -312000, +-- ["name"] = "P51", +-- ["communication"] = true, +-- ["start_time"] = 0, +-- ["frequency"] = 124, +-- } +-- +-- +-- local mustang = SPAWN:NewFromTemplate(ttemp,"P51D") +-- -- you MUST set the next three: +-- mustang:InitCountry(country.id.FRANCE) +-- mustang:InitCategory(Group.Category.AIRPLANE) +-- mustang:InitCoalition(coalition.side.BLUE) +-- mustang:OnSpawnGroup( +-- function(grp) +-- MESSAGE:New("Group Spawned: "..grp:GetName(),15,"SPAWN"):ToAll() +-- end +-- ) +-- mustang:Spawn() +-- +function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix, MooseNaming ) + local self = BASE:Inherit( self, BASE:New() ) + self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } ) + --if SpawnAliasPrefix == nil or SpawnAliasPrefix == "" then + --BASE:I( "ERROR: in function NewFromTemplate, required parameter SpawnAliasPrefix is not set" ) + --return nil + --end + if SpawnTemplatePrefix == nil or SpawnTemplatePrefix == "" then + BASE:I( "ERROR: in function NewFromTemplate, required parameter SpawnTemplatePrefix is not set" ) return nil end if SpawnTemplate then self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! self.SpawnTemplatePrefix = SpawnTemplatePrefix - self.SpawnAliasPrefix = SpawnAliasPrefix + self.SpawnAliasPrefix = SpawnAliasPrefix or SpawnTemplatePrefix + self.SpawnTemplate.name = SpawnTemplatePrefix self.SpawnIndex = 0 self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. self.AliveUnits = 0 -- Contains the counter how many units are currently alive @@ -435,7 +532,8 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr self.SpawnInitModex = nil self.SpawnInitAirbase = nil self.TweakedTemplate = true -- Check if the user is using self made template. - + self.MooseNameing = MooseNaming or true + self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else error( "There is no template provided for SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) @@ -3060,6 +3158,9 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 if self.TweakedTemplate ~= nil and self.TweakedTemplate == true then BASE:I( "WARNING: You are using a tweaked template." ) SpawnTemplate = self.SpawnTemplate + if self.MooseNameing then + SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) + end else SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) From 4c7239c5ecbde271998cb1584fab8e2500f176e1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 25 May 2023 08:22:51 +0200 Subject: [PATCH 265/603] Bugfixes --- Moose Development/Moose/Ops/RescueHelo.lua | 4 +++- Moose Development/Moose/Utilities/Utils.lua | 17 +++++++++++++++++ .../Moose/Wrapper/Controllable.lua | 12 ++++++++---- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/RescueHelo.lua b/Moose Development/Moose/Ops/RescueHelo.lua index 4820b6501..b341c8a14 100644 --- a/Moose Development/Moose/Ops/RescueHelo.lua +++ b/Moose Development/Moose/Ops/RescueHelo.lua @@ -805,7 +805,9 @@ function RESCUEHELO:_OnEventCrashOrEject(EventData) self:T(self.lid..text) -- Get coordinate of unit. - local coord=unit:GetCoordinate() + --local coord=unit:GetCoordinate() + local Vec3 = EventData.IniDCSUnit:getPoint() -- Vec3 + local coord = COORDINATE:NewFromVec3(Vec3) if coord and self.rescuezone:IsCoordinateInZone(coord) then diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index b2daf3229..2e3ff8ff0 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -385,6 +385,23 @@ UTILS.BasicSerialize = function(s) end end +function UTILS.PrintTableToLog(table, indent) + if not table then + BASE:E("No table passed!") + return + end + if not indent then indent = 0 end + for k, v in pairs(table) do + if type(v) == "table" then + BASE:I(string.rep(" ", indent) .. tostring(k) .. " = {") + UTILS.PrintTableToLog(v, indent + 1) + BASE:I(string.rep(" ", indent) .. "}") + else + BASE:I(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v)) + end + end +end + UTILS.ToDegree = function(angle) return angle*180/math.pi diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 4234d43b7..c6117c328 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -690,7 +690,8 @@ function CONTROLLABLE:CommandActivateACLS( UnitID, Name, Delay ) if Delay and Delay > 0 then SCHEDULER:New( nil, self.CommandActivateACLS, { self, UnitID, Name }, Delay ) else - self:SetCommand( CommandActivateACLS ) + local controller = self:_GetController() + controller:setCommand( CommandActivateACLS ) end return self @@ -711,7 +712,8 @@ function CONTROLLABLE:CommandDeactivateACLS( Delay ) if Delay and Delay > 0 then SCHEDULER:New( nil, self.CommandDeactivateACLS, { self }, Delay ) else - self:SetCommand( CommandDeactivateACLS ) + local controller = self:_GetController() + controller:setCommand( CommandDeactivateACLS ) end return self @@ -772,7 +774,8 @@ function CONTROLLABLE:CommandActivateLink4(Frequency, UnitID, Callsign, Delay) if Delay and Delay>0 then SCHEDULER:New(nil, self.CommandActivateLink4, {self, Frequency, UnitID, Callsign}, Delay) else - self:SetCommand(CommandActivateLink4) + local controller = self:_GetController() + controller:setCommand(CommandActivateLink4) end return self @@ -810,7 +813,8 @@ function CONTROLLABLE:CommandDeactivateLink4(Delay) if Delay and Delay>0 then SCHEDULER:New(nil, self.CommandDeactivateLink4, {self}, Delay) else - self:SetCommand(CommandDeactivateLink4) + local controller = self:_GetController() + controller:setCommand(CommandDeactivateLink4) end return self From 158ffde7807179cf927ebc7bf5920f8758ceffd4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 25 May 2023 08:58:21 +0200 Subject: [PATCH 266/603] #CONTROLLER * Fix for Link4 --- Moose Development/Moose/Wrapper/Controllable.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index c6117c328..a609f5a10 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -691,6 +691,7 @@ function CONTROLLABLE:CommandActivateACLS( UnitID, Name, Delay ) SCHEDULER:New( nil, self.CommandActivateACLS, { self, UnitID, Name }, Delay ) else local controller = self:_GetController() + self:I({controller}) controller:setCommand( CommandActivateACLS ) end @@ -763,7 +764,7 @@ function CONTROLLABLE:CommandActivateLink4(Frequency, UnitID, Callsign, Delay) local CommandActivateLink4= { id = "ActivateLink4", params= { - ["frequency "] = freq*1000000, + ["frequency"] = freq*1000000, ["unitId"] = UnitID or self:GetID(), ["name"] = Callsign or "LNK", } From b70780162bf20122045caa8d0e3d1b39bf6d79cb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 28 May 2023 15:29:01 +0200 Subject: [PATCH 267/603] #SPAWN * Logic fix for the last parameter of `NewFromTemplate()` --- Moose Development/Moose/Core/Spawn.lua | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index b170501bc..6e28be1a1 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -318,7 +318,7 @@ function SPAWN:New( SpawnTemplatePrefix ) self.SpawnInitRadio = nil -- No radio comms setting. self.SpawnInitModex = nil self.SpawnInitAirbase = nil - self.TweakedTemplate = false -- Check if the user is using self made template. + self.TweakedTemplate = true -- Check if the user is using self made template. self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -388,9 +388,9 @@ end --- Creates a new SPAWN instance to create new groups based on the provided template. This will also register the template for future use. -- @param #SPAWN self -- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure - see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_func_addGroup)! --- @param #string SpawnTemplatePrefix [Mandatory] is the name of the template and the prefix of the GROUP on spawn. +-- @param #string SpawnTemplatePrefix [Mandatory] is the name of the template and the prefix of the GROUP on spawn. The name in the template **will** be overwritten! -- @param #string SpawnAliasPrefix [Optional] is the prefix that will be given to the GROUP on spawn. --- @param #boolean MooseNaming [Optional] If false, skip the Moose naming additions (like groupname#001-01) - you need to ensure yourself no duplicate group names exist! +-- @param #boolean NoMooseNamingPostfix [Optional] If true, skip the Moose naming additions (like groupname#001-01) - **but** you need to ensure yourself no duplicate group names exist! -- @return #SPAWN self -- @usage -- -- Spawn a P51 Mustang from scratch @@ -491,7 +491,7 @@ end -- ) -- mustang:Spawn() -- -function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix, MooseNaming ) +function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix, NoMooseNamingPostfix ) local self = BASE:Inherit( self, BASE:New() ) self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } ) --if SpawnAliasPrefix == nil or SpawnAliasPrefix == "" then @@ -532,7 +532,10 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr self.SpawnInitModex = nil self.SpawnInitAirbase = nil self.TweakedTemplate = true -- Check if the user is using self made template. - self.MooseNameing = MooseNaming or true + self.MooseNameing = true + if NoMooseNamingPostfix == true then + self.MooseNameing = false + end self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -3158,8 +3161,10 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 if self.TweakedTemplate ~= nil and self.TweakedTemplate == true then BASE:I( "WARNING: You are using a tweaked template." ) SpawnTemplate = self.SpawnTemplate - if self.MooseNameing then + if self.MooseNameing == true then SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) + else + SpawnTemplate.name = self:SpawnGroupName() end else SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) From cf39f494f8ef77676b93b5e10bf9e8701d66d063 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 30 May 2023 07:38:05 +0200 Subject: [PATCH 268/603] #CSAR * Corrected flare distance to kms --- Moose Development/Moose/Ops/CSAR.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index abbe3980d..84a07b2dd 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -30,7 +30,7 @@ -- @module Ops.CSAR -- @image OPS_CSAR.jpg --- Date: January 2023 +-- Date: May 2023 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM @@ -292,7 +292,7 @@ CSAR.AircraftType["Bronco-OV-10A"] = 2 --- CSAR class version. -- @field #string version -CSAR.version="1.0.17" +CSAR.version="1.0.18" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -1880,7 +1880,7 @@ function CSAR:_SignalFlare(_unitName) if _SETTINGS:IsImperial() then _distance = string.format("%.1fnm",UTILS.MetersToNM(_closest.distance)) else - _distance = string.format("%.1fkm",_closest.distance) + _distance = string.format("%.1fkm",_closest.distance/1000) end local _msg = string.format("%s - Popping signal flare at your %s o\'clock. Distance %s", self:_GetCustomCallSign(_unitName), _clockDir, _distance) self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true, true) From 631d82216eab0d7e5833a0e9c08e1daa4b865bfb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 30 May 2023 11:53:15 +0200 Subject: [PATCH 269/603] SET --- Moose Development/Moose/Core/Set.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 454d092f9..a2363beac 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -7843,7 +7843,7 @@ do -- SET_SCENERY function SET_SCENERY:GetRelativeLife() local life = self:GetLife() local life0 = self:GetLife0() - self:T3(string.format("Set Lifepoints: %d life0 | %d life",life0,life)) + self:T2(string.format("Set Lifepoints: %d life0 | %d life",life0,life)) local rlife = math.floor((life / life0) * 100) return rlife end From 5afad6e3135673758b084e5930d966e550e5016a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 30 May 2023 12:05:51 +0200 Subject: [PATCH 270/603] #PLAYERTASK * Added FSM event "Progress" that will be triggered if the targetcount goes down for a task, but is not yet zero. This will bubble up to PLAYERTASKCONTROLLER #PLAYERTASKCONTROLLER * Added FSM event"TaskProgess" (see above). The event function will recveive the PLAYERTASK and the current target count * Changed the menu build strategy to ensure a lot less menu rebuilds. Menus are now only build if * A new player joins (for the joining single player) * On joining a task (for the joining single player) * When aborting a task (for the aborting single player) * On Task succes (for all players) * Removal of time based builds: The status loop will now only enforce the menu build if the task count in the queue is smaller than the menu item limit. This effectively enforces a time-based order of tasks, and newer, even higher prio task will not bubble into the task list of pilots automatically. --- Moose Development/Moose/Ops/PlayerTask.lua | 96 ++++++++++++++++------ 1 file changed, 70 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index b5ee9e527..34471b41f 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -58,6 +58,7 @@ do -- @field #table NextTaskFailure -- @field #string FinalState -- @field #string TypeName +-- @field #number PreviousCount -- @extends Core.Fsm#FSM @@ -92,11 +93,12 @@ PLAYERTASK = { NextTaskSuccess = {}, NextTaskFailure = {}, FinalState = "none", + PreviousCount = 0, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.14" +PLAYERTASK.version="0.1.16" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -151,6 +153,8 @@ function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType) return self end + self.PreviousCount = self.Target:CountTargets() + self:T(self.lid.."Created.") -- FMS start state is PLANNED. @@ -162,6 +166,7 @@ function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType) self:AddTransition("*", "ClientAdded", "*") -- Client has been added to the task self:AddTransition("*", "ClientRemoved", "*") -- Client has been removed from the task self:AddTransition("*", "Executing", "Executing") -- First client is executing the Task. + self:AddTransition("*", "Progress", "*") -- Task target count reduced - progress self:AddTransition("*", "Done", "Done") -- All clients have reported that Task is done. self:AddTransition("*", "Cancel", "Done") -- Command to cancel the Task. self:AddTransition("*", "Success", "Done") @@ -710,6 +715,16 @@ function PLAYERTASK:onafterStatus(From, Event, To) status = "Success" end + if status ~= "Failed" and status ~= "Success" then + -- Partial Success? + local targetcount = self.Target:CountTargets() + if targetcount < self.PreviousCount then + -- Progress + self:__Progress(-2,targetcount) + self.PreviousCount = targetcount + end + end + if self.verbose then self:I(self.lid.."Target dead: "..tostring(targetdead).." | Clients alive: " .. tostring(clientsalive)) end @@ -723,6 +738,21 @@ function PLAYERTASK:onafterStatus(From, Event, To) end +--- [Internal] On after progress call +-- @param #PLAYERTASK self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number TargetCount +-- @return #PLAYERTASK self +function PLAYERTASK:onafterProgress(From, Event, To, TargetCount) + self:T({From, Event, To}) + if self.TaskController then + self.TaskController:__TaskProgress(-1,self,TargetCount ) + end + return self +end + --- [Internal] On after planned call -- @param #PLAYERTASK self -- @param #string From @@ -1549,6 +1579,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self:AddTransition("*", "TaskCancelled", "*") self:AddTransition("*", "TaskSuccess", "*") self:AddTransition("*", "TaskFailed", "*") + self:AddTransition("*", "TaskProgress", "*") self:AddTransition("*", "TaskTargetSmoked", "*") self:AddTransition("*", "TaskTargetFlared", "*") self:AddTransition("*", "TaskTargetIlluminated", "*") @@ -1615,6 +1646,15 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) -- @param #string Event Event. -- @param #string To To state. -- @param Ops.PlayerTask#PLAYERTASK Task + + --- On After "TaskProgress" event. Task target count has been reduced. + -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskProgress + -- @param #PLAYERTASKCONTROLLER self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.PlayerTask#PLAYERTASK Task The current Task. + -- @param #number TargetCount Targets left over --- On After "TaskRepeatOnFailed" event. Task has failed and will be repeated. -- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskRepeatOnFailed @@ -3303,13 +3343,13 @@ function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu local taskinfomenu = nil if self.taskinfomenu then local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) - local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu):SetTag(newtag) + local taskinfomenu = MENU_GROUP:New(group,menutaskinfo,topmenu):SetTag(newtag) local ittypes = {} local itaskmenu = {} local tnow = timer.getTime() for _tasktype,_data in pairs(tasktypes) do - ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu):SetTag(newtag) + ittypes[_tasktype] = MENU_GROUP:New(group,_tasktype,taskinfomenu):SetTag(newtag) local tasks = taskpertype[_tasktype] or {} local n = 0 for _,_task in pairs(tasks) do @@ -3335,7 +3375,7 @@ function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu --self:T(self.lid.."Menu text = "..text) end end - local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task):SetTag(newtag) + local taskentry = MENU_GROUP_COMMAND:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task):SetTag(newtag) --taskentry:SetTag(playername) itaskmenu[#itaskmenu+1] = taskentry -- keep max items limit @@ -3409,6 +3449,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) -- 2)+3) Join or abort? if joinorabort then self.PlayerMenu[playername]:RemoveSubMenus() + self.PlayerMenu[playername] = MENU_GROUP:New(group,menuname,self.MenuParent) self.PlayerMenu[playername]:SetTag(newtag) topmenu = self.PlayerMenu[playername] elseif (not playerhastask) or enforced then @@ -3420,17 +3461,18 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) --self.PlayerMenu[playername]:RemoveSubMenus() --oldmenu = self.PlayerMenu[playername] --self.PlayerMenu[playername] = nil - self.PlayerMenu[playername]:RemoveSubMenus() - self.PlayerMenu[playername] = MENU_GROUP_DELAYED:New(group,menuname,self.MenuParent) - self.PlayerMenu[playername]:SetTag(newtag) - self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime() - timedbuild = true + + --self.PlayerMenu[playername]:RemoveSubMenus() + --self.PlayerMenu[playername] = MENU_GROUP:New(group,menuname,self.MenuParent) + --self.PlayerMenu[playername]:SetTag(newtag) + --self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime() + --timedbuild = true end topmenu = self.PlayerMenu[playername] end else -- 1) new player# - topmenu = MENU_GROUP_DELAYED:New(group,menuname,self.MenuParent) + topmenu = MENU_GROUP:New(group,menuname,self.MenuParent) self.PlayerMenu[playername] = topmenu self.PlayerMenu[playername]:SetTag(newtag) self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime() @@ -3450,21 +3492,21 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale) local menuabort = self.gettext:GetEntry("MENUABORT",self.locale) - local active = MENU_GROUP_DELAYED:New(group,menuactive,topmenu):SetTag(newtag) - local info = MENU_GROUP_COMMAND_DELAYED:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client):SetTag(newtag) - local mark = MENU_GROUP_COMMAND_DELAYED:New(group,menumark,active,self._MarkTask,self,group,client):SetTag(newtag) + local active = MENU_GROUP:New(group,menuactive,topmenu):SetTag(newtag) + local info = MENU_GROUP_COMMAND:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client):SetTag(newtag) + local mark = MENU_GROUP_COMMAND:New(group,menumark,active,self._MarkTask,self,group,client):SetTag(newtag) if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then if self.noflaresmokemenu ~= true then -- no smoking/flaring here if A2A or designer has set noflaresmokemenu to true - local smoke = MENU_GROUP_COMMAND_DELAYED:New(group,menusmoke,active,self._SmokeTask,self,group,client):SetTag(newtag) - local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client):SetTag(newtag) + local smoke = MENU_GROUP_COMMAND:New(group,menusmoke,active,self._SmokeTask,self,group,client):SetTag(newtag) + local flare = MENU_GROUP_COMMAND:New(group,menuflare,active,self._FlareTask,self,group,client):SetTag(newtag) local IsNight = client:GetCoordinate():IsNight() if IsNight then - local light = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._IlluminateTask,self,group,client):SetTag(newtag) + local light = MENU_GROUP_COMMAND:New(group,menuflare,active,self._IlluminateTask,self,group,client):SetTag(newtag) end end end - local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client):SetTag(newtag) + local abort = MENU_GROUP_COMMAND:New(group,menuabort,active,self._AbortTask,self,group,client):SetTag(newtag) if self.activehasinfomenu and self.taskinfomenu then self:T("Building Active-Info Menus for "..playername) if self.PlayerInfoMenu[playername] then @@ -3484,13 +3526,13 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) self.PlayerJoinMenu[playername]:RemoveSubMenus(nil,oldtag) end - local joinmenu = MENU_GROUP_DELAYED:New(group,menujoin,topmenu):SetTag(newtag) + local joinmenu = MENU_GROUP:New(group,menujoin,topmenu):SetTag(newtag) self.PlayerJoinMenu[playername] = joinmenu local ttypes = {} local taskmenu = {} for _tasktype,_data in pairs(tasktypes) do - ttypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,joinmenu):SetTag(newtag) + ttypes[_tasktype] = MENU_GROUP:New(group,_tasktype,joinmenu):SetTag(newtag) local tasks = taskpertype[_tasktype] or {} local n = 0 for _,_task in pairs(tasks) do @@ -3510,7 +3552,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) end end - local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task):SetTag(newtag) + local taskentry = MENU_GROUP_COMMAND:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task):SetTag(newtag) --taskentry:SetTag(playername) taskmenu[#taskmenu+1] = taskentry n = n + 1 @@ -3527,22 +3569,22 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag) end end - if self.AllowFlash then + if self.AllowFlash and topmenu ~= nil then local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) - local flashmenu = MENU_GROUP_COMMAND_DELAYED:New(group,flashtext,topmenu,self._SwitchFlashing,self,group,client):SetTag(newtag) + local flashmenu = MENU_GROUP_COMMAND:New(group,flashtext,topmenu,self._SwitchFlashing,self,group,client):SetTag(newtag) end if self.TaskQueue:Count() == 0 then self:T("No open tasks info") local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) - local joinmenu = MENU_GROUP_DELAYED:New(group,menunotasks,self.PlayerMenu[playername]):SetTag(newtag) + local joinmenu = MENU_GROUP:New(group,menunotasks,self.PlayerMenu[playername]):SetTag(newtag) rebuilddone = true end --- -- REFRESH MENU --- if rebuilddone then - self.PlayerMenu[playername]:RemoveSubMenus(nil,oldtag) - self.PlayerMenu[playername]:Set() + --self.PlayerMenu[playername]:RemoveSubMenus(nil,oldtag) + --self.PlayerMenu[playername]:Set() self.PlayerMenu[playername]:Refresh() end end @@ -3874,7 +3916,9 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To) if taskcount ~= self.lasttaskcount then self.lasttaskcount = taskcount - enforcedmenu = true + if taskcount < self.menuitemlimit then + enforcedmenu = true + end end self:_BuildMenus(nil,enforcedmenu) From 878169f51c566663bccd4edfc9023b8608695d30 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 1 Jun 2023 10:00:31 +0200 Subject: [PATCH 271/603] Fixes --- Moose Development/Moose/Core/Database.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 2 +- Moose Development/Moose/Wrapper/Airbase.lua | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index b2c91dba2..4ad7f9e50 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1124,7 +1124,7 @@ end -- @param #string AirbaseName Name of the airbase. -- @return #number Category. function DATABASE:GetCategoryFromAirbase( AirbaseName ) - return self.AIRBASES[AirbaseName]:GetCategory() + return self.AIRBASES[AirbaseName]:GetAirbaseCategory() end diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 6e28be1a1..df22111c3 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -318,7 +318,7 @@ function SPAWN:New( SpawnTemplatePrefix ) self.SpawnInitRadio = nil -- No radio comms setting. self.SpawnInitModex = nil self.SpawnInitAirbase = nil - self.TweakedTemplate = true -- Check if the user is using self made template. + self.TweakedTemplate = false -- Check if the user is using self made template. self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 07c2acf7e..45235aaf2 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -2295,3 +2295,19 @@ function AIRBASE:CheckOnRunWay(group, radius, despawn) return false end + +--- Get category of airbase. +-- @param #AIRBASE self +-- @return #number Category of airbase from GetDesc().category. +function AIRBASE:GetCategory() + return self.category +end + +--- Get category name of airbase. +-- @param #AIRBASE self +-- @return #string Category of airbase, i.e. Airdrome, Ship, or Helipad +function AIRBASE:GetCategoryName() + return AIRBASE.CategoryName[self.category] +end + + From 065c59f167dab88b07d74718aef53523e0c828c1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 2 Jun 2023 08:44:43 +0200 Subject: [PATCH 272/603] #UNIT #CTLD * Stabilize that sometimes a unit coordinate cannot be found --- Moose Development/Moose/Modules_local.lua | 1 + Moose Development/Moose/Ops/CTLD.lua | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index c638e81db..a16bfc378 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -21,6 +21,7 @@ __Moose.Include( 'Core\\Zone_Detection.lua' ) __Moose.Include( 'Core\\Database.lua' ) __Moose.Include( 'Core\\Set.lua' ) __Moose.Include( 'Core\\Point.lua' ) +__Moose.Include( 'Core\\Pathline.lua' ) __Moose.Include( 'Core\\Velocity.lua' ) __Moose.Include( 'Core\\Message.lua' ) __Moose.Include( 'Core\\Fsm.lua' ) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 04e84ee7d..0edb51e80 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update Apr 2023 +-- Last Update June 2023 do @@ -1221,7 +1221,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.37" +CTLD.version="1.0.38" --- Instantiate a new CTLD. -- @param #CTLD self @@ -4308,6 +4308,9 @@ end local uspeed = Unit:GetVelocityMPS() local uheight = Unit:GetHeight() local ucoord = Unit:GetCoordinate() + if not ucoord then + return false + end local gheight = ucoord:GetLandHeight() local aheight = uheight - gheight -- height above ground local maxh = self.maximumHoverHeight -- 15 @@ -4334,6 +4337,9 @@ end local uspeed = Unit:GetVelocityMPS() local uheight = Unit:GetHeight() local ucoord = Unit:GetCoordinate() + if not ucoord then + return false + end local gheight = ucoord:GetLandHeight() local aheight = uheight - gheight -- height above ground local minh = self.HercMinAngels-- 1500m @@ -4419,6 +4425,9 @@ end end local uheight = Unit:GetHeight() local ucoord = Unit:GetCoordinate() + if not ucoord then + return false + end local gheight = ucoord:GetLandHeight() local aheight = uheight - gheight -- height above ground if aheight >= minheight then From bb17e8cc1fccbd09a9aa8fb83d8387e6ab786f19 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 3 Jun 2023 12:34:09 +0200 Subject: [PATCH 273/603] #CTLD * Ensure new menus can be build if player changes helos --- Moose Development/Moose/Ops/CTLD.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 0edb51e80..298f7b1dd 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1221,7 +1221,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.38" +CTLD.version="1.0.39" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1750,6 +1750,8 @@ function CTLD:_EventHandler(EventData) if _coalition ~= self.coalition then return --ignore! end + local unitname = event.IniUnitName or "none" + self.MenusDone[unitname] = nil -- check is Helicopter local _unit = event.IniUnit local _group = event.IniGroup @@ -1770,6 +1772,7 @@ function CTLD:_EventHandler(EventData) local unitname = event.IniUnitName or "none" self.CtldUnits[unitname] = nil self.Loaded_Cargo[unitname] = nil + self.MenusDone[unitname] = nil end return self end @@ -3442,6 +3445,9 @@ function CTLD:_RefreshF10Menus() if _unit:IsHelicopter() or (self:IsHercules(_unit) and self.enableHercules) then --ensure no stupid unit entries here local unitName = _unit:GetName() _UnitList[unitName] = unitName + else + local unitName = _unit:GetName() + _UnitList[unitName] = nil end end -- end isAlive end -- end if _unit From 00f89f0855df980c47424140356d784b28c6cdec Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 6 Jun 2023 16:13:23 +0200 Subject: [PATCH 274/603] PLAYERTASK * Small fix in TTS text --- Moose Development/Moose/Ops/PlayerTask.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 34471b41f..70d88bbbd 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1490,7 +1490,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.58" +PLAYERTASKCONTROLLER.version="0.1.59" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -3153,7 +3153,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) --self:I(self.lid.." | ".. CoordText) end local ThreatLocaleTextTTS = self.gettext:GetEntry("THREATTEXTTTS",self.locale) - local ttstext = string.format(ThreatLocaleTextTTS,self.MenuName or self.Name,ttsplayername,ttstaskname,ThreatLevelText, targets, CoordText) + local ttstext = string.format(ThreatLocaleTextTTS,ttsplayername,self.MenuName or self.Name,ttstaskname,ThreatLevelText, targets, CoordText) -- POINTERTARGETLASINGTTS = ". Pointer over target and lasing." if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then if self.LasingDrone.playertask.inreach and self.LasingDrone:IsLasing() then From 0a0a4684f8f8eb536e7ac0802210b2c13ec462a5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 8 Jun 2023 13:55:52 +0200 Subject: [PATCH 275/603] #PLAYERTASKCONTROLLER * Added basic scoring --- .../Moose/Functional/Scoring.lua | 45 ++++++------ Moose Development/Moose/Ops/PlayerTask.lua | 72 +++++++++++++++++-- 2 files changed, 93 insertions(+), 24 deletions(-) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 3dda306a3..950ccd3c0 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -715,11 +715,11 @@ function SCORING:AddGoalScorePlayer( PlayerName, GoalTag, Text, Score ) PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 } PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score PlayerData.Score = PlayerData.Score + Score - - MESSAGE:NewType( self.DisplayMessagePrefix .. Text, - MESSAGE.Type.Information ) - :ToAll() - + if Text then + MESSAGE:NewType( self.DisplayMessagePrefix .. Text, + MESSAGE.Type.Information ) + :ToAll() + end self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, nil ) end end @@ -738,7 +738,7 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score ) local PlayerName = PlayerUnit:GetPlayerName() - self:F( { PlayerUnit.UnitName, PlayerName, GoalTag, Text, Score } ) + self:T2( { PlayerUnit.UnitName, PlayerName, GoalTag, Text, Score } ) -- PlayerName can be nil, if the Unit with the player crashed or due to another reason. if PlayerName then @@ -747,11 +747,12 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score ) PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 } PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score PlayerData.Score = PlayerData.Score + Score - - MESSAGE:NewType( self.DisplayMessagePrefix .. Text, + + if Text then + MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE.Type.Information ) :ToAll() - + end self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() ) end end @@ -784,11 +785,12 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score ) PlayerData.Score = self.Players[PlayerName].Score + Score PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score - - MESSAGE:NewType( self.DisplayMessagePrefix .. Mission:GetText() .. " : " .. Text .. " Score: " .. Score, - MESSAGE.Type.Information ) - :ToAll() - + + if Text then + MESSAGE:NewType( self.DisplayMessagePrefix .. Mission:GetText() .. " : " .. Text .. " Score: " .. Score, + MESSAGE.Type.Information ) + :ToAll() + end self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() ) end end @@ -820,9 +822,11 @@ function SCORING:_AddMissionGoalScore( Mission, PlayerName, Text, Score ) PlayerData.Score = self.Players[PlayerName].Score + Score PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score - - MESSAGE:NewType( string.format( "%s%s: %s! Player %s receives %d score!", self.DisplayMessagePrefix, Mission:GetText(), Text, PlayerName, Score ), MESSAGE.Type.Information ):ToAll() - + + if Text then + MESSAGE:NewType( string.format( "%s%s: %s! Player %s receives %d score!", self.DisplayMessagePrefix, Mission:GetText(), Text, PlayerName, Score ), MESSAGE.Type.Information ):ToAll() + end + self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score ) end end @@ -847,11 +851,12 @@ function SCORING:_AddMissionScore( Mission, Text, Score ) PlayerData.Score = PlayerData.Score + Score PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score - - MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in " .. Mission:GetText() .. ". " .. Score .. " mission score!", + + if Text then + MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in " .. Mission:GetText() .. ". " .. Score .. " mission score!", MESSAGE.Type.Information ) :ToAll() - + end self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) end end diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 70d88bbbd..ff0ba343d 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update December 2022 +-- @date Last Update June 2023 do @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.16" +PLAYERTASK.version="0.1.17" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -461,6 +461,9 @@ function PLAYERTASK:AddClient(Client) self.Clients:Push(Client,name) self:__ClientAdded(-2,Client) end + if self.TaskController and self.TaskController.Scoring then + self.TaskController.Scoring:_AddPlayerFromUnit(Client) + end return self end @@ -748,7 +751,15 @@ end function PLAYERTASK:onafterProgress(From, Event, To, TargetCount) self:T({From, Event, To}) if self.TaskController then - self.TaskController:__TaskProgress(-1,self,TargetCount ) + if self.TaskController.Scoring then + local clients,count = self:GetClientObjects() + if count > 0 then + for _,_client in pairs(clients) do + self.TaskController.Scoring:AddGoalScore(_client,self.Type,nil,10) + end + end + end + self.TaskController:__TaskProgress(-1,self,TargetCount) end return self end @@ -865,6 +876,15 @@ function PLAYERTASK:onafterSuccess(From, Event, To) if self.TargetMarker then self.TargetMarker:Remove() end + if self.TaskController.Scoring then + local clients,count = self:GetClientObjects() + if count > 0 then + for _,_client in pairs(clients) do + local auftrag = self:GetSubType() + self.TaskController.Scoring:AddGoalScore(_client,self.Type,nil,self.TaskController.Scores[self.Type]) + end + end + end self.timestamp = timer.getAbsTime() self.FinalState = "Success" self:__Done(-1) @@ -894,6 +914,15 @@ function PLAYERTASK:onafterFailed(From, Event, To) self.FinalState = "Failed" self:__Done(-1) end + if self.TaskController.Scoring then + local clients,count = self:GetClientObjects() + if count > 0 then + for _,_client in pairs(clients) do + local auftrag = self:GetSubType() + self.TaskController.Scoring:AddGoalScore(_client,self.Type,nil,-self.TaskController.Scores[self.Type]) + end + end + end self.timestamp = timer.getAbsTime() return self end @@ -969,6 +998,7 @@ do -- @field #boolean InfoHasLLDDM -- @field #table PlayerMenuTag -- @field #boolean UseTypeNames +-- @field Functional.Scoring#SCORING Scoring -- @extends Core.Fsm#FSM --- @@ -1294,6 +1324,7 @@ PLAYERTASKCONTROLLER = { InfoHasLLDDM = false, InfoHasCoordinate = false, UseTypeNames = false, + Scoring = nil, } --- @@ -1313,6 +1344,22 @@ PLAYERTASKCONTROLLER.Type = { AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing" AUFTRAG.Type.CTLD = "Combat Transport" AUFTRAG.Type.CSAR = "Combat Rescue" + +--- +-- @type Scores +PLAYERTASKCONTROLLER.Scores = { + [AUFTRAG.Type.PRECISIONBOMBING] = 100, + [AUFTRAG.Type.CTLD] = 100, + [AUFTRAG.Type.CSAR] = 100, + [AUFTRAG.Type.INTERCEPT] = 100, + [AUFTRAG.Type.ANTISHIP] = 100, + [AUFTRAG.Type.CAS] = 100, + [AUFTRAG.Type.BAI] = 100, + [AUFTRAG.Type.SEAD] = 100, + [AUFTRAG.Type.BOMBING] = 100, + [AUFTRAG.Type.PRECISIONBOMBING] = 100, + [AUFTRAG.Type.BOMBRUNWAY] = 100, +} --- -- @type SeadAttributes @@ -1490,7 +1537,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.59" +PLAYERTASKCONTROLLER.version="0.1.60" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -1710,6 +1757,23 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) end +--- [User] Set or create a SCORING object for this taskcontroller +-- @param #PLAYERTASKCONTROLLER self +-- @param Functional.Scoring#SCORING Scoring (optional) the Scoring object +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:EnableScoring(Scoring) + self.Scoring = Scoring or SCORING:New(self.Name) + return self +end + +--- [User] Remove the SCORING object from this taskcontroller +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:DisableScoring() + self.Scoring = nil + return self +end + --- [Internal] Init localization -- @param #PLAYERTASKCONTROLLER self -- @return #PLAYERTASKCONTROLLER self From 23e71e991fc77a7edbd732d220616126bd703bb4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 9 Jun 2023 15:13:20 +0200 Subject: [PATCH 276/603] #AIRBASE * Sinai airfield enumerator --- .../Moose/Core/ScheduleDispatcher.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 2 +- Moose Development/Moose/Wrapper/Airbase.lua | 67 ++++++++++++++++++- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index 611cb6fba..5eb58e731 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -1,4 +1,4 @@ ---- **Core** - SCHEDULEDISPATCHER dispatches the different schedules. +---- **Core** - SCHEDULEDISPATCHER dispatches the different schedules. -- -- === -- diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index df22111c3..894f43a36 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1614,7 +1614,7 @@ end -- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups. -- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn. -- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval. --- @param #boolen WithDelay Do not spawn the **first** group immediately, but delay the spawn as per the calculation below. +-- @param #boolean WithDelay Do not spawn the **first** group immediately, but delay the spawn as per the calculation below. -- Effectively the same as @{InitDelayOn}(). -- @return #SPAWN self -- @usage diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 0930f88ed..4aeb5b21b 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -490,7 +490,6 @@ AIRBASE.Syria={ ["Wujah_Al_Hajar"]="Wujah Al Hajar", ["Al_Dumayr"]="Al-Dumayr", ["Gazipasa"]="Gazipasa", - --["Ru_Convoy_4"]="Ru Convoy-4", ["Hatay"]="Hatay", ["Nicosia"]="Nicosia", ["Pinarbashi"]="Pinarbashi", @@ -619,6 +618,72 @@ AIRBASE.SouthAtlantic={ ["Gull_Point"] = "Gull Point", } +--- Airbases of the Sinai map: +-- +-- * AIRBASE.Sinai.Abu_Suwayr +-- * AIRBASE.Sinai.Sde_Dov +-- * AIRBASE.Sinai.AzZaqaziq +-- * AIRBASE.Sinai.Hatzor +-- * AIRBASE.Sinai.Kedem +-- * AIRBASE.Sinai.Nevatim +-- * AIRBASE.Sinai.Cairo_International_Airport +-- * AIRBASE.Sinai.Al_Ismailiyah +-- * AIRBASE.Sinai.As_Salihiyah +-- * AIRBASE.Sinai.Fayed +-- * AIRBASE.Sinai.Bilbeis_Air_Base +-- * AIRBASE.Sinai.Ramon_Airbase +-- * AIRBASE.Sinai.Kibrit_Air_Base +-- * AIRBASE.Sinai.El_Arish +-- * AIRBASE.Sinai.Ovda +-- * AIRBASE.Sinai.Melez +-- * AIRBASE.Sinai.Al_Mansurah +-- * AIRBASE.Sinai.Palmahim +-- * AIRBASE.Sinai.Baluza +-- * AIRBASE.Sinai.El_Gora +-- * AIRBASE.Sinai.Difarsuwar_Airfield +-- * AIRBASE.Sinai.Wadi_al_Jandali +-- * AIRBASE.Sinai.St_Catherine +-- * AIRBASE.Sinai.Tel_Nof +-- * AIRBASE.Sinai.Abu_Rudeis +-- * AIRBASE.Sinai.Inshas_Airbase +-- * AIRBASE.Sinai.Ben-Gurion +-- * AIRBASE.Sinai.Bir_Hasanah +-- * AIRBASE.Sinai.Cairo_West +-- +-- @field Sinai +AIRBASE.Sinai = { + ["Hatzerim"] = "Hatzerim", + ["Abu_Suwayr"] = "Abu Suwayr", + ["Sde_Dov"] = "Sde Dov", + ["AzZaqaziq"] = "AzZaqaziq", + ["Hatzor"] = "Hatzor", + ["Kedem"] = "Kedem", + ["Nevatim"] = "Nevatim", + ["Cairo_International_Airport"] = "Cairo International Airport", + ["Al_Ismailiyah"] = "Al Ismailiyah", + ["As_Salihiyah"] = "As Salihiyah", + ["Fayed"] = "Fayed", + ["Bilbeis_Air_Base"] = "Bilbeis Air Base", + ["Ramon_Airbase"] = "Ramon Airbase", + ["Kibrit_Air_Base"] = "Kibrit Air Base", + ["El_Arish"] = "El Arish", + ["Ovda"] = "Ovda", + ["Melez"] = "Melez", + ["Al_Mansurah"] = "Al Mansurah", + ["Palmahim"] = "Palmahim", + ["Baluza"] = "Baluza", + ["El_Gora"] = "El Gora", + ["Difarsuwar_Airfield"] = "Difarsuwar Airfield", + ["Wadi_al_Jandali"] = "Wadi al Jandali", + ["St_Catherine"] = "St Catherine", + ["Tel_Nof"] = "Tel Nof", + ["Abu_Rudeis"] = "Abu Rudeis", + ["Inshas_Airbase"] = "Inshas Airbase", + ["Ben-Gurion"] = "Ben-Gurion", + ["Bir_Hasanah"] = "Bir Hasanah", + ["Cairo_West"] = "Cairo West", +} + --- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". -- @type AIRBASE.ParkingSpot -- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot. From ccc11623c30d09d5b56f7f9e9a8a4292de8ce751 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 10 Jun 2023 19:04:18 +0200 Subject: [PATCH 277/603] #UTILS * Sinai Time --- Moose Development/Moose/Utilities/Utils.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 2e3ff8ff0..b86e0a109 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1685,6 +1685,10 @@ function UTILS.GMTToLocalTimeDifference() return 10 -- Guam is UTC+10 hours. elseif theatre==DCSMAP.Falklands then return -3 -- Fireland is UTC-3 hours. + elseif theatre==DCSMAP.SinaiMap then + return 2 -- Sinai is UTC+2 hours. + elseif theatre==DCSMAP.Sinai then -- in case ED corrects the name + return 2 -- Sinai is UTC+2 hours. else BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre))) return 0 From 4d247e75f212d8e793bbdc8b7da50d006169316d Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 12 Jun 2023 06:29:24 +0200 Subject: [PATCH 278/603] Airbase fixes (#1958) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Airbase.lua Corrected enumerator for Ben-Gurion (Ben_Gurion) * Update Airbase.lua (#1957) Deleted raj al … airbases #1955 --- Moose Development/Moose/Wrapper/Airbase.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 4aeb5b21b..2ad137c49 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -467,8 +467,8 @@ AIRBASE.TheChannel = { -- * AIRBASE.Syria.H3_Northwest -- * AIRBASE.Syria.H3_Southwest -- * AIRBASE.Syria.Kharab_Ishk --- * AIRBASE.Syria.Raj_al_Issa_East --- * AIRBASE.Syria.Raj_al_Issa_West +-- * AIRBASE.Syria.Raj_al_Issa_East (deleted by ED) +-- * AIRBASE.Syria.Raj_al_Issa_West (deleted by ED) -- * AIRBASE.Syria.Ruwayshid -- * AIRBASE.Syria.Sanliurfa -- * AIRBASE.Syria.Tal_Siman @@ -532,8 +532,8 @@ AIRBASE.Syria={ ["H3_Northwest"]="H3 Northwest", ["H3_Southwest"]="H3 Southwest", ["Kharab_Ishk"]="Kharab Ishk", - ["Raj_al_Issa_East"]="Raj al Issa East", - ["Raj_al_Issa_West"]="Raj al Issa West", + -- ["Raj_al_Issa_East"]="Raj al Issa East", + -- ["Raj_al_Issa_West"]="Raj al Issa West", ["Ruwayshid"]="Ruwayshid", ["Sanliurfa"]="Sanliurfa", ["Tal_Siman"]="Tal Siman", @@ -679,7 +679,7 @@ AIRBASE.Sinai = { ["Tel_Nof"] = "Tel Nof", ["Abu_Rudeis"] = "Abu Rudeis", ["Inshas_Airbase"] = "Inshas Airbase", - ["Ben-Gurion"] = "Ben-Gurion", + ["Ben_Gurion"] = "Ben-Gurion", ["Bir_Hasanah"] = "Bir Hasanah", ["Cairo_West"] = "Cairo West", } From 253ed62d467208665f7a896754012fd50e38533d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 13 Jun 2023 08:35:27 +0200 Subject: [PATCH 279/603] Fixes --- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Wrapper/Airbase.lua | 2 +- .../Moose/Wrapper/Positionable.lua | 19 ++++++++++--------- Moose Development/Moose/Wrapper/Unit.lua | 19 +++++++++++++------ 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index a2363beac..1d06739f9 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -3040,7 +3040,7 @@ do -- SET_UNIT -- @param Wrapper.Unit#UNIT MUnit -- @return #SET_UNIT self function SET_UNIT:IsIncludeObject( MUnit ) - self:F2( MUnit ) + self:F2( {MUnit} ) local MUnitInclude = false diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 4aeb5b21b..64a23f3cd 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -679,7 +679,7 @@ AIRBASE.Sinai = { ["Tel_Nof"] = "Tel Nof", ["Abu_Rudeis"] = "Abu Rudeis", ["Inshas_Airbase"] = "Inshas Airbase", - ["Ben-Gurion"] = "Ben-Gurion", + ["Ben_Gurion"] = "Ben-Gurion", ["Bir_Hasanah"] = "Bir Hasanah", ["Cairo_West"] = "Cairo West", } diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 5016aac7f..a935282f7 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -237,22 +237,23 @@ end -- @return DCS#Vec3 The 3D point vector of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetVec3() - local DCSPositionable = self:GetDCSObject() - if DCSPositionable then - - local vec3 = DCSPositionable:getPoint() - - if vec3 then + local status, vec3 = pcall( + function() + local vec3 = DCSPositionable:getPoint() + return vec3 + end + ) + --local vec3 = DCSPositionable:getPoint() + if status then return vec3 else - self:E( "ERROR: Cannot get vec3!" ) + self:E( { "Cannot get Vec3 from DCS Object", Positionable = self, Alive = self:IsAlive() } ) end end - -- ERROR! - self:E( { "Cannot GetVec3", Positionable = self, Alive = self:IsAlive() } ) + self:E( { "Cannot get the Positionable DCS Object for GetVec3", Positionable = self, Alive = self:IsAlive() } ) return nil end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index e716269a2..1ffcf5fe3 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -681,20 +681,27 @@ end --- Returns the Unit's ammunition. -- @param #UNIT self --- @return DCS#Unit.Ammo Table with ammuntion of the unit (or nil). This can be a complex table! +-- @return DCS#Unit.Ammo Table with ammuntion of the unit (or nil). This can be a complex table! function UNIT:GetAmmo() self:F2( self.UnitName ) - local DCSUnit = self:GetDCSObject() - if DCSUnit then - local UnitAmmo = DCSUnit:getAmmo() - return UnitAmmo + local status, unitammo = pcall( + function() + local UnitAmmo = DCSUnit:getAmmo() + return UnitAmmo + end + ) + if status then + return unitammo + end + --local UnitAmmo = DCSUnit:getAmmo() + --return UnitAmmo end - return nil end + --- Sets the Unit's Internal Cargo Mass, in kg -- @param #UNIT self -- @param #number mass to set cargo to From a8be273479ed6177b7aef0dae2f75238b6de4640 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 13 Jun 2023 18:01:13 +0200 Subject: [PATCH 280/603] FIXES --- Moose Development/Moose/Core/Spawn.lua | 6 +++++- Moose Development/Moose/Wrapper/Unit.lua | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 894f43a36..b1370313f 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3127,7 +3127,11 @@ function SPAWN:_GetTemplate( SpawnTemplatePrefix ) self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnTemplatePrefix } ) local SpawnTemplate = nil - + + if _DATABASE.Templates.Groups[SpawnTemplatePrefix] == nil then + error( 'No Template exists for SpawnTemplatePrefix = ' .. SpawnTemplatePrefix ) + end + local Template = _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template self:F( { Template = Template } ) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index e271ce1a0..11b363bca 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -179,9 +179,9 @@ function UNIT:GetDCSObject() return DCSUnit end - if self.DCSUnit then - return self.DCSUnit - end + --if self.DCSUnit then + --return self.DCSUnit + --end return nil end From ef17399128e3f5d3f6f6137a1fd3652ac3f4736f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 16 Jun 2023 18:10:07 +0200 Subject: [PATCH 281/603] #AIRBOSS SRS --- Moose Development/Moose/Ops/Airboss.lua | 346 +++++++++++++++++------- Moose Development/Moose/Sound/SRS.lua | 19 +- Moose Development/Moose/Wrapper/Net.lua | 22 +- 3 files changed, 283 insertions(+), 104 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index cde2bffce..3cbf96bc6 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -152,6 +152,7 @@ -- @field #boolean ICLSon Automatic ICLS is activated. -- @field #number ICLSchannel ICLS channel. -- @field #string ICLSmorse ICLS morse code, e.g. "STN". +-- @field #AIRBOSS.Radio PilotRadio Radio for Pilot calls. -- @field #AIRBOSS.Radio LSORadio Radio for LSO calls. -- @field #number LSOFreq LSO radio frequency in MHz. -- @field #string LSOModu LSO radio modulation "AM" or "FM". @@ -1731,6 +1732,10 @@ AIRBOSS.Difficulty = { -- @field #table trapsheet Groove data table recorded every 0.5 seconds. -- @field #boolean trapon If true, save trap sheets. -- @field #string debriefschedulerID Debrief scheduler ID. +-- +-- @field Sound.SRS#MSRS SRS +-- @field Sound.SRS#MSRSQUEUE SRSQ +-- -- @extends #AIRBOSS.FlightGroup --- Main group level radio menu: F10 Other/Airboss. @@ -1743,7 +1748,7 @@ AIRBOSS.MenuF10Root = nil --- Airboss class version. -- @field #string version -AIRBOSS.version = "1.3.0" +AIRBOSS.version = "1.3.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -3043,12 +3048,56 @@ function AIRBOSS:SetBeaconRefresh( TimeInterval ) return self end +--- Set up SRS for usage without sound files +-- @param #AIRBOSS self +-- @param #string PathToSRS Path to SRS folder, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone". +-- @param #number Port Port of the SRS server, defaults to 5002. +-- @param #string Culture (Optional) Culture, defaults to "en-US". +-- @param #string Gender (Optional) Gender, e.g. "male" or "female". Defaults to "male". +-- @param #string Voice (Optional) Set to use a specific voice. Will **override gender and culture** settings. +-- @param #string GoogleCreds (Optional) Path to Google credentials, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourgooglekey.json". +-- @param #number Volume (Optional) E.g. 0.75. Defaults to 1.0 (loudest). +-- @param #table AltBackend (Optional) See MSRS for details. +-- @return #AIRBOSS self +function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volume,AltBackend) + -- SRS + local Frequency = self.MarshalRadio.frequency + local Modulation = self.MarshalRadio.modulation + self.SRS = MSRS:New(PathToSRS,Frequency,Modulation,Volume,AltBackend) + self.SRS:SetCoalition(self:GetCoalition()) + self.SRS:SetCoordinate(self:GetCoordinate()) + self.SRS:SetCulture(Culture or "en-US") + --self.SRS:SetFrequencies(Frequencies) + self.SRS:SetGender(Gender or "male") + self.SRS:SetPath(PathToSRS) + self.SRS:SetPort(Port or 5002) + self.SRS:SetLabel(self.MarshalRadio.alias or "AIRBOSS") + --self.SRS:SetModulations(Modulations) + if GoogleCreds then + self.SRS:SetGoogle(GoogleCreds) + end + if Voice then + self.SRS:SetVoice(Voice) + end + self.SRS:SetVolume(Volume or 1.0) + -- SRSQUEUE + self.SRSQ = MSRSQUEUE:New("AIRBOSS") + self.SRSQ:SetTransmitOnlyWithPlayers(true) + if not self.PilotRadio then + self:SetSRSPilotVoice() + end + return self +end + --- Set LSO radio frequency and modulation. Default frequency is 264 MHz AM. -- @param #AIRBOSS self -- @param #number Frequency (Optional) Frequency in MHz. Default 264 MHz. -- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM". +-- @param #string Voice (Optional) SRS specific voice +-- @param #string Gender (Optional) SRS specific gender +-- @param #string Culture (Optional) SRS specific culture -- @return #AIRBOSS self -function AIRBOSS:SetLSORadio( Frequency, Modulation ) +function AIRBOSS:SetLSORadio( Frequency, Modulation, Voice, Gender, Culture ) self.LSOFreq = (Frequency or 264) Modulation = Modulation or "AM" @@ -3063,6 +3112,9 @@ function AIRBOSS:SetLSORadio( Frequency, Modulation ) self.LSORadio.frequency = self.LSOFreq self.LSORadio.modulation = self.LSOModu self.LSORadio.alias = "LSO" + self.LSORadio.voice = Voice + self.LSORadio.gender = Gender or "male" + self.LSORadio.culture = Culture or "en-US" return self end @@ -3071,8 +3123,11 @@ end -- @param #AIRBOSS self -- @param #number Frequency (Optional) Frequency in MHz. Default 305 MHz. -- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM". +-- @param #string Voice (Optional) SRS specific voice +-- @param #string Gender (Optional) SRS specific gender +-- @param #string Culture (Optional) SRS specific culture -- @return #AIRBOSS self -function AIRBOSS:SetMarshalRadio( Frequency, Modulation ) +function AIRBOSS:SetMarshalRadio( Frequency, Modulation, Voice, Gender, Culture ) self.MarshalFreq = Frequency or 305 Modulation = Modulation or "AM" @@ -3087,6 +3142,9 @@ function AIRBOSS:SetMarshalRadio( Frequency, Modulation ) self.MarshalRadio.frequency = self.MarshalFreq self.MarshalRadio.modulation = self.MarshalModu self.MarshalRadio.alias = "MARSHAL" + self.MarshalRadio.voice = Voice + self.MarshalRadio.gender = Gender or "male" + self.MarshalRadio.culture = Culture or "en-US" return self end @@ -14588,59 +14646,129 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p if radio == nil or call == nil then return end - - -- Create a new radio transmission item. - local transmission = {} -- #AIRBOSS.Radioitem - - transmission.radio = radio - transmission.call = call - transmission.Tplay = timer.getAbsTime() + (delay or 0) - transmission.interval = interval - transmission.isplaying = false - transmission.Tstarted = nil - transmission.loud = loud and call.loud - - -- Player onboard number if sender has one. - if self:_IsOnboard( call.modexsender ) then - self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall ) - end - - -- Play onboard number if receiver has one. - if self:_IsOnboard( call.modexreceiver ) then - self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall ) - end - - -- Add transmission to the right queue. - local caller = "" - if radio.alias == "LSO" then - - table.insert( self.RQLSO, transmission ) - - caller = "LSOCall" - - -- Schedule radio queue checks. - if not self.RQLid then - self:T( self.lid .. string.format( "Starting LSO radio queue." ) ) - self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 ) + + if not self.SRS then + + -- Create a new radio transmission item. + local transmission = {} -- #AIRBOSS.Radioitem + + transmission.radio = radio + transmission.call = call + transmission.Tplay = timer.getAbsTime() + (delay or 0) + transmission.interval = interval + transmission.isplaying = false + transmission.Tstarted = nil + transmission.loud = loud and call.loud + + -- Player onboard number if sender has one. + if self:_IsOnboard( call.modexsender ) then + self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall ) end - - elseif radio.alias == "MARSHAL" then - - table.insert( self.RQMarshal, transmission ) - - caller = "MarshalCall" - - if not self.RQMid then - self:T( self.lid .. string.format( "Starting Marhal radio queue." ) ) - self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 ) + + -- Play onboard number if receiver has one. + if self:_IsOnboard( call.modexreceiver ) then + self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall ) end - + + -- Add transmission to the right queue. + local caller = "" + if radio.alias == "LSO" then + + table.insert( self.RQLSO, transmission ) + + caller = "LSOCall" + + -- Schedule radio queue checks. + if not self.RQLid then + self:T( self.lid .. string.format( "Starting LSO radio queue." ) ) + self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 ) + end + + elseif radio.alias == "MARSHAL" then + + table.insert( self.RQMarshal, transmission ) + + caller = "MarshalCall" + + if not self.RQMid then + self:T( self.lid .. string.format( "Starting Marhal radio queue." ) ) + self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 ) + end + + end + + -- Append radio click sound at the end of the transmission. + if click then + self:RadioTransmission( radio, self[caller].CLICK, false, delay ) + end + + else + -- SRS transmission + + local frequency = self.MarshalRadio.frequency + local modulation = self.MarshalRadio.modulation + local voice = nil + local gender = nil + local culture = nil + + if radio.alias == "MARSHAL" or radio.alias == "AIRBOSS" then + voice = self.MarshalRadio.voice + gender = self.MarshalRadio.gender + culture = self.MarshalRadio.culture + end + if radio.alias == "LSO" then + frequency = self.LSORadio.frequency + modulation = self.LSORadio.modulation + voice = self.LSORadio.voice + gender = self.LSORadio.gender + culture = self.LSORadio.culture + end + if pilotcall then + voice = self.PilotRadio.voice + gender = self.PilotRadio.gender + culture = self.PilotRadio.culture + radio.alias = "PILOT" + end + if not radio.alias then + -- TODO - what freq to use here? + frequency = 243 + modulation = radio.modulation.AM + radio.alias = "AIRBOSS" + end + + local volume = nil + + if loud then + volume = 1.0 + end + + --local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle + local text = call.subtitle + self:I(text) + local srstext = string.gsub(text,"\n","; ") + self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, tstart, 0.1, subgroups, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) end +end - -- Append radio click sound at the end of the transmission. - if click then - self:RadioTransmission( radio, self[caller].CLICK, false, delay ) +--- Set SRS voice for the pilot calls. +-- @param #AIRBOSS self +-- @param #string Voice (Optional) SRS specific voice +-- @param #string Gender (Optional) SRS specific gender +-- @param #string Culture (Optional) SRS specific culture +-- @return #AIRBOSS self +function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture ) + + self.PilotRadio = {} -- #AIRBOSS.Radio + self.PilotRadio.alias = "PILOT" + self.PilotRadio.voice = Voice or MSRS.Voices.Microsoft.David + self.PilotRadio.gender = Gender or "male" + self.PilotRadio.culture = Culture or "en-US" + + if (not Voice) and self.SRS and self.SRS.google then + self.PilotRadio.voice = MSRS.Voices.Google.Standard.en_US_Standard_J end + + return self end --- Check if a call needs a subtitle because the complete voice overs are not available. @@ -14920,49 +15048,81 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio -- SCHEDULER:New(nil, self.MessageToPlayer, {self, playerData, message, sender, receiver, duration, clear}, delay) self:ScheduleOnce( delay, self.MessageToPlayer, self, playerData, message, sender, receiver, duration, clear ) else - - -- Wait until previous sound finished. - local wait = 0 - - -- Onboard number to get the attention. - if receiver == playerData.onboard then - - -- Which voice over number to use. - if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then - - -- User sound of board number. - wait = wait + self:_Number2Sound( playerData, sender, receiver ) - + + if not self.SRS then + -- Wait until previous sound finished. + local wait = 0 + + -- Onboard number to get the attention. + if receiver == playerData.onboard then + + -- Which voice over number to use. + if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then + + -- User sound of board number. + wait = wait + self:_Number2Sound( playerData, sender, receiver ) + + end end + + -- Negative. + if string.find( text:lower(), "negative" ) then + local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" ) + USERSOUND:New( filename ):ToGroup( playerData.group, wait ) + wait = wait + self.MarshalCall.NEGATIVE.duration + end + + -- Affirm. + if string.find( text:lower(), "affirm" ) then + local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" ) + USERSOUND:New( filename ):ToGroup( playerData.group, wait ) + wait = wait + self.MarshalCall.AFFIRMATIVE.duration + end + + -- Roger. + if string.find( text:lower(), "roger" ) then + local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" ) + USERSOUND:New( filename ):ToGroup( playerData.group, wait ) + wait = wait + self.MarshalCall.ROGER.duration + end + + -- Play click sound to end message. + if wait > 0 then + local filename = self:_RadioFilename( self.MarshalCall.CLICK ) + USERSOUND:New( filename ):ToGroup( playerData.group, wait ) + end + else + -- SRS transmission + local frequency = self.MarshalRadio.frequency + local modulation = self.MarshalRadio.modulation + local voice = nil + local gender = nil + local culture = nil + + if sender == "MARSHAL" or sender == "AIRBOSS" then + voice = self.MarshalRadio.voice + gender = self.MarshalRadio.gender + culture = self.MarshalRadio.culture + end + if sender == "LSO" then + frequency = self.LSORadio.frequency + modulation = self.LSORadio.modulation + voice = self.LSORadio.voice + gender = self.LSORadio.gender + culture = self.LSORadio.culture + elseif not sender then + -- TODO - what freq to use here? + frequency = 243 + modulation = radio.modulation.AM + sender = "AIRBOSS" + end + + -- local text = tostring(receiver or "").."; "..sender.."; "..text + --local text = sender.."; "..text + self:I(text) + local srstext = string.gsub(text,"\n","; ") + self.SRSQ:NewTransmission(srstext,duration,self.SRS,tstart,0.1,subgroups,subtitle,subduration,frequency,modulation,gender,culture,voice,volume,sender) end - - -- Negative. - if string.find( text:lower(), "negative" ) then - local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" ) - USERSOUND:New( filename ):ToGroup( playerData.group, wait ) - wait = wait + self.MarshalCall.NEGATIVE.duration - end - - -- Affirm. - if string.find( text:lower(), "affirm" ) then - local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" ) - USERSOUND:New( filename ):ToGroup( playerData.group, wait ) - wait = wait + self.MarshalCall.AFFIRMATIVE.duration - end - - -- Roger. - if string.find( text:lower(), "roger" ) then - local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" ) - USERSOUND:New( filename ):ToGroup( playerData.group, wait ) - wait = wait + self.MarshalCall.ROGER.duration - end - - -- Play click sound to end message. - if wait > 0 then - local filename = self:_RadioFilename( self.MarshalCall.CLICK ) - USERSOUND:New( filename ):ToGroup( playerData.group, wait ) - end - -- Text message to player client. if playerData.client then MESSAGE:New( text, duration, sender, clear ):ToClient( playerData.client ) @@ -16301,7 +16461,7 @@ function AIRBOSS:_RequestSpinning( _unitName ) -- Some advice. if playerData.difficulty == AIRBOSS.Difficulty.EASY then local text = "Climb to 1200 feet and proceed to the initial again." - self:MessageToPlayer( playerData, text, "INSTRUCTOR", "" ) + self:MessageToPlayer( playerData, text, "AIRBOSS", "" ) end return diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 66e942b90..62f2f1c2f 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1347,6 +1347,11 @@ MSRSQUEUE = { -- @field #number interval Interval in seconds before next transmission. -- @field #boolean TransmitOnlyWithPlayers If true, only transmit if there are alive Players. -- @field Core.Set#SET_CLIENT PlayerSet PlayerSet created when TransmitOnlyWithPlayers == true +-- @field #string gender Voice gender +-- @field #string culture Voice culture +-- @field #string voice Voice if any +-- @field #number volume Volume +-- @field #string label Label to be used --- Create a new MSRSQUEUE object for a given radio frequency/modulation. -- @param #MSRSQUEUE self @@ -1426,8 +1431,13 @@ end -- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec. -- @param #number frequency Radio frequency if other than MSRS default. -- @param #number modulation Radio modulation if other then MSRS default. +-- @param #string gender Gender of the voice +-- @param #string culture Culture of the voice +-- @param #string voice Specific voice +-- @param #number volume Volume setting +-- @param #string label Label to be used -- @return #MSRSQUEUE.Transmission Radio transmission table. -function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation) +function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation, gender, culture, voice, volume, label) if self.TransmitOnlyWithPlayers then if self.PlayerSet and self.PlayerSet:CountAlive() == 0 then @@ -1462,6 +1472,11 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr else transmission.subduration=0 --nil end + transmission.gender = gender + transmission.culture = culture + transmission.voice = voice + transmission.gender = volume + transmission.label = label -- Add transmission to queue. self:AddTransmission(transmission) @@ -1475,7 +1490,7 @@ end function MSRSQUEUE:Broadcast(transmission) if transmission.frequency then - transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, Gender, Culture, Voice, Volume, Label) + transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label) else transmission.msrs:PlayText(transmission.text) end diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index f42c1c1ee..289d13155 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -35,6 +35,7 @@ do -- @field #number id -- @field #number side -- @field #number slot +-- @field #numner timestamp --- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) -- with some added FSM functions and options to block/unblock players in MP environments. @@ -205,7 +206,7 @@ function NET:_EventHandler(EventData) -- Joining if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then - self:T(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid) + self:T(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid.." | Event ID: "..data.id) -- Check for blockages local blocked = self:IsAnyBlocked(ucid,name,PlayerID,PlayerSide,PlayerSlot) @@ -213,15 +214,18 @@ function NET:_EventHandler(EventData) -- block pilot local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) else - self.KnownPilots[name] = { - name = name, - ucid = ucid, - id = PlayerID, - side = PlayerSide, - slot = PlayerSlot, - } local client = CLIENT:FindByPlayerName(name) or data.IniUnit - self:__PlayerJoined(1,client,name) + if not self.KnownPilots[name] or (self.KnownPilots[name] and TNow-self.KnownPilots[name].timestamp > 3) then + self:__PlayerJoined(1,client,name) + self.KnownPilots[name] = { + name = name, + ucid = ucid, + id = PlayerID, + side = PlayerSide, + slot = PlayerSlot, + timestamp = TNow, + } + end return self end end From 0d40b36613ef214b30107804e6696d1761177e3c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 17 Jun 2023 15:01:57 +0200 Subject: [PATCH 282/603] #AIRBOSS * Added option to set Airboss voice for SRS etc * Nicer read out of weather info and carrier info --- Moose Development/Moose/Ops/Airboss.lua | 154 +++++++++++++++++++----- 1 file changed, 125 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 3cbf96bc6..f577ac151 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -159,6 +159,9 @@ -- @field #AIRBOSS.Radio MarshalRadio Radio for carrier calls. -- @field #number MarshalFreq Marshal radio frequency in MHz. -- @field #string MarshalModu Marshal radio modulation "AM" or "FM". +-- @field #AIRBOSS.Radio AirbossRadio Radio for carrier calls. +-- @field #number AirbossFreq Airboss radio frequency in MHz. +-- @field #string AirbossModu Airboss radio modulation "AM" or "FM". -- @field #number TowerFreq Tower radio frequency in MHz. -- @field Core.Scheduler#SCHEDULER radiotimer Radio queue scheduler. -- @field Core.Zone#ZONE_UNIT zoneCCA Carrier controlled area (CCA), i.e. a zone of 50 NM radius around the carrier. @@ -1881,6 +1884,7 @@ function AIRBOSS:New( carriername, alias ) -- Set up Airboss radio. self:SetMarshalRadio() + self:SetAirbossRadio() -- Set up LSO radio. self:SetLSORadio() @@ -3052,17 +3056,17 @@ end -- @param #AIRBOSS self -- @param #string PathToSRS Path to SRS folder, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone". -- @param #number Port Port of the SRS server, defaults to 5002. --- @param #string Culture (Optional) Culture, defaults to "en-US". --- @param #string Gender (Optional) Gender, e.g. "male" or "female". Defaults to "male". --- @param #string Voice (Optional) Set to use a specific voice. Will **override gender and culture** settings. +-- @param #string Culture (Optional, Airboss Culture) Culture, defaults to "en-US". +-- @param #string Gender (Optional, Airboss Gender) Gender, e.g. "male" or "female". Defaults to "male". +-- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings. -- @param #string GoogleCreds (Optional) Path to Google credentials, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourgooglekey.json". -- @param #number Volume (Optional) E.g. 0.75. Defaults to 1.0 (loudest). -- @param #table AltBackend (Optional) See MSRS for details. -- @return #AIRBOSS self function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volume,AltBackend) -- SRS - local Frequency = self.MarshalRadio.frequency - local Modulation = self.MarshalRadio.modulation + local Frequency = self.AirbossRadio.frequency + local Modulation = self.AirbossRadio.modulation self.SRS = MSRS:New(PathToSRS,Frequency,Modulation,Volume,AltBackend) self.SRS:SetCoalition(self:GetCoalition()) self.SRS:SetCoordinate(self:GetCoordinate()) @@ -3071,7 +3075,7 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum self.SRS:SetGender(Gender or "male") self.SRS:SetPath(PathToSRS) self.SRS:SetPort(Port or 5002) - self.SRS:SetLabel(self.MarshalRadio.alias or "AIRBOSS") + self.SRS:SetLabel(self.AirbossRadio.alias or "AIRBOSS") --self.SRS:SetModulations(Modulations) if GoogleCreds then self.SRS:SetGoogle(GoogleCreds) @@ -3119,7 +3123,47 @@ function AIRBOSS:SetLSORadio( Frequency, Modulation, Voice, Gender, Culture ) return self end ---- Set carrier radio frequency and modulation. Default frequency is 305 MHz AM. +--- Set Airboss radio frequency and modulation. Default frequency is Tower frequency. +-- @param #AIRBOSS self +-- @param #number Frequency (Optional) Frequency in MHz. Default frequency is Tower frequency. +-- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM". +-- @param #string Voice (Optional) SRS specific voice +-- @param #string Gender (Optional) SRS specific gender +-- @param #string Culture (Optional) SRS specific culture +-- @return #AIRBOSS self +-- @usage +-- -- Set single frequency +-- myairboss:SetAirbossRadio(127.5,"AM",MSRS.Voices.Google.Standard.en_GB_Standard_F) +-- +-- -- Set multiple frequencies, note you **need** to pass one modulation per frequency given! +-- myairboss:SetAirbossRadio({127.5,243},{radio.modulation.AM,radio.modulation.AM},MSRS.Voices.Google.Standard.en_GB_Standard_F) +function AIRBOSS:SetAirbossRadio( Frequency, Modulation, Voice, Gender, Culture ) + + self.AirbossFreq = Frequency or self:_GetTowerFrequency() or 127.5 + Modulation = Modulation or "AM" + + if type(Modulation) == "table" then + self.AirbossModu = Modulation + else + if Modulation == "FM" then + self.AirbossModu = radio.modulation.FM + else + self.AirbossModu = radio.modulation.AM + end + end + + self.AirbossRadio = {} -- #AIRBOSS.Radio + self.AirbossRadio.frequency = self.AirbossFreq + self.AirbossRadio.modulation = self.AirbossModu + self.AirbossRadio.alias = "AIRBOSS" + self.AirbossRadio.voice = Voice + self.AirbossRadio.gender = Gender or "male" + self.AirbossRadio.culture = Culture or "en-US" + + return self +end + +--- Set Marshal radio frequency and modulation. Default frequency is 305 MHz AM. -- @param #AIRBOSS self -- @param #number Frequency (Optional) Frequency in MHz. Default 305 MHz. -- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM". @@ -14711,11 +14755,20 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p local gender = nil local culture = nil - if radio.alias == "MARSHAL" or radio.alias == "AIRBOSS" then + if radio.alias == "AIRBOSS" then + frequency = self.AirbossRadio.frequency + modulation = self.AirbossRadio.modulation + voice = self.AirbossRadio.voice + gender = self.AirbossRadio.gender + culture = self.AirbossRadio.culture + end + + if radio.alias == "MARSHAL" then voice = self.MarshalRadio.voice gender = self.MarshalRadio.gender culture = self.MarshalRadio.culture end + if radio.alias == "LSO" then frequency = self.LSORadio.frequency modulation = self.LSORadio.modulation @@ -14723,16 +14776,18 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p gender = self.LSORadio.gender culture = self.LSORadio.culture end + if pilotcall then voice = self.PilotRadio.voice gender = self.PilotRadio.gender culture = self.PilotRadio.culture radio.alias = "PILOT" end + if not radio.alias then -- TODO - what freq to use here? - frequency = 243 - modulation = radio.modulation.AM + frequency = self.AirbossRadio.frequency + modulation = self.AirbossRadio.modulation radio.alias = "AIRBOSS" end @@ -14744,8 +14799,8 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p --local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle local text = call.subtitle - self:I(text) - local srstext = string.gsub(text,"\n","; ") + self:I(self.lid..text) + local srstext = self:_GetNiceSRSText(text) self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, tstart, 0.1, subgroups, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) end end @@ -15014,6 +15069,37 @@ function AIRBOSS:_RadioFilename( call, loud, channel ) return filename end +--- Format text into SRS friendly string +-- @param #AIRBOSS self +-- @param #string text +-- @return #string text +function AIRBOSS:_GetNiceSRSText(text) + text = string.gsub(text,"================================\n","") + text = string.gsub(text,"||","parallel") + text = string.gsub(text,"==","perpendicular") + text = string.gsub(text,"BRC","Base recovery") + text = string.gsub(text,"#","Number") + text = string.gsub(text,"°C","° Celsius") + text = string.gsub(text,"°"," degrees") + text = string.gsub(text," FB "," Final bearing ") + text = string.gsub(text," ops"," operations ") + text = string.gsub(text," kts"," knots") + text = string.gsub(text,"TACAN","Tackan") + text = string.gsub(text,"ICLS","I.C.L.S.") + text = string.gsub(text,"LSO","L.S.O.") + text = string.gsub(text,"inHg","inches of Mercury") + text = string.gsub(text,"QFE","Q.F.E.") + text = string.gsub(text,"hPa","hecto pascal") + text = string.gsub(text," NM"," nautical miles") + text = string.gsub(text," ft"," feet") + text = string.gsub(text,"A/C","aircraft") + text = string.gsub(text,"%.000"," dot zero") + text = string.gsub(text,"00"," double zero") + text = string.gsub(text," 0 "," zero " ) + text = string.gsub(text,"\n","; ") + return text +end + --- Send text message to player client. -- Message format will be "SENDER: RECCEIVER, MESSAGE". -- @param #AIRBOSS self @@ -15025,7 +15111,7 @@ end -- @param #boolean clear If true, clear screen from previous messages. -- @param #number delay Delay in seconds, before the message is displayed. function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duration, clear, delay ) - + self:I({sender,receiver,message}) if playerData and message and message ~= "" then -- Default duration. @@ -15095,32 +15181,42 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio -- SRS transmission local frequency = self.MarshalRadio.frequency local modulation = self.MarshalRadio.modulation - local voice = nil - local gender = nil - local culture = nil + local voice = self.MarshalRadio.voice + local gender = self.MarshalRadio.gender + local culture = self.MarshalRadio.culture - if sender == "MARSHAL" or sender == "AIRBOSS" then - voice = self.MarshalRadio.voice - gender = self.MarshalRadio.gender - culture = self.MarshalRadio.culture + if not sender then sender = "AIRBOSS" end + + if string.find(sender,"AIRBOSS" ) then + frequency = self.AirbossRadio.frequency + modulation = self.AirbossRadio.modulation + voice = self.AirbossRadio.voice + gender = self.AirbossRadio.gender + culture = self.AirbossRadio.culture end + + --if sender == "MARSHAL" then + --voice = self.MarshalRadio.voice + --gender = self.MarshalRadio.gender + --culture = self.MarshalRadio.culture + --end + if sender == "LSO" then frequency = self.LSORadio.frequency modulation = self.LSORadio.modulation voice = self.LSORadio.voice gender = self.LSORadio.gender culture = self.LSORadio.culture - elseif not sender then + --elseif not sender then -- TODO - what freq to use here? - frequency = 243 - modulation = radio.modulation.AM - sender = "AIRBOSS" + --frequency = self.AirbossRadio.frequency + --modulation = self.AirbossRadio.modulation + --sender = "AIRBOSS" end - - -- local text = tostring(receiver or "").."; "..sender.."; "..text - --local text = sender.."; "..text - self:I(text) - local srstext = string.gsub(text,"\n","; ") + + self:I(self.lid..text) + self:I({sender,frequency,modulation,voice}) + local srstext = self:_GetNiceSRSText(text) self.SRSQ:NewTransmission(srstext,duration,self.SRS,tstart,0.1,subgroups,subtitle,subduration,frequency,modulation,gender,culture,voice,volume,sender) end -- Text message to player client. From 09f0c9e06977fe057cdb0c27211c45e7c4045c06 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 18 Jun 2023 12:18:09 +0200 Subject: [PATCH 283/603] #AIRBOSS --- Moose Development/Moose/Ops/Airboss.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index f577ac151..fc367d1e2 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -15078,7 +15078,8 @@ function AIRBOSS:_GetNiceSRSText(text) text = string.gsub(text,"||","parallel") text = string.gsub(text,"==","perpendicular") text = string.gsub(text,"BRC","Base recovery") - text = string.gsub(text,"#","Number") + --text = string.gsub(text,"#","Number") + text = string.gsub(text,"%((%a+)%)","Morse %1") text = string.gsub(text,"°C","° Celsius") text = string.gsub(text,"°"," degrees") text = string.gsub(text," FB "," Final bearing ") @@ -15093,6 +15094,7 @@ function AIRBOSS:_GetNiceSRSText(text) text = string.gsub(text," NM"," nautical miles") text = string.gsub(text," ft"," feet") text = string.gsub(text,"A/C","aircraft") + text = string.gsub(text,"(#[%a%d%p%s]+)\n","") text = string.gsub(text,"%.000"," dot zero") text = string.gsub(text,"00"," double zero") text = string.gsub(text," 0 "," zero " ) From d005064c28936b1a0f13de4af9bf9b6e599dc884 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 18 Jun 2023 13:12:35 +0200 Subject: [PATCH 284/603] #PLAYERTASK * Menu build finetuning --- Moose Development/Moose/Ops/PlayerTask.lua | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 50fc27582..16b563226 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -2195,8 +2195,10 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) --local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext) local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext) self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation) - if EventData.IniUnitName then - self:_BuildMenus(CLIENT:FindByName(EventData.IniUnitName)) + if EventData.IniPlayerName then + self.PlayerMenu[EventData.IniPlayerName] = nil + --self:_BuildMenus(CLIENT:FindByName(EventData.IniUnitName)) + self:_BuildMenus(CLIENT:FindByPlayerName(EventData.IniPlayerName)) end end end @@ -3460,7 +3462,14 @@ end -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) self:T(self.lid.."_BuildMenus") - + + if self.MenuBuildLocked then + self:ScheduleOnce(2,self._BuildMenus,self,Client,enforced,fromsuccess) + return self + else + self.MenuBuildLocked = true + end + local clients = self.ClientSet:GetAliveSet() local joinorabort = false local timedbuild = false @@ -3653,6 +3662,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) end end end + self.MenuBuildLocked = false return self end From c88bb3bbdb06095be5efce0cd240d2afe233bdc8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 18 Jun 2023 13:29:25 +0200 Subject: [PATCH 285/603] #PLAYERTASKCONTROLLER * Menu build lock --- Moose Development/Moose/Ops/PlayerTask.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 16b563226..a7c127b1f 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1536,7 +1536,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.60" +PLAYERTASKCONTROLLER.version="0.1.60a" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -3463,11 +3463,11 @@ end function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) self:T(self.lid.."_BuildMenus") - if self.MenuBuildLocked then + if self.MenuBuildLocked and (timer.getAbsTime() - self.MenuBuildLocked < 2) then self:ScheduleOnce(2,self._BuildMenus,self,Client,enforced,fromsuccess) return self else - self.MenuBuildLocked = true + self.MenuBuildLocked = timer.getAbsTime() end local clients = self.ClientSet:GetAliveSet() From 925ac7907b76c9843977063d0e9079149fc4d745 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 21 Jun 2023 10:25:30 +0200 Subject: [PATCH 286/603] #AIRBOSS SRS --- Moose Development/Moose/Ops/Airboss.lua | 38 +++++++++++++++++++++++-- Moose Development/Moose/Sound/SRS.lua | 2 +- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index fc367d1e2..ef6b6b5ca 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -1751,7 +1751,7 @@ AIRBOSS.MenuF10Root = nil --- Airboss class version. -- @field #string version -AIRBOSS.version = "1.3.1" +AIRBOSS.version = "1.3.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -11550,8 +11550,42 @@ end -- @return #number Carrier heading in degrees. function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) + local function adjustDegreesForWindSpeed(windSpeed) + local degreesAdjustment = 0 + -- the windspeeds are in m/s + + -- +0 degrees at 15m/s = 37kts + -- +0 degrees at 14m/s = 35kts + -- +0 degrees at 13m/s = 33kts + -- +4 degrees at 12m/s = 31kts + -- +4 degrees at 11m/s = 29kts + -- +4 degrees at 10m/s = 27kts + -- +4 degrees at 9m/s = 27kts + -- +4 degrees at 8m/s = 27kts + -- +8 degrees at 7m/s = 27kts + -- +8 degrees at 6m/s = 27kts + -- +8 degrees at 5m/s = 26kts + -- +20 degrees at 4m/s = 26kts + -- +20 degrees at 3m/s = 26kts + -- +30 degrees at 2m/s = 26kts 1s + + if windSpeed > 0 and windSpeed < 3 then + degreesAdjustment = 30 + elseif windSpeed >= 3 and windSpeed < 5 then + degreesAdjustment = 20 + elseif windSpeed >= 5 and windSpeed < 8 then + degreesAdjustment = 8 + elseif windSpeed >= 8 and windSpeed < 13 then + degreesAdjustment = 4 + elseif windSpeed >= 13 then + degreesAdjustment = 0 + end + + return degreesAdjustment + end + -- Get direction the wind is blowing from. This is where we want to go. - local windfrom, vwind = self:GetWind( nil, nil, coord ) + local windfrom, vwind = self:GetWind( nil, nil, coord ) + adjustDegreesForWindSpeed(vwind) -- Actually, we want the runway in the wind. local intowind = windfrom - self.carrierparam.rwyangle diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 62f2f1c2f..0665b3885 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -861,7 +861,7 @@ end -- @param #string command Command to executer -- @return #number Return value of os.execute() command. function MSRS:_ExecCommand(command) - + self:T("SRS TTS command="..command) -- Create a tmp file. local filename=os.getenv('TMP').."\\MSRS-"..STTS.uuid()..".bat" From 24c58acf858decb8d8e6abc822e6c713380a835a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 22 Jun 2023 09:34:21 +0200 Subject: [PATCH 287/603] #AIRBOSS * Bugfix --- Moose Development/Moose/Ops/Airboss.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index a641fb4ee..005f5b614 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -11585,7 +11585,13 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) end -- Get direction the wind is blowing from. This is where we want to go. - local windfrom, vwind = self:GetWind( nil, nil, coord ) + adjustDegreesForWindSpeed(vwind) + local windfrom, vwind = self:GetWind( nil, nil, coord ) + + --self:I("windfrom="..windfrom.." vwind="..vwind) + + vwind = vwind + adjustDegreesForWindSpeed(vwind) + + --self:I("windfrom="..windfrom.." (c)vwind="..vwind) -- Actually, we want the runway in the wind. local intowind = windfrom - self.carrierparam.rwyangle @@ -17348,7 +17354,7 @@ function AIRBOSS:_DisplayCarrierInfo( _unitname ) state = "Deck closed" end if self.turning then - state = state .. " (turning currently)" + state = state .. " (currently turning)" end -- Message text. From abf694aa81989b5be90b13763e642c1ed6376133 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 22 Jun 2023 12:16:03 +0200 Subject: [PATCH 288/603] # Docu fixes --- Moose Development/Moose/.vscode/settings.json | 9 ++- Moose Development/Moose/AI/AI_A2A_Cap.lua | 2 +- .../Moose/AI/AI_A2A_Dispatcher.lua | 8 +- Moose Development/Moose/AI/AI_A2A_Gci.lua | 2 +- Moose Development/Moose/AI/AI_A2A_Patrol.lua | 2 +- Moose Development/Moose/AI/AI_A2G_BAI.lua | 2 +- Moose Development/Moose/AI/AI_A2G_CAS.lua | 2 +- .../Moose/AI/AI_A2G_Dispatcher.lua | 44 +++++----- Moose Development/Moose/AI/AI_A2G_SEAD.lua | 2 +- Moose Development/Moose/AI/AI_Air.lua | 30 +++---- .../Moose/AI/AI_Air_Dispatcher.lua | 38 ++++----- Moose Development/Moose/AI/AI_Air_Engage.lua | 18 ++--- Moose Development/Moose/AI/AI_Air_Patrol.lua | 2 +- .../Moose/AI/AI_Air_Squadron.lua | 2 +- Moose Development/Moose/AI/AI_BAI.lua | 16 ++-- Moose Development/Moose/AI/AI_Balancer.lua | 14 ++-- Moose Development/Moose/AI/AI_CAP.lua | 18 ++--- Moose Development/Moose/AI/AI_CAS.lua | 16 ++-- Moose Development/Moose/AI/AI_Cargo.lua | 2 +- Moose Development/Moose/AI/AI_Cargo_APC.lua | 2 +- .../Moose/AI/AI_Cargo_Airplane.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher.lua | 6 +- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 2 +- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_Ship.lua | 2 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 8 +- Moose Development/Moose/AI/AI_Cargo_Ship.lua | 2 +- Moose Development/Moose/AI/AI_Escort.lua | 72 ++++++++--------- .../Moose/AI/AI_Escort_Dispatcher.lua | 8 +- .../Moose/AI/AI_Escort_Dispatcher_Request.lua | 8 +- .../Moose/AI/AI_Escort_Request.lua | 12 +-- Moose Development/Moose/AI/AI_Formation.lua | 16 ++-- Moose Development/Moose/AI/AI_Patrol.lua | 24 +++--- .../Moose/Actions/Act_Account.lua | 6 +- .../Moose/Actions/Act_Assist.lua | 2 +- Moose Development/Moose/Cargo/Cargo.lua | 10 +-- Moose Development/Moose/Cargo/CargoCrate.lua | 2 +- Moose Development/Moose/Cargo/CargoGroup.lua | 6 +- .../Moose/Cargo/CargoSlingload.lua | 2 +- Moose Development/Moose/Core/Base.lua | 6 +- Moose Development/Moose/Core/Database.lua | 4 +- Moose Development/Moose/Core/Event.lua | 2 +- Moose Development/Moose/Core/Fsm.lua | 6 +- Moose Development/Moose/Core/Goal.lua | 8 +- Moose Development/Moose/Core/Menu.lua | 22 ++--- Moose Development/Moose/Core/Point.lua | 4 +- Moose Development/Moose/Core/Report.lua | 2 +- Moose Development/Moose/Core/Set.lua | 80 +++++++++---------- Moose Development/Moose/Core/Settings.lua | 30 +++---- Moose Development/Moose/Core/Spawn.lua | 8 +- Moose Development/Moose/Core/SpawnStatic.lua | 2 +- Moose Development/Moose/Core/Spot.lua | 8 +- Moose Development/Moose/Core/UserFlag.lua | 2 +- Moose Development/Moose/Core/Velocity.lua | 4 +- Moose Development/Moose/Core/Zone.lua | 16 ++-- .../Moose/Core/Zone_Detection.lua | 2 +- Moose Development/Moose/DCS.lua | 78 +++++++++--------- .../Moose/Functional/ATC_Ground.lua | 36 ++++----- .../Moose/Functional/CleanUp.lua | 9 ++- .../Moose/Functional/Designate.lua | 16 ++-- .../Moose/Functional/Detection.lua | 30 +++---- .../Moose/Functional/DetectionZones.lua | 10 +-- Moose Development/Moose/Functional/Escort.lua | 36 ++++----- Moose Development/Moose/Functional/Fox.lua | 2 +- .../Moose/Functional/MissileTrainer.lua | 4 +- .../Moose/Functional/Movement.lua | 2 +- .../Moose/Functional/Scoring.lua | 2 +- Moose Development/Moose/Functional/Sead.lua | 4 +- Moose Development/Moose/Functional/Shorad.lua | 4 +- .../Moose/Functional/Warehouse.lua | 2 +- .../Moose/Functional/ZoneCaptureCoalition.lua | 16 ++-- .../Moose/Functional/ZoneGoal.lua | 4 +- .../Moose/Functional/ZoneGoalCargo.lua | 4 +- .../Moose/Functional/ZoneGoalCoalition.lua | 4 +- Moose Development/Moose/Ops/ATIS.lua | 5 +- Moose Development/Moose/Ops/Auftrag.lua | 3 +- Moose Development/Moose/Ops/Awacs.lua | 19 +++-- Moose Development/Moose/Ops/CSAR.lua | 17 ++-- Moose Development/Moose/Ops/CTLD.lua | 2 +- Moose Development/Moose/Ops/Cohort.lua | 2 +- Moose Development/Moose/Ops/PlayerRecce.lua | 4 +- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- Moose Development/Moose/Sound/SoundOutput.lua | 6 +- Moose Development/Moose/Sound/UserSound.lua | 2 +- .../Moose/Tasking/CommandCenter.lua | 12 +-- .../Moose/Tasking/DetectionManager.lua | 4 +- Moose Development/Moose/Tasking/Mission.lua | 14 ++-- Moose Development/Moose/Tasking/Task.lua | 10 +-- Moose Development/Moose/Tasking/TaskInfo.lua | 6 +- Moose Development/Moose/Tasking/Task_A2A.lua | 24 +++--- .../Moose/Tasking/Task_A2A_Dispatcher.lua | 2 +- Moose Development/Moose/Tasking/Task_A2G.lua | 22 ++--- .../Moose/Tasking/Task_CARGO.lua | 50 ++++++------ .../Moose/Tasking/Task_Capture_Dispatcher.lua | 2 +- .../Moose/Tasking/Task_Capture_Zone.lua | 10 +-- .../Moose/Tasking/Task_Cargo_CSAR.lua | 6 +- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 2 +- .../Moose/Tasking/Task_Cargo_Transport.lua | 4 +- .../Moose/Utilities/Routines.lua | 2 +- Moose Development/Moose/Utilities/Utils.lua | 4 +- Moose Development/Moose/Wrapper/Airbase.lua | 2 +- Moose Development/Moose/Wrapper/Client.lua | 2 +- .../Moose/Wrapper/Controllable.lua | 4 +- Moose Development/Moose/Wrapper/Group.lua | 4 +- .../Moose/Wrapper/Identifiable.lua | 2 +- Moose Development/Moose/Wrapper/Object.lua | 2 +- .../Moose/Wrapper/Positionable.lua | 8 +- Moose Development/Moose/Wrapper/Static.lua | 2 +- Moose Development/Moose/Wrapper/Unit.lua | 2 +- 110 files changed, 587 insertions(+), 571 deletions(-) diff --git a/Moose Development/Moose/.vscode/settings.json b/Moose Development/Moose/.vscode/settings.json index 13211d027..a8201d6ad 100644 --- a/Moose Development/Moose/.vscode/settings.json +++ b/Moose Development/Moose/.vscode/settings.json @@ -1,7 +1,14 @@ { "Lua.workspace.preloadFileSize": 1000, "Lua.diagnostics.disable": [ - "undefined-doc-name" + "undefined-doc-name", + "duplicate-set-field", + "trailing-space", + "need-check-nil", + "ambiguity-1", + "undefined-doc-param", + "redundant-parameter", + "param-type-mismatch" ], "Lua.diagnostics.globals": [ "BASE", diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 61b82e19e..011022576 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -11,7 +11,7 @@ -- @module AI.AI_A2A_Cap -- @image AI_Combat_Air_Patrol.JPG ---- @type AI_A2A_CAP +-- @type AI_A2A_CAP -- @extends AI.AI_Air_Patrol#AI_AIR_PATROL -- @extends AI.AI_Air_Engage#AI_AIR_ENGAGE diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 504d39985..9875352f2 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -3252,7 +3252,7 @@ do -- AI_A2A_DISPATCHER end end - --- @param #AI_A2A_DISPATCHER self + -- @param #AI_A2A_DISPATCHER self function AI_A2A_Fsm:onafterHome( Defender, From, Event, To, Action ) if Defender and Defender:IsAlive() then self:F( { "CAP Home", Defender:GetName() } ) @@ -3500,7 +3500,7 @@ do -- AI_A2A_DISPATCHER Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) end - --- @param #AI_A2A_DISPATCHER self + -- @param #AI_A2A_DISPATCHER self function Fsm:onafterLostControl( Defender, From, Event, To ) self:F( { "GCI LostControl", Defender:GetName() } ) self:GetParent( self ).onafterHome( self, Defender, From, Event, To ) @@ -3513,7 +3513,7 @@ do -- AI_A2A_DISPATCHER end end - --- @param #AI_A2A_DISPATCHER self + -- @param #AI_A2A_DISPATCHER self function Fsm:onafterHome( DefenderGroup, From, Event, To, Action ) self:F( { "GCI Home", DefenderGroup:GetName() } ) self:GetParent( self ).onafterHome( self, DefenderGroup, From, Event, To ) @@ -3944,7 +3944,7 @@ end do - --- @type AI_A2A_GCICAP + -- @type AI_A2A_GCICAP -- @extends #AI_A2A_DISPATCHER --- Create an automatic air defence system for a coalition setting up GCI and CAP air defenses. diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index 567ff55c2..0d099f5a1 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -13,7 +13,7 @@ ---- @type AI_A2A_GCI +-- @type AI_A2A_GCI -- @extends AI.AI_A2A#AI_A2A diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index 07fdd43c1..1bb2452c9 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -10,7 +10,7 @@ -- @image AI_Air_Patrolling.JPG ---- @type AI_A2A_PATROL +-- @type AI_A2A_PATROL -- @extends AI.AI_A2A#AI_A2A --- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}. diff --git a/Moose Development/Moose/AI/AI_A2G_BAI.lua b/Moose Development/Moose/AI/AI_A2G_BAI.lua index 4f00f7635..04ac52fe2 100644 --- a/Moose Development/Moose/AI/AI_A2G_BAI.lua +++ b/Moose Development/Moose/AI/AI_A2G_BAI.lua @@ -11,7 +11,7 @@ -- @module AI.AI_A2G_BAI -- @image AI_Air_To_Ground_Engage.JPG ---- @type AI_A2G_BAI +-- @type AI_A2G_BAI -- @extends AI.AI_A2A_Engage#AI_A2A_Engage -- TODO: Documentation. This class does not exist, unable to determine what it extends. --- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders. diff --git a/Moose Development/Moose/AI/AI_A2G_CAS.lua b/Moose Development/Moose/AI/AI_A2G_CAS.lua index a1a31414f..0a3cdb5bd 100644 --- a/Moose Development/Moose/AI/AI_A2G_CAS.lua +++ b/Moose Development/Moose/AI/AI_A2G_CAS.lua @@ -11,7 +11,7 @@ -- @module AI.AI_A2G_CAS -- @image AI_Air_To_Ground_Engage.JPG ---- @type AI_A2G_CAS +-- @type AI_A2G_CAS -- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL TODO: Documentation. This class does not exist, unable to determine what it extends. --- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders. diff --git a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua index cccbdbddc..7a7b9ff3f 100644 --- a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua @@ -909,14 +909,14 @@ do -- AI_A2G_DISPATCHER -- @type AI_A2G_DISPATCHER.DefenseCoordinates -- @map <#string,Core.Point#COORDINATE> A list of all defense coordinates mapped per defense coordinate name. - --- @field #AI_A2G_DISPATCHER.DefenseCoordinates DefenseCoordinates + -- @field #AI_A2G_DISPATCHER.DefenseCoordinates DefenseCoordinates AI_A2G_DISPATCHER.DefenseCoordinates = {} --- Enumerator for spawns at airbases. -- @type AI_A2G_DISPATCHER.Takeoff -- @extends Wrapper.Group#GROUP.Takeoff - --- @field #AI_A2G_DISPATCHER.Takeoff Takeoff + -- @field #AI_A2G_DISPATCHER.Takeoff Takeoff AI_A2G_DISPATCHER.Takeoff = GROUP.Takeoff --- Defines Landing location. @@ -947,7 +947,7 @@ do -- AI_A2G_DISPATCHER -- @type AI_A2G_DISPATCHER.DefenseQueue -- @list<#AI_A2G_DISPATCHER.DefenseQueueItem> DefenseQueueItem A list of all defenses being queued ... - --- @field #AI_A2G_DISPATCHER.DefenseQueue DefenseQueue + -- @field #AI_A2G_DISPATCHER.DefenseQueue DefenseQueue AI_A2G_DISPATCHER.DefenseQueue = {} --- Defense approach types. @@ -1141,7 +1141,7 @@ do -- AI_A2G_DISPATCHER end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_DISPATCHER:onafterStart( From, Event, To ) self:GetParent( self ).onafterStart( self, From, Event, To ) @@ -1206,7 +1206,7 @@ do -- AI_A2G_DISPATCHER end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_DISPATCHER:ResourcePark( DefenderSquadron ) local TemplateID = math.random( 1, #DefenderSquadron.Spawn ) local Spawn = DefenderSquadron.Spawn[ TemplateID ] -- Core.Spawn#SPAWN @@ -1223,7 +1223,7 @@ do -- AI_A2G_DISPATCHER end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_A2G_DISPATCHER:OnEventBaseCaptured( EventData ) @@ -1242,14 +1242,14 @@ do -- AI_A2G_DISPATCHER end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_A2G_DISPATCHER:OnEventCrashOrDead( EventData ) self.Detection:ForgetDetectedUnit( EventData.IniUnitName ) end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_A2G_DISPATCHER:OnEventLand( EventData ) self:F( "Landed" ) @@ -1266,7 +1266,7 @@ do -- AI_A2G_DISPATCHER self:RemoveDefenderFromSquadron( Squadron, Defender ) end DefenderUnit:Destroy() - self:ResourcePark( Squadron, Defender ) + self:ResourcePark( Squadron ) return end if DefenderUnit:GetLife() ~= DefenderUnit:GetLife0() then @@ -1278,7 +1278,7 @@ do -- AI_A2G_DISPATCHER end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_A2G_DISPATCHER:OnEventEngineShutdown( EventData ) local DefenderUnit = EventData.IniUnit @@ -1294,7 +1294,7 @@ do -- AI_A2G_DISPATCHER self:RemoveDefenderFromSquadron( Squadron, Defender ) end DefenderUnit:Destroy() - self:ResourcePark( Squadron, Defender ) + self:ResourcePark( Squadron ) end end end @@ -1302,7 +1302,7 @@ do -- AI_A2G_DISPATCHER do -- Manage the defensive behaviour - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self -- @param #string DefenseCoordinateName The name of the coordinate to be defended by A2G defenses. -- @param Core.Point#COORDINATE DefenseCoordinate The coordinate to be defended by A2G defenses. function AI_A2G_DISPATCHER:AddDefenseCoordinate( DefenseCoordinateName, DefenseCoordinate ) @@ -1310,19 +1310,19 @@ do -- AI_A2G_DISPATCHER end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_DISPATCHER:SetDefenseReactivityLow() self.DefenseReactivity = 0.05 end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_DISPATCHER:SetDefenseReactivityMedium() self.DefenseReactivity = 0.15 end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_DISPATCHER:SetDefenseReactivityHigh() self.DefenseReactivity = 0.5 end @@ -1873,7 +1873,7 @@ do -- AI_A2G_DISPATCHER end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self -- @param #string SquadronName The squadron name. -- @param #number TakeoffInterval Only Takeoff new units each specified interval in seconds in 10 seconds steps. -- @usage @@ -3374,7 +3374,7 @@ do -- AI_A2G_DISPATCHER end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_DISPATCHER:AddDefenderToSquadron( Squadron, Defender, Size ) self.Defenders = self.Defenders or {} local DefenderName = Defender:GetName() @@ -3385,7 +3385,7 @@ do -- AI_A2G_DISPATCHER self:F( { DefenderName = DefenderName, SquadronResourceCount = Squadron.ResourceCount } ) end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_DISPATCHER:RemoveDefenderFromSquadron( Squadron, Defender ) self.Defenders = self.Defenders or {} local DefenderName = Defender:GetName() @@ -3801,7 +3801,7 @@ do -- AI_A2G_DISPATCHER Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_Fsm:onafterLostControl( DefenderGroup, From, Event, To ) self:F({"LostControl", DefenderGroup:GetName()}) self:GetParent(self).onafterHome( self, DefenderGroup, From, Event, To ) @@ -3818,7 +3818,7 @@ do -- AI_A2G_DISPATCHER end end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_Fsm:onafterHome( DefenderGroup, From, Event, To, Action ) self:F({"Home", DefenderGroup:GetName()}) self:GetParent(self).onafterHome( self, DefenderGroup, From, Event, To ) @@ -3938,7 +3938,7 @@ do -- AI_A2G_DISPATCHER Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_Fsm:onafterLostControl( DefenderGroup, From, Event, To ) self:F({"Defender LostControl", DefenderGroup:GetName()}) self:GetParent(self).onafterHome( self, DefenderGroup, From, Event, To ) @@ -3955,7 +3955,7 @@ do -- AI_A2G_DISPATCHER end end - --- @param #AI_A2G_DISPATCHER self + -- @param #AI_A2G_DISPATCHER self function AI_A2G_Fsm:onafterHome( DefenderGroup, From, Event, To, Action ) self:F({"Defender Home", DefenderGroup:GetName()}) self:GetParent(self).onafterHome( self, DefenderGroup, From, Event, To ) diff --git a/Moose Development/Moose/AI/AI_A2G_SEAD.lua b/Moose Development/Moose/AI/AI_A2G_SEAD.lua index d98c40ee8..55583d270 100644 --- a/Moose Development/Moose/AI/AI_A2G_SEAD.lua +++ b/Moose Development/Moose/AI/AI_A2G_SEAD.lua @@ -13,7 +13,7 @@ ---- @type AI_A2G_SEAD +-- @type AI_A2G_SEAD -- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL diff --git a/Moose Development/Moose/AI/AI_Air.lua b/Moose Development/Moose/AI/AI_Air.lua index dea3ee2a8..9650bfee5 100644 --- a/Moose Development/Moose/AI/AI_Air.lua +++ b/Moose Development/Moose/AI/AI_Air.lua @@ -9,7 +9,7 @@ -- @module AI.AI_Air -- @image MOOSE.JPG ---- @type AI_AIR +-- @type AI_AIR -- @extends Core.Fsm#FSM_CONTROLLABLE --- The AI_AIR class implements the core functions to operate an AI @{Wrapper.Group}. @@ -259,7 +259,7 @@ function AI_AIR:New( AIGroup ) return self end ---- @param Wrapper.Group#GROUP self +-- @param Wrapper.Group#GROUP self -- @param Core.Event#EVENTDATA EventData function GROUP:OnEventTakeoff( EventData, Fsm ) Fsm:Takeoff() @@ -441,13 +441,13 @@ function AI_AIR:onafterReturn( Controllable, From, Event, To ) end ---- @param #AI_AIR self +-- @param #AI_AIR self function AI_AIR:onbeforeStatus() return self.CheckStatus end ---- @param #AI_AIR self +-- @param #AI_AIR self function AI_AIR:onafterStatus() if self.Controllable and self.Controllable:IsAlive() then @@ -555,7 +555,7 @@ function AI_AIR:onafterStatus() end ---- @param Wrapper.Group#GROUP AIGroup +-- @param Wrapper.Group#GROUP AIGroup function AI_AIR.RTBRoute( AIGroup, Fsm ) AIGroup:F( { "AI_AIR.RTBRoute:", AIGroup:GetName() } ) @@ -566,7 +566,7 @@ function AI_AIR.RTBRoute( AIGroup, Fsm ) end ---- @param Wrapper.Group#GROUP AIGroup +-- @param Wrapper.Group#GROUP AIGroup function AI_AIR.RTBHold( AIGroup, Fsm ) AIGroup:F( { "AI_AIR.RTBHold:", AIGroup:GetName() } ) @@ -593,7 +593,7 @@ function AI_AIR:SetRTBSpeedFactors(MinFactor,MaxFactor) end ---- @param #AI_AIR self +-- @param #AI_AIR self -- @param Wrapper.Group#GROUP AIGroup function AI_AIR:onafterRTB( AIGroup, From, Event, To ) self:F( { AIGroup, From, Event, To } ) @@ -681,7 +681,7 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To ) end ---- @param #AI_AIR self +-- @param #AI_AIR self -- @param Wrapper.Group#GROUP AIGroup function AI_AIR:onafterHome( AIGroup, From, Event, To ) self:F( { AIGroup, From, Event, To } ) @@ -695,7 +695,7 @@ end ---- @param #AI_AIR self +-- @param #AI_AIR self -- @param Wrapper.Group#GROUP AIGroup function AI_AIR:onafterHold( AIGroup, From, Event, To, HoldTime ) self:F( { AIGroup, From, Event, To } ) @@ -717,7 +717,7 @@ function AI_AIR:onafterHold( AIGroup, From, Event, To, HoldTime ) end ---- @param Wrapper.Group#GROUP AIGroup +-- @param Wrapper.Group#GROUP AIGroup function AI_AIR.Resume( AIGroup, Fsm ) AIGroup:I( { "AI_AIR.Resume:", AIGroup:GetName() } ) @@ -727,7 +727,7 @@ function AI_AIR.Resume( AIGroup, Fsm ) end ---- @param #AI_AIR self +-- @param #AI_AIR self -- @param Wrapper.Group#GROUP AIGroup function AI_AIR:onafterRefuel( AIGroup, From, Event, To ) self:F( { AIGroup, From, Event, To } ) @@ -793,13 +793,13 @@ end ---- @param #AI_AIR self +-- @param #AI_AIR self function AI_AIR:onafterDead() self:SetStatusOff() end ---- @param #AI_AIR self +-- @param #AI_AIR self -- @param Core.Event#EVENTDATA EventData function AI_AIR:OnCrash( EventData ) @@ -810,7 +810,7 @@ function AI_AIR:OnCrash( EventData ) end end ---- @param #AI_AIR self +-- @param #AI_AIR self -- @param Core.Event#EVENTDATA EventData function AI_AIR:OnEjection( EventData ) @@ -819,7 +819,7 @@ function AI_AIR:OnEjection( EventData ) end end ---- @param #AI_AIR self +-- @param #AI_AIR self -- @param Core.Event#EVENTDATA EventData function AI_AIR:OnPilotDead( EventData ) diff --git a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua index 7c9c735b5..284c1482d 100644 --- a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua @@ -905,14 +905,14 @@ do -- AI_AIR_DISPATCHER -- @type AI_AIR_DISPATCHER.DefenseCoordinates -- @map <#string,Core.Point#COORDINATE> A list of all defense coordinates mapped per defense coordinate name. - --- @field #AI_AIR_DISPATCHER.DefenseCoordinates DefenseCoordinates + -- @field #AI_AIR_DISPATCHER.DefenseCoordinates DefenseCoordinates AI_AIR_DISPATCHER.DefenseCoordinates = {} --- Enumerator for spawns at airbases -- @type AI_AIR_DISPATCHER.Takeoff -- @extends Wrapper.Group#GROUP.Takeoff - --- @field #AI_AIR_DISPATCHER.Takeoff Takeoff + -- @field #AI_AIR_DISPATCHER.Takeoff Takeoff AI_AIR_DISPATCHER.Takeoff = GROUP.Takeoff --- Defnes Landing location. @@ -943,7 +943,7 @@ do -- AI_AIR_DISPATCHER -- @type AI_AIR_DISPATCHER.DefenseQueue -- @list<#AI_AIR_DISPATCHER.DefenseQueueItem> DefenseQueueItem A list of all defenses being queued ... - --- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue + -- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue AI_AIR_DISPATCHER.DefenseQueue = {} --- Defense approach types @@ -1135,7 +1135,7 @@ do -- AI_AIR_DISPATCHER end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self function AI_AIR_DISPATCHER:onafterStart( From, Event, To ) self:GetParent( self ).onafterStart( self, From, Event, To ) @@ -1199,7 +1199,7 @@ do -- AI_AIR_DISPATCHER end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self function AI_AIR_DISPATCHER:ResourcePark( DefenderSquadron ) local TemplateID = math.random( 1, #DefenderSquadron.Spawn ) local Spawn = DefenderSquadron.Spawn[ TemplateID ] -- Core.Spawn#SPAWN @@ -1216,7 +1216,7 @@ do -- AI_AIR_DISPATCHER end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_AIR_DISPATCHER:OnEventBaseCaptured( EventData ) @@ -1234,13 +1234,13 @@ do -- AI_AIR_DISPATCHER end end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_AIR_DISPATCHER:OnEventCrashOrDead( EventData ) self.Detection:ForgetDetectedUnit( EventData.IniUnitName ) end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_AIR_DISPATCHER:OnEventLand( EventData ) self:F( "Landed" ) @@ -1257,7 +1257,7 @@ do -- AI_AIR_DISPATCHER self:RemoveDefenderFromSquadron( Squadron, Defender ) end DefenderUnit:Destroy() - self:ResourcePark( Squadron, Defender ) + self:ResourcePark( Squadron ) return end if DefenderUnit:GetLife() ~= DefenderUnit:GetLife0() then @@ -1268,7 +1268,7 @@ do -- AI_AIR_DISPATCHER end end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_AIR_DISPATCHER:OnEventEngineShutdown( EventData ) local DefenderUnit = EventData.IniUnit @@ -1284,31 +1284,31 @@ do -- AI_AIR_DISPATCHER self:RemoveDefenderFromSquadron( Squadron, Defender ) end DefenderUnit:Destroy() - self:ResourcePark( Squadron, Defender ) + self:ResourcePark( Squadron ) end end end do -- Manage the defensive behaviour - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self -- @param #string DefenseCoordinateName The name of the coordinate to be defended by AIR defenses. -- @param Core.Point#COORDINATE DefenseCoordinate The coordinate to be defended by AIR defenses. function AI_AIR_DISPATCHER:AddDefenseCoordinate( DefenseCoordinateName, DefenseCoordinate ) self.DefenseCoordinates[DefenseCoordinateName] = DefenseCoordinate end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self function AI_AIR_DISPATCHER:SetDefenseReactivityLow() self.DefenseReactivity = 0.05 end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self function AI_AIR_DISPATCHER:SetDefenseReactivityMedium() self.DefenseReactivity = 0.15 end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self function AI_AIR_DISPATCHER:SetDefenseReactivityHigh() self.DefenseReactivity = 0.5 end @@ -1872,7 +1872,7 @@ do -- AI_AIR_DISPATCHER end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self -- @param #string SquadronName The squadron name. -- @param #number TakeoffInterval Only Takeoff new units each specified interval in seconds in 10 seconds steps. -- @usage @@ -2774,7 +2774,7 @@ do -- AI_AIR_DISPATCHER -- TODO: Need to model the resources in a squadron. - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self -- @param AI.AI_Air_Squadron#AI_AIR_SQUADRON Squadron function AI_AIR_DISPATCHER:AddDefenderToSquadron( Squadron, Defender, Size ) self.Defenders = self.Defenders or {} @@ -2787,7 +2787,7 @@ do -- AI_AIR_DISPATCHER self:F( { DefenderName = DefenderName, SquadronResourceCount = Squadron.ResourceCount } ) end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self -- @param AI.AI_Air_Squadron#AI_AIR_SQUADRON Squadron function AI_AIR_DISPATCHER:RemoveDefenderFromSquadron( Squadron, Defender ) self.Defenders = self.Defenders or {} @@ -2800,7 +2800,7 @@ do -- AI_AIR_DISPATCHER self:F( { DefenderName = DefenderName, SquadronResourceCount = SquadronResourceCount } ) end - --- @param #AI_AIR_DISPATCHER self + -- @param #AI_AIR_DISPATCHER self -- @param Wrapper.Group#GROUP Defender -- @return AI.AI_Air_Squadron#AI_AIR_SQUADRON The Squadron. function AI_AIR_DISPATCHER:GetSquadronFromDefender( Defender ) diff --git a/Moose Development/Moose/AI/AI_Air_Engage.lua b/Moose Development/Moose/AI/AI_Air_Engage.lua index 40438daba..12f7a71fc 100644 --- a/Moose Development/Moose/AI/AI_Air_Engage.lua +++ b/Moose Development/Moose/AI/AI_Air_Engage.lua @@ -13,7 +13,7 @@ ---- @type AI_AIR_ENGAGE +-- @type AI_AIR_ENGAGE -- @extends AI.AI_AIR#AI_AIR @@ -367,7 +367,7 @@ function AI_AIR_ENGAGE:onafterAbort( AIGroup, From, Event, To ) end ---- @param #AI_AIR_ENGAGE self +-- @param #AI_AIR_ENGAGE self -- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -377,7 +377,7 @@ function AI_AIR_ENGAGE:onafterAccomplish( AIGroup, From, Event, To ) --self:SetDetectionOff() end ---- @param #AI_AIR_ENGAGE self +-- @param #AI_AIR_ENGAGE self -- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -390,7 +390,7 @@ function AI_AIR_ENGAGE:onafterDestroy( AIGroup, From, Event, To, EventData ) end end ---- @param #AI_AIR_ENGAGE self +-- @param #AI_AIR_ENGAGE self -- @param Core.Event#EVENTDATA EventData function AI_AIR_ENGAGE:OnEventDead( EventData ) self:F( { "EventDead", EventData } ) @@ -403,7 +403,7 @@ function AI_AIR_ENGAGE:OnEventDead( EventData ) end ---- @param Wrapper.Group#GROUP AIControllable +-- @param Wrapper.Group#GROUP AIControllable function AI_AIR_ENGAGE.___EngageRoute( AIGroup, Fsm, AttackSetUnit ) Fsm:I(string.format("AI_AIR_ENGAGE.___EngageRoute: %s", tostring(AIGroup:GetName()))) @@ -413,7 +413,7 @@ function AI_AIR_ENGAGE.___EngageRoute( AIGroup, Fsm, AttackSetUnit ) end ---- @param #AI_AIR_ENGAGE self +-- @param #AI_AIR_ENGAGE self -- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -494,7 +494,7 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac end ---- @param Wrapper.Group#GROUP AIControllable +-- @param Wrapper.Group#GROUP AIControllable function AI_AIR_ENGAGE.___Engage( AIGroup, Fsm, AttackSetUnit ) Fsm:I(string.format("AI_AIR_ENGAGE.___Engage: %s", tostring(AIGroup:GetName()))) @@ -506,7 +506,7 @@ function AI_AIR_ENGAGE.___Engage( AIGroup, Fsm, AttackSetUnit ) end ---- @param #AI_AIR_ENGAGE self +-- @param #AI_AIR_ENGAGE self -- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -591,7 +591,7 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU end end ---- @param Wrapper.Group#GROUP AIEngage +-- @param Wrapper.Group#GROUP AIEngage function AI_AIR_ENGAGE.Resume( AIEngage, Fsm ) AIEngage:F( { "Resume:", AIEngage:GetName() } ) diff --git a/Moose Development/Moose/AI/AI_Air_Patrol.lua b/Moose Development/Moose/AI/AI_Air_Patrol.lua index d354ded6f..6b1562817 100644 --- a/Moose Development/Moose/AI/AI_Air_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Air_Patrol.lua @@ -9,7 +9,7 @@ -- @module AI.AI_Air_Patrol -- @image AI_Air_To_Ground_Patrol.JPG ---- @type AI_AIR_PATROL +-- @type AI_AIR_PATROL -- @extends AI.AI_Air#AI_AIR --- The AI_AIR_PATROL class implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} diff --git a/Moose Development/Moose/AI/AI_Air_Squadron.lua b/Moose Development/Moose/AI/AI_Air_Squadron.lua index 40585187b..f1a74fecc 100644 --- a/Moose Development/Moose/AI/AI_Air_Squadron.lua +++ b/Moose Development/Moose/AI/AI_Air_Squadron.lua @@ -13,7 +13,7 @@ ---- @type AI_AIR_SQUADRON +-- @type AI_AIR_SQUADRON -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua index 32a6616e9..e2e329d3c 100644 --- a/Moose Development/Moose/AI/AI_BAI.lua +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -403,7 +403,7 @@ function AI_BAI_ZONE:onafterStart( Controllable, From, Event, To ) self:SetDetectionDeactivated() -- When not engaging, set the detection off. end ---- @param Wrapper.Controllable#CONTROLLABLE AIControllable +-- @param Wrapper.Controllable#CONTROLLABLE AIControllable function _NewEngageRoute( AIControllable ) AIControllable:T( "NewEngageRoute" ) @@ -412,7 +412,7 @@ function _NewEngageRoute( AIControllable ) end ---- @param #AI_BAI_ZONE self +-- @param #AI_BAI_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -424,7 +424,7 @@ function AI_BAI_ZONE:onbeforeEngage( Controllable, From, Event, To ) end end ---- @param #AI_BAI_ZONE self +-- @param #AI_BAI_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -473,7 +473,7 @@ function AI_BAI_ZONE:onafterTarget( Controllable, From, Event, To ) end ---- @param #AI_BAI_ZONE self +-- @param #AI_BAI_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -483,7 +483,7 @@ function AI_BAI_ZONE:onafterAbort( Controllable, From, Event, To ) self:__Route( 1 ) end ---- @param #AI_BAI_ZONE self +-- @param #AI_BAI_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -607,7 +607,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To, end ---- @param #AI_BAI_ZONE self +-- @param #AI_BAI_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -618,7 +618,7 @@ function AI_BAI_ZONE:onafterAccomplish( Controllable, From, Event, To ) end ---- @param #AI_BAI_ZONE self +-- @param #AI_BAI_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -632,7 +632,7 @@ function AI_BAI_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) end ---- @param #AI_BAI_ZONE self +-- @param #AI_BAI_ZONE self -- @param Core.Event#EVENTDATA EventData function AI_BAI_ZONE:OnEventDead( EventData ) self:F( { "EventDead", EventData } ) diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index 047135fb1..6ef9e13f1 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -27,7 +27,7 @@ -- @module AI.AI_Balancer -- @image AI_Balancing.JPG ---- @type AI_BALANCER +-- @type AI_BALANCER -- @field Core.Set#SET_CLIENT SetClient -- @field Core.Spawn#SPAWN SpawnAI -- @field Wrapper.Group#GROUP Test @@ -163,7 +163,7 @@ function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange ) self.ReturnThresholdRange = ReturnThresholdRange end ---- @param #AI_BALANCER self +-- @param #AI_BALANCER self -- @param Core.Set#SET_GROUP SetGroup -- @param #string ClientName -- @param Wrapper.Group#GROUP AIGroup @@ -185,7 +185,7 @@ function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName ) end end ---- @param #AI_BALANCER self +-- @param #AI_BALANCER self -- @param Core.Set#SET_GROUP SetGroup -- @param Wrapper.Group#GROUP AIGroup function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, AIGroup ) @@ -229,14 +229,14 @@ function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup ) end ---- @param #AI_BALANCER self +-- @param #AI_BALANCER self function AI_BALANCER:onenterMonitoring( SetGroup ) self:T2( { self.SetClient:Count() } ) --self.SetClient:Flush() self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Client#CLIENT Client function( Client ) self:T3(Client.ClientName) @@ -259,7 +259,7 @@ function AI_BALANCER:onenterMonitoring( SetGroup ) self:T2( RangeZone ) _DATABASE:ForEachPlayerUnit( - --- @param Wrapper.Unit#UNIT RangeTestUnit + -- @param Wrapper.Unit#UNIT RangeTestUnit function( RangeTestUnit, RangeZone, AIGroup, PlayerInRange ) self:T2( { PlayerInRange, RangeTestUnit.UnitName, RangeZone.ZoneName } ) if RangeTestUnit:IsInZone( RangeZone ) == true then @@ -271,7 +271,7 @@ function AI_BALANCER:onenterMonitoring( SetGroup ) end end, - --- @param Core.Zone#ZONE_RADIUS RangeZone + -- @param Core.Zone#ZONE_RADIUS RangeZone -- @param Wrapper.Group#GROUP AIGroup function( RangeZone, AIGroup, PlayerInRange ) if PlayerInRange.Value == false then diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index f767a52cd..252c86297 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -31,7 +31,7 @@ -- @module AI.AI_CAP -- @image AI_Combat_Air_Patrol.JPG ---- @type AI_CAP_ZONE +-- @type AI_CAP_ZONE -- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed. -- @extends AI.AI_Patrol#AI_PATROL_ZONE @@ -339,7 +339,7 @@ function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To ) end ---- @param AI.AI_CAP#AI_CAP_ZONE +-- @param AI.AI_CAP#AI_CAP_ZONE -- @param Wrapper.Group#GROUP EngageGroup function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm ) @@ -350,7 +350,7 @@ function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm ) end end ---- @param #AI_CAP_ZONE self +-- @param #AI_CAP_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -362,7 +362,7 @@ function AI_CAP_ZONE:onbeforeEngage( Controllable, From, Event, To ) end end ---- @param #AI_CAP_ZONE self +-- @param #AI_CAP_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -390,7 +390,7 @@ function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To ) end end ---- @param #AI_CAP_ZONE self +-- @param #AI_CAP_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -400,7 +400,7 @@ function AI_CAP_ZONE:onafterAbort( Controllable, From, Event, To ) self:__Route( 1 ) end ---- @param #AI_CAP_ZONE self +-- @param #AI_CAP_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -500,7 +500,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To ) end end ---- @param #AI_CAP_ZONE self +-- @param #AI_CAP_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -510,7 +510,7 @@ function AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To ) self:SetDetectionOff() end ---- @param #AI_CAP_ZONE self +-- @param #AI_CAP_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -523,7 +523,7 @@ function AI_CAP_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) end end ---- @param #AI_CAP_ZONE self +-- @param #AI_CAP_ZONE self -- @param Core.Event#EVENTDATA EventData function AI_CAP_ZONE:OnEventDead( EventData ) self:F( { "EventDead", EventData } ) diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index d92c6baa4..97348e69c 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -358,7 +358,7 @@ function AI_CAS_ZONE:onafterStart( Controllable, From, Event, To ) self:SetDetectionDeactivated() -- When not engaging, set the detection off. end ---- @param AI.AI_CAS#AI_CAS_ZONE +-- @param AI.AI_CAS#AI_CAS_ZONE -- @param Wrapper.Group#GROUP EngageGroup function AI_CAS_ZONE.EngageRoute( EngageGroup, Fsm ) @@ -370,7 +370,7 @@ function AI_CAS_ZONE.EngageRoute( EngageGroup, Fsm ) end ---- @param #AI_CAS_ZONE self +-- @param #AI_CAS_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -382,7 +382,7 @@ function AI_CAS_ZONE:onbeforeEngage( Controllable, From, Event, To ) end end ---- @param #AI_CAS_ZONE self +-- @param #AI_CAS_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -415,7 +415,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To ) end ---- @param #AI_CAS_ZONE self +-- @param #AI_CAS_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -425,7 +425,7 @@ function AI_CAS_ZONE:onafterAbort( Controllable, From, Event, To ) self:__Route( 1 ) end ---- @param #AI_CAS_ZONE self +-- @param #AI_CAS_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -525,7 +525,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, end ---- @param #AI_CAS_ZONE self +-- @param #AI_CAS_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -536,7 +536,7 @@ function AI_CAS_ZONE:onafterAccomplish( Controllable, From, Event, To ) end ---- @param #AI_CAS_ZONE self +-- @param #AI_CAS_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. @@ -550,7 +550,7 @@ function AI_CAS_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) end ---- @param #AI_CAS_ZONE self +-- @param #AI_CAS_ZONE self -- @param Core.Event#EVENTDATA EventData function AI_CAS_ZONE:OnEventDead( EventData ) self:F( { "EventDead", EventData } ) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 43d5eb358..8d6fef8ed 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -9,7 +9,7 @@ -- @module AI.AI_Cargo -- @image Cargo.JPG ---- @type AI_CARGO +-- @type AI_CARGO -- @extends Core.Fsm#FSM_CONTROLLABLE diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 6bc78debb..f5f25f8a7 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -9,7 +9,7 @@ -- @module AI.AI_Cargo_APC -- @image AI_Cargo_Dispatching_For_APC.JPG ---- @type AI_CARGO_APC +-- @type AI_CARGO_APC -- @extends AI.AI_Cargo#AI_CARGO diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index ede3d20bd..9275c62be 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -9,7 +9,7 @@ -- @module AI.AI_Cargo_Airplane -- @image AI_Cargo_Dispatching_For_Airplanes.JPG ---- @type AI_CARGO_AIRPLANE +-- @type AI_CARGO_AIRPLANE -- @extends Core.Fsm#FSM_CONTROLLABLE diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index ec725d5f8..9929b5d4c 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -111,7 +111,7 @@ -- @image AI_Cargo_Dispatcher.JPG ---- @type AI_CARGO_DISPATCHER +-- @type AI_CARGO_DISPATCHER -- @field Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers that will transport the cargo. -- @field Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects. -- @field Core.Zone#SET_ZONE PickupZoneSet The set of pickup zones, which are used to where the cargo can be picked up by the carriers. If nil, then cargo can be picked up everywhere. @@ -578,10 +578,10 @@ AI_CARGO_DISPATCHER = { PickupCargo = {} } ---- @field #list +-- @field #list AI_CARGO_DISPATCHER.AI_Cargo = {} ---- @field #list +-- @field #list AI_CARGO_DISPATCHER.PickupCargo = {} diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index cd530ab24..e25725394 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -30,7 +30,7 @@ -- @module AI.AI_Cargo_Dispatcher_APC -- @image AI_Cargo_Dispatching_For_APC.JPG ---- @type AI_CARGO_DISPATCHER_APC +-- @type AI_CARGO_DISPATCHER_APC -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 81121e357..bc5deaa42 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -24,7 +24,7 @@ -- @image AI_Cargo_Dispatching_For_Airplanes.JPG ---- @type AI_CARGO_DISPATCHER_AIRPLANE +-- @type AI_CARGO_DISPATCHER_AIRPLANE -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 33e6ca0d1..89c9cb771 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -25,7 +25,7 @@ -- @module AI.AI_Cargo_Dispatcher_Helicopter -- @image AI_Cargo_Dispatching_For_Helicopters.JPG ---- @type AI_CARGO_DISPATCHER_HELICOPTER +-- @type AI_CARGO_DISPATCHER_HELICOPTER -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua index 36604f30b..dfb445680 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua @@ -23,7 +23,7 @@ -- @module AI.AI_Cargo_Dispatcher_Ship -- @image AI_Cargo_Dispatcher.JPG ---- @type AI_CARGO_DISPATCHER_SHIP +-- @type AI_CARGO_DISPATCHER_SHIP -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 2c1d5c028..20fb3e7e5 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -9,7 +9,7 @@ -- @module AI.AI_Cargo_Helicopter -- @image AI_Cargo_Dispatching_For_Helicopters.JPG ---- @type AI_CARGO_HELICOPTER +-- @type AI_CARGO_HELICOPTER -- @extends Core.Fsm#FSM_CONTROLLABLE @@ -282,7 +282,7 @@ function AI_CARGO_HELICOPTER:SetLandingSpeedAndHeight(speed, height) return self end ---- @param #AI_CARGO_HELICOPTER self +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param From -- @param Event @@ -321,7 +321,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) end ---- @param #AI_CARGO_HELICOPTER self +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param From -- @param Event @@ -404,7 +404,7 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina end ---- @param #AI_CARGO_HELICOPTER self +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param From -- @param Event diff --git a/Moose Development/Moose/AI/AI_Cargo_Ship.lua b/Moose Development/Moose/AI/AI_Cargo_Ship.lua index 8722e5026..f902f153f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Ship.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Ship.lua @@ -9,7 +9,7 @@ -- @module AI.AI_Cargo_Ship -- @image AI_Cargo_Dispatcher.JPG ---- @type AI_CARGO_SHIP +-- @type AI_CARGO_SHIP -- @extends AI.AI_Cargo#AI_CARGO --- Brings a dynamic cargo handling capability for an AI naval group. diff --git a/Moose Development/Moose/AI/AI_Escort.lua b/Moose Development/Moose/AI/AI_Escort.lua index e18948790..ee84d118f 100644 --- a/Moose Development/Moose/AI/AI_Escort.lua +++ b/Moose Development/Moose/AI/AI_Escort.lua @@ -148,7 +148,7 @@ ---- @type AI_ESCORT +-- @type AI_ESCORT -- @extends AI.AI_Formation#AI_FORMATION @@ -189,7 +189,7 @@ AI_ESCORT = { TaskPoints = {} } ---- @field Functional.Detection#DETECTION_AREAS +-- @field Functional.Detection#DETECTION_AREAS AI_ESCORT.Detection = nil --- MENUPARAM type @@ -250,7 +250,7 @@ function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) EscortGroupSet:ForEachGroup( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) -- Set EscortGroup known at EscortUnit. if not self.PlayerUnit._EscortGroups then @@ -325,14 +325,14 @@ function AI_ESCORT:_InitEscortRoute( EscortGroup ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Core.Set#SET_GROUP EscortGroupSet function AI_ESCORT:onafterStart( EscortGroupSet ) self:F() EscortGroupSet:ForEachGroup( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) EscortGroup:WayPointInitialize() @@ -370,7 +370,7 @@ function AI_ESCORT:onafterStart( EscortGroupSet ) self:_InitFlightMenus() self.EscortGroupSet:ForSomeGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) self:_InitEscortMenus( EscortGroup ) @@ -378,7 +378,7 @@ function AI_ESCORT:onafterStart( EscortGroupSet ) self:SetFlightModeFormation( EscortGroup ) - --- @param #AI_ESCORT self + -- @param #AI_ESCORT self -- @param Core.Event#EVENTDATA EventData function EscortGroup:OnEventDeadOrCrash( EventData ) self:F( { "EventDead", EventData } ) @@ -394,14 +394,14 @@ function AI_ESCORT:onafterStart( EscortGroupSet ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Core.Set#SET_GROUP EscortGroupSet function AI_ESCORT:onafterStop( EscortGroupSet ) self:F() EscortGroupSet:ForEachGroup( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) EscortGroup:WayPointInitialize() @@ -550,7 +550,7 @@ function AI_ESCORT:SetFlightMenuFormation( Formation ) local MenuFlightFormationID = MENU_GROUP_COMMAND:New( self.PlayerGroup, Formation, FlightMenuFormation, function ( self, Formation, ... ) self.EscortGroupSet:ForSomeGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup, self, Formation, Arguments ) if EscortGroup:IsAir() then self:E({FormationID=FormationID}) @@ -1231,7 +1231,7 @@ function AI_ESCORT:MenuAssistedAttack() self:F() self.EscortGroupSet:ForSomeGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if not EscortGroup:IsAir() then -- Request assistance from other escorts. @@ -1375,7 +1375,7 @@ function AI_ESCORT:SetEscortMenuResumeMission( EscortGroup ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP OrbitGroup -- @param Wrapper.Group#GROUP EscortGroup -- @param #number OrbitHeight @@ -1419,7 +1419,7 @@ function AI_ESCORT:_HoldPosition( OrbitGroup, EscortGroup, OrbitHeight, OrbitSec end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP OrbitGroup -- @param #number OrbitHeight -- @param #number OrbitSeconds @@ -1428,7 +1428,7 @@ function AI_ESCORT:_FlightHoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) local EscortUnit = self.PlayerUnit self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup, OrbitGroup ) if EscortGroup:IsAir() then if OrbitGroup == nil then @@ -1456,7 +1456,7 @@ end function AI_ESCORT:_FlightJoinUp() self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_JoinUp( EscortGroup ) @@ -1483,7 +1483,7 @@ end function AI_ESCORT:_FlightFormationTrail( XStart, XSpace, YStart ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart ) @@ -1510,7 +1510,7 @@ end function AI_ESCORT:_FlightFormationStack( XStart, XSpace, YStart, YSpace ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_EscortFormationStack( EscortGroup, XStart, XSpace, YStart, YSpace ) @@ -1533,7 +1533,7 @@ end function AI_ESCORT:_FlightFlare( Color, Message ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_Flare( EscortGroup, Color, Message ) @@ -1556,7 +1556,7 @@ end function AI_ESCORT:_FlightSmoke( Color, Message ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_Smoke( EscortGroup, Color, Message ) @@ -1587,7 +1587,7 @@ end function AI_ESCORT:_FlightSwitchReportNearbyTargets( ReportTargets ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_EscortSwitchReportNearbyTargets( EscortGroup, ReportTargets ) @@ -1679,7 +1679,7 @@ function AI_ESCORT:_ScanTargets( ScanDuration ) end ---- @param Wrapper.Group#GROUP EscortGroup +-- @param Wrapper.Group#GROUP EscortGroup -- @param #AI_ESCORT self function AI_ESCORT.___Resume( EscortGroup, self ) @@ -1701,7 +1701,7 @@ function AI_ESCORT.___Resume( EscortGroup, self ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP EscortGroup -- @param #number WayPoint function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint ) @@ -1723,7 +1723,7 @@ function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item. -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem ) @@ -1743,7 +1743,7 @@ function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem ) local AttackUnitTasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then AttackUnitTasks[#AttackUnitTasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit ) @@ -1767,7 +1767,7 @@ function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem ) local Tasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) @@ -1795,7 +1795,7 @@ end function AI_ESCORT:_FlightAttackTarget( DetectedItem ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup, DetectedItem ) if EscortGroup:IsAir() then self:_AttackTarget( EscortGroup, DetectedItem ) @@ -1842,7 +1842,7 @@ end --- ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item. -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem function AI_ESCORT:_AssistTarget( EscortGroup, DetectedItem ) @@ -1854,7 +1854,7 @@ function AI_ESCORT:_AssistTarget( EscortGroup, DetectedItem ) local Tasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) @@ -1881,7 +1881,7 @@ end function AI_ESCORT:_FlightROEHoldFire( EscortROEMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROE( EscortGroup, EscortGroup.OptionROEHoldFire, EscortROEMessage ) end @@ -1890,7 +1890,7 @@ end function AI_ESCORT:_FlightROEOpenFire( EscortROEMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROE( EscortGroup, EscortGroup.OptionROEOpenFire, EscortROEMessage ) end @@ -1899,7 +1899,7 @@ end function AI_ESCORT:_FlightROEReturnFire( EscortROEMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROE( EscortGroup, EscortGroup.OptionROEReturnFire, EscortROEMessage ) end @@ -1908,7 +1908,7 @@ end function AI_ESCORT:_FlightROEWeaponFree( EscortROEMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROE( EscortGroup, EscortGroup.OptionROEWeaponFree, EscortROEMessage ) end @@ -1924,7 +1924,7 @@ end function AI_ESCORT:_FlightROTNoReaction( EscortROTMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROT( EscortGroup, EscortGroup.OptionROTNoReaction, EscortROTMessage ) end @@ -1933,7 +1933,7 @@ end function AI_ESCORT:_FlightROTPassiveDefense( EscortROTMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROT( EscortGroup, EscortGroup.OptionROTPassiveDefense, EscortROTMessage ) end @@ -1942,7 +1942,7 @@ end function AI_ESCORT:_FlightROTEvadeFire( EscortROTMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROT( EscortGroup, EscortGroup.OptionROTEvadeFire, EscortROTMessage ) end @@ -1951,7 +1951,7 @@ end function AI_ESCORT:_FlightROTVertical( EscortROTMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROT( EscortGroup, EscortGroup.OptionROTVertical, EscortROTMessage ) end diff --git a/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua b/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua index 26e89a728..7af67f5f0 100644 --- a/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua @@ -15,7 +15,7 @@ -- @image MOOSE.JPG ---- @type AI_ESCORT_DISPATCHER +-- @type AI_ESCORT_DISPATCHER -- @extends Core.Fsm#FSM @@ -28,7 +28,7 @@ AI_ESCORT_DISPATCHER = { ClassName = "AI_ESCORT_DISPATCHER", } ---- @field #list +-- @field #list AI_ESCORT_DISPATCHER.AI_Escorts = {} @@ -97,7 +97,7 @@ function AI_ESCORT_DISPATCHER:onafterStart( From, Event, To ) end ---- @param #AI_ESCORT_DISPATCHER self +-- @param #AI_ESCORT_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_ESCORT_DISPATCHER:OnEventExit( EventData ) @@ -120,7 +120,7 @@ function AI_ESCORT_DISPATCHER:OnEventExit( EventData ) end ---- @param #AI_ESCORT_DISPATCHER self +-- @param #AI_ESCORT_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_ESCORT_DISPATCHER:OnEventBirth( EventData ) diff --git a/Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua b/Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua index 25d29b7e5..8b671c368 100644 --- a/Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua +++ b/Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua @@ -15,7 +15,7 @@ -- @image MOOSE.JPG ---- @type AI_ESCORT_DISPATCHER_REQUEST +-- @type AI_ESCORT_DISPATCHER_REQUEST -- @extends Core.Fsm#FSM @@ -28,7 +28,7 @@ AI_ESCORT_DISPATCHER_REQUEST = { ClassName = "AI_ESCORT_DISPATCHER_REQUEST", } ---- @field #list +-- @field #list AI_ESCORT_DISPATCHER_REQUEST.AI_Escorts = {} @@ -75,7 +75,7 @@ function AI_ESCORT_DISPATCHER_REQUEST:onafterStart( From, Event, To ) end ---- @param #AI_ESCORT_DISPATCHER_REQUEST self +-- @param #AI_ESCORT_DISPATCHER_REQUEST self -- @param Core.Event#EVENTDATA EventData function AI_ESCORT_DISPATCHER_REQUEST:OnEventExit( EventData ) @@ -92,7 +92,7 @@ function AI_ESCORT_DISPATCHER_REQUEST:OnEventExit( EventData ) end ---- @param #AI_ESCORT_DISPATCHER_REQUEST self +-- @param #AI_ESCORT_DISPATCHER_REQUEST self -- @param Core.Event#EVENTDATA EventData function AI_ESCORT_DISPATCHER_REQUEST:OnEventBirth( EventData ) diff --git a/Moose Development/Moose/AI/AI_Escort_Request.lua b/Moose Development/Moose/AI/AI_Escort_Request.lua index c7d109fff..b56e7d1c7 100644 --- a/Moose Development/Moose/AI/AI_Escort_Request.lua +++ b/Moose Development/Moose/AI/AI_Escort_Request.lua @@ -148,7 +148,7 @@ ---- @type AI_ESCORT_REQUEST +-- @type AI_ESCORT_REQUEST -- @extends AI.AI_Escort#AI_ESCORT --- AI_ESCORT_REQUEST class @@ -223,7 +223,7 @@ function AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, EscortAirbase, EscortNa return self end ---- @param #AI_ESCORT_REQUEST self +-- @param #AI_ESCORT_REQUEST self function AI_ESCORT_REQUEST:SpawnEscort() local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot ) @@ -248,7 +248,7 @@ function AI_ESCORT_REQUEST:SpawnEscort() self:_InitEscortMenus( EscortGroup ) self:_InitEscortRoute( EscortGroup ) - --- @param #AI_ESCORT self + -- @param #AI_ESCORT self -- @param Core.Event#EVENTDATA EventData function EscortGroup:OnEventDeadOrCrash( EventData ) self:F( { "EventDead", EventData } ) @@ -263,7 +263,7 @@ function AI_ESCORT_REQUEST:SpawnEscort() end ---- @param #AI_ESCORT_REQUEST self +-- @param #AI_ESCORT_REQUEST self -- @param Core.Set#SET_GROUP EscortGroupSet function AI_ESCORT_REQUEST:onafterStart( EscortGroupSet ) @@ -285,14 +285,14 @@ function AI_ESCORT_REQUEST:onafterStart( EscortGroupSet ) end ---- @param #AI_ESCORT_REQUEST self +-- @param #AI_ESCORT_REQUEST self -- @param Core.Set#SET_GROUP EscortGroupSet function AI_ESCORT_REQUEST:onafterStop( EscortGroupSet ) self:F() EscortGroupSet:ForEachGroup( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) EscortGroup:WayPointInitialize() diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index 657f8aa2a..9b593b358 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -112,7 +112,7 @@ AI_FORMATION = { AI_FORMATION.__Enum = {} ---- @type AI_FORMATION.__Enum.Formation +-- @type AI_FORMATION.__Enum.Formation -- @field #number None -- @field #number Line -- @field #number Trail @@ -137,7 +137,7 @@ AI_FORMATION.__Enum.Formation = { Box = 10, } ---- @type AI_FORMATION.__Enum.Mode +-- @type AI_FORMATION.__Enum.Mode -- @field #number Mission -- @field #number Formation AI_FORMATION.__Enum.Mode = { @@ -147,13 +147,13 @@ AI_FORMATION.__Enum.Mode = { Reconnaissance = "R", } ---- @type AI_FORMATION.__Enum.ReportType +-- @type AI_FORMATION.__Enum.ReportType -- @field #number All -- @field #number Airborne -- @field #number GroundRadar -- @field #number Ground AI_FORMATION.__Enum.ReportType = { - Airborne = "*", + All = "*", Airborne = "A", GroundRadar = "R", Ground = "G", @@ -1000,7 +1000,7 @@ function AI_FORMATION:SetFlightModeMission( FollowGroup ) FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission ) else self.FollowGroupSet:ForSomeGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( FollowGroup ) FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) ) FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission ) @@ -1024,7 +1024,7 @@ function AI_FORMATION:SetFlightModeAttack( FollowGroup ) FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack ) else self.FollowGroupSet:ForSomeGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( FollowGroup ) FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) ) FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack ) @@ -1048,7 +1048,7 @@ function AI_FORMATION:SetFlightModeFormation( FollowGroup ) FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation ) else self.FollowGroupSet:ForSomeGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( FollowGroup ) FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) ) FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation ) @@ -1226,7 +1226,7 @@ function AI_FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2) local CVI = { x = CV2.x + CS * 10 * math.sin(Ca), y = GH2.y + Inclination, -- + FollowFormation.y, - y = GH2.y, + --y = GH2.y, z = CV2.z + CS * 10 * math.cos(Ca), } diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index beba94951..12572b209 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -647,15 +647,15 @@ function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To ) end ---- @param #AI_PATROL_ZONE self ---- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @param #AI_PATROL_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable+ function AI_PATROL_ZONE:onbeforeDetect( Controllable, From, Event, To ) return self.DetectOn and self.DetectActivated end ---- @param #AI_PATROL_ZONE self ---- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @param #AI_PATROL_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable function AI_PATROL_ZONE:onafterDetect( Controllable, From, Event, To ) local Detected = false @@ -700,7 +700,7 @@ function AI_PATROL_ZONE:onafterDetect( Controllable, From, Event, To ) end ---- @param Wrapper.Controllable#CONTROLLABLE AIControllable +-- @param Wrapper.Controllable#CONTROLLABLE AIControllable -- This static method is called from the route path within the last task at the last waypoint of the Controllable. -- Note that this method is required, as triggers the next route when patrolling for the Controllable. function AI_PATROL_ZONE:_NewPatrolRoute( AIControllable ) @@ -817,13 +817,13 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To ) end ---- @param #AI_PATROL_ZONE self +-- @param #AI_PATROL_ZONE self function AI_PATROL_ZONE:onbeforeStatus() return self.CheckStatus end ---- @param #AI_PATROL_ZONE self +-- @param #AI_PATROL_ZONE self function AI_PATROL_ZONE:onafterStatus() self:F2() @@ -859,7 +859,7 @@ function AI_PATROL_ZONE:onafterStatus() end end ---- @param #AI_PATROL_ZONE self +-- @param #AI_PATROL_ZONE self function AI_PATROL_ZONE:onafterRTB() self:F2() @@ -898,13 +898,13 @@ function AI_PATROL_ZONE:onafterRTB() end ---- @param #AI_PATROL_ZONE self +-- @param #AI_PATROL_ZONE self function AI_PATROL_ZONE:onafterDead() self:SetDetectionOff() self:SetStatusOff() end ---- @param #AI_PATROL_ZONE self +-- @param #AI_PATROL_ZONE self -- @param Core.Event#EVENTDATA EventData function AI_PATROL_ZONE:OnCrash( EventData ) @@ -915,7 +915,7 @@ function AI_PATROL_ZONE:OnCrash( EventData ) end end ---- @param #AI_PATROL_ZONE self +-- @param #AI_PATROL_ZONE self -- @param Core.Event#EVENTDATA EventData function AI_PATROL_ZONE:OnEjection( EventData ) @@ -924,7 +924,7 @@ function AI_PATROL_ZONE:OnEjection( EventData ) end end ---- @param #AI_PATROL_ZONE self +-- @param #AI_PATROL_ZONE self -- @param Core.Event#EVENTDATA EventData function AI_PATROL_ZONE:OnPilotDead( EventData ) diff --git a/Moose Development/Moose/Actions/Act_Account.lua b/Moose Development/Moose/Actions/Act_Account.lua index ef2e6d15d..4cca719da 100644 --- a/Moose Development/Moose/Actions/Act_Account.lua +++ b/Moose Development/Moose/Actions/Act_Account.lua @@ -269,7 +269,7 @@ do -- ACT_ACCOUNT_DEADS --- DCS Events - --- @param #ACT_ACCOUNT_DEADS self + -- @param #ACT_ACCOUNT_DEADS self -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:OnEventHit( EventData ) self:T( { "EventDead", EventData } ) @@ -280,7 +280,7 @@ do -- ACT_ACCOUNT_DEADS end end - --- @param #ACT_ACCOUNT_DEADS self + -- @param #ACT_ACCOUNT_DEADS self -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData ) self:T( { "EventDead", EventData } ) @@ -292,7 +292,7 @@ do -- ACT_ACCOUNT_DEADS --- DCS Events - --- @param #ACT_ACCOUNT_DEADS self + -- @param #ACT_ACCOUNT_DEADS self -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData ) self:T( { "EventDead", EventData } ) diff --git a/Moose Development/Moose/Actions/Act_Assist.lua b/Moose Development/Moose/Actions/Act_Assist.lua index 07d990fb3..78fb5b9f5 100644 --- a/Moose Development/Moose/Actions/Act_Assist.lua +++ b/Moose Development/Moose/Actions/Act_Assist.lua @@ -195,7 +195,7 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking( ProcessUnit, From, Event, To ) self.TargetSetUnit:ForEachUnit( - --- @param Wrapper.Unit#UNIT SmokeUnit + -- @param Wrapper.Unit#UNIT SmokeUnit function( SmokeUnit ) if math.random( 1, ( 100 * self.TargetSetUnit:Count() ) / 4 ) <= 100 then SCHEDULER:New( self, diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 0d2d53ef8..c58b9ed6c 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -365,7 +365,7 @@ CARGOS = {} do -- CARGO - --- @type CARGO + -- @type CARGO -- @extends Core.Fsm#FSM_PROCESS -- @field #string Type A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers. -- @field #string Name A string defining the name of the cargo. The name is the unique identifier of the cargo. @@ -428,7 +428,7 @@ do -- CARGO Reported = {}, } - --- @type CARGO.CargoObjects + -- @type CARGO.CargoObjects -- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. --- CARGO Constructor. This class is an abstract class and should not be instantiated. @@ -1029,7 +1029,7 @@ end -- CARGO do -- CARGO_REPRESENTABLE - --- @type CARGO_REPRESENTABLE + -- @type CARGO_REPRESENTABLE -- @extends #CARGO -- @field test @@ -1137,7 +1137,7 @@ end -- CARGO_REPRESENTABLE do -- CARGO_REPORTABLE - --- @type CARGO_REPORTABLE + -- @type CARGO_REPORTABLE -- @extends #CARGO CARGO_REPORTABLE = { ClassName = "CARGO_REPORTABLE" @@ -1173,7 +1173,7 @@ end do -- CARGO_PACKAGE - --- @type CARGO_PACKAGE + -- @type CARGO_PACKAGE -- @extends #CARGO_REPRESENTABLE CARGO_PACKAGE = { ClassName = "CARGO_PACKAGE" diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 81bc7ca6a..cbb105f7f 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -73,7 +73,7 @@ do -- CARGO_CRATE return self end - --- @param #CARGO_CRATE self + -- @param #CARGO_CRATE self -- @param Core.Event#EVENTDATA EventData function CARGO_CRATE:OnEventCargoDead( EventData ) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index deb46b7d1..82b6f0339 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -19,7 +19,7 @@ do -- CARGO_GROUP - --- @type CARGO_GROUP + -- @type CARGO_GROUP -- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects. -- @field #string GroupName The name of the CargoGroup. -- @extends Cargo.Cargo#CARGO_REPORTABLE @@ -262,7 +262,7 @@ do -- CARGO_GROUP end - --- @param #CARGO_GROUP self + -- @param #CARGO_GROUP self -- @param Core.Event#EVENTDATA EventData function CARGO_GROUP:OnEventCargoDead( EventData ) @@ -422,7 +422,7 @@ do -- CARGO_GROUP -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 self.CargoSet:ForEach( - --- @param Cargo.Cargo#CARGO Cargo + -- @param Cargo.Cargo#CARGO Cargo function( Cargo, NearRadius ) if not Cargo:IsDestroyed() then local ToVec=nil diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index ba3d8bb0b..07855325a 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -67,7 +67,7 @@ do -- CARGO_SLINGLOAD end - --- @param #CARGO_SLINGLOAD self + -- @param #CARGO_SLINGLOAD self -- @param Core.Event#EVENTDATA EventData function CARGO_SLINGLOAD:OnEventCargoDead( EventData ) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index c94aee4cd..74cd76ca1 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -34,7 +34,7 @@ local _TraceClassMethod = {} local _ClassID = 0 ---- @type BASE +-- @type BASE -- @field ClassName The name of the class. -- @field ClassID The ID number of the class. -- @field ClassNameAndID The name of the class concatenated with the ID number of the class. @@ -201,10 +201,10 @@ BASE = { Scheduler = nil, } ---- @field #BASE.__ +-- @field #BASE.__ BASE.__ = {} ---- @field #BASE._ +-- @field #BASE._ BASE._ = { Schedules = {}, --- Contains the Schedulers Active } diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 4ad7f9e50..3029b605b 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -32,7 +32,7 @@ -- @image Core_Database.JPG ---- @type DATABASE +-- @type DATABASE -- @field #string ClassName Name of the class. -- @field #table Templates Templates: Units, Groups, Statics, ClientsByName, ClientsByID. -- @field #table CLIENTS Clients. @@ -1813,7 +1813,7 @@ function DATABASE:GetFlightControl(airbasename) return self.FLIGHTCONTROLS[airbasename] end ---- @param #DATABASE self +-- @param #DATABASE self function DATABASE:_RegisterTemplates() self:F2() diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 95c4f6a35..e88bbc892 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -173,7 +173,7 @@ -- @image Core_Event.JPG ---- @type EVENT +-- @type EVENT -- @field #EVENT.Events Events -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 2b5f499e5..7e434e127 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -79,7 +79,7 @@ do -- FSM - --- @type FSM + -- @type FSM -- @field #string ClassName Name of the class. -- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler. -- @field #table options Options. @@ -949,7 +949,7 @@ end do -- FSM_CONTROLLABLE - --- @type FSM_CONTROLLABLE + -- @type FSM_CONTROLLABLE -- @field Wrapper.Controllable#CONTROLLABLE Controllable -- @extends Core.Fsm#FSM @@ -1082,7 +1082,7 @@ end do -- FSM_PROCESS - --- @type FSM_PROCESS + -- @type FSM_PROCESS -- @field Tasking.Task#TASK Task -- @extends Core.Fsm#FSM_CONTROLLABLE diff --git a/Moose Development/Moose/Core/Goal.lua b/Moose Development/Moose/Core/Goal.lua index bc33246f6..cff272dbd 100644 --- a/Moose Development/Moose/Core/Goal.lua +++ b/Moose Development/Moose/Core/Goal.lua @@ -24,7 +24,7 @@ do -- Goal - --- @type GOAL + -- @type GOAL -- @extends Core.Fsm#FSM --- Models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized. @@ -71,10 +71,10 @@ do -- Goal ClassName = "GOAL", } - --- @field #table GOAL.Players + -- @field #table GOAL.Players GOAL.Players = {} - --- @field #number GOAL.TotalContributions + -- @field #number GOAL.TotalContributions GOAL.TotalContributions = 0 --- GOAL Constructor. @@ -145,7 +145,7 @@ do -- Goal self.TotalContributions = self.TotalContributions + 1 end - --- @param #GOAL self + -- @param #GOAL self -- @param #number Player contribution. function GOAL:GetPlayerContribution( PlayerName ) return self.Players[PlayerName] or 0 diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 8a6edf657..1d0f1b1d9 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -166,7 +166,7 @@ function MENU_INDEX:Refresh( Group ) end do -- MENU_BASE - --- @type MENU_BASE + -- @type MENU_BASE -- @extends Core.Base#BASE --- Defines the main MENU class where other MENU classes are derived from. -- This is an abstract class, so don't use it. @@ -278,7 +278,7 @@ do -- MENU_BASE end do -- MENU_COMMAND_BASE - --- @type MENU_COMMAND_BASE + -- @type MENU_COMMAND_BASE -- @field #function MenuCallHandler -- @extends Core.Menu#MENU_BASE @@ -344,7 +344,7 @@ do -- MENU_COMMAND_BASE end do -- MENU_MISSION - --- @type MENU_MISSION + -- @type MENU_MISSION -- @extends Core.Menu#MENU_BASE --- Manages the main menus for a complete mission. -- @@ -433,7 +433,7 @@ do -- MENU_MISSION end do -- MENU_MISSION_COMMAND - --- @type MENU_MISSION_COMMAND + -- @type MENU_MISSION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE --- Manages the command menus for a complete mission, which allow players to execute functions during mission execution. @@ -510,7 +510,7 @@ do -- MENU_MISSION_COMMAND end end do -- MENU_COALITION - --- @type MENU_COALITION + -- @type MENU_COALITION -- @extends Core.Menu#MENU_BASE --- Manages the main menus for @{DCS.coalition}s. @@ -637,7 +637,7 @@ do -- MENU_COALITION end do -- MENU_COALITION_COMMAND - --- @type MENU_COALITION_COMMAND + -- @type MENU_COALITION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE --- Manages the command menus for coalitions, which allow players to execute functions during mission execution. @@ -726,7 +726,7 @@ do -- the same menus twice during initialization logic. -- These menu classes are handling this logic with this variable. local _MENUGROUPS = {} - --- @type MENU_GROUP + -- @type MENU_GROUP -- @extends Core.Menu#MENU_BASE @@ -757,7 +757,7 @@ do -- MenuStatus[MenuGroupName]:Remove() -- end -- - -- --- @param Wrapper.Group#GROUP MenuGroup + -- -- @param Wrapper.Group#GROUP MenuGroup -- local function AddStatusMenu( MenuGroup ) -- local MenuGroupName = MenuGroup:GetName() -- -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. @@ -900,7 +900,7 @@ do end - --- @type MENU_GROUP_COMMAND + -- @type MENU_GROUP_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE --- The @{Core.Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. @@ -983,7 +983,7 @@ do end --- MENU_GROUP_DELAYED do - --- @type MENU_GROUP_DELAYED + -- @type MENU_GROUP_DELAYED -- @extends Core.Menu#MENU_BASE @@ -1107,7 +1107,7 @@ do end - --- @type MENU_GROUP_COMMAND_DELAYED + -- @type MENU_GROUP_COMMAND_DELAYED -- @extends Core.Menu#MENU_COMMAND_BASE --- Manages the command menus for coalitions, which allow players to execute functions during mission execution. diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index d942e49ba..91287229c 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -41,7 +41,7 @@ do -- COORDINATE - --- @type COORDINATE + -- @type COORDINATE -- @field #string ClassName Name of the class -- @field #number x Component of the 3D vector. -- @field #number y Component of the 3D vector. @@ -3592,7 +3592,7 @@ end do -- POINT_VEC2 - --- @type POINT_VEC2 + -- @type POINT_VEC2 -- @field DCS#Distance x The x coordinate in meters. -- @field DCS#Distance y the y coordinate in meters. -- @extends Core.Point#COORDINATE diff --git a/Moose Development/Moose/Core/Report.lua b/Moose Development/Moose/Core/Report.lua index d8225adcc..7b592f42d 100644 --- a/Moose Development/Moose/Core/Report.lua +++ b/Moose Development/Moose/Core/Report.lua @@ -15,7 +15,7 @@ -- @module Core.Report -- @image Core_Report.JPG ---- @type REPORT +-- @type REPORT -- @extends Core.Base#BASE --- Provides a handy means to create messages and reports. diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 1d06739f9..d7e7b6f97 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -47,7 +47,7 @@ do -- SET_BASE - --- @type SET_BASE + -- @type SET_BASE -- @field #table Filter Table of filters. -- @field #table Set Table of objects. -- @field #table Index Table of indices. @@ -578,8 +578,8 @@ do -- SET_BASE end ----- Private method that registers all alive players in the mission. - ---- @param #SET_BASE self - ---- @return #SET_BASE self + -- @param #SET_BASE self + -- @return #SET_BASE self -- function SET_BASE:_RegisterPlayers() -- -- local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } @@ -796,8 +796,8 @@ do -- SET_BASE ----- Iterate the SET_BASE and call an iterator function for each **alive** unit, providing the Unit and optional parameters. - ---- @param #SET_BASE self - ---- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self -- function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) -- self:F3( arg ) @@ -808,8 +808,8 @@ do -- SET_BASE -- end -- ----- Iterate the SET_BASE and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters. - ---- @param #SET_BASE self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self -- function SET_BASE:ForEachPlayer( IteratorFunction, ... ) -- self:F3( arg ) @@ -821,8 +821,8 @@ do -- SET_BASE -- -- ----- Iterate the SET_BASE and call an iterator function for each client, providing the Client to the function and optional parameters. - ---- @param #SET_BASE self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. ---- @return #SET_BASE self -- function SET_BASE:ForEachClient( IteratorFunction, ... ) -- self:F3( arg ) @@ -903,7 +903,7 @@ end do -- SET_GROUP - --- @type SET_GROUP #SET_GROUP + -- @type SET_GROUP #SET_GROUP -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -1568,7 +1568,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsCompletelyInZone( ZoneObject ) then @@ -1590,7 +1590,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsPartlyInZone( ZoneObject ) then @@ -1612,7 +1612,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsNotInZone( ZoneObject ) then @@ -1659,7 +1659,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsAnyInZone( ZoneObject ) then @@ -1857,8 +1857,8 @@ do -- SET_GROUP end ----- Iterate the SET_GROUP and call an iterator function for each **alive** player, providing the Group of the player and optional parameters. - ---- @param #SET_GROUP self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. + -- @param #SET_GROUP self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. ---- @return #SET_GROUP self -- function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) -- self:F2( arg ) @@ -1870,8 +1870,8 @@ do -- SET_GROUP -- -- ----- Iterate the SET_GROUP and call an iterator function for each client, providing the Client to the function and optional parameters. - ---- @param #SET_GROUP self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. + -- @param #SET_GROUP self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. ---- @return #SET_GROUP self -- function SET_GROUP:ForEachClient( IteratorFunction, ... ) -- self:F2( arg ) @@ -2012,7 +2012,7 @@ end do -- SET_UNIT - --- @type SET_UNIT + -- @type SET_UNIT -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -2672,7 +2672,7 @@ do -- SET_UNIT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject function( ZoneObject, UnitObject ) if UnitObject:IsInZone( ZoneObject ) then @@ -2694,7 +2694,7 @@ do -- SET_UNIT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject function( ZoneObject, UnitObject ) if UnitObject:IsNotInZone( ZoneObject ) then @@ -3011,8 +3011,8 @@ do -- SET_UNIT ----- Iterate the SET_UNIT and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters. - ---- @param #SET_UNIT self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. + -- @param #SET_UNIT self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. ---- @return #SET_UNIT self -- function SET_UNIT:ForEachPlayer( IteratorFunction, ... ) -- self:F2( arg ) @@ -3024,8 +3024,8 @@ do -- SET_UNIT -- -- ----- Iterate the SET_UNIT and call an iterator function for each client, providing the Client to the function and optional parameters. - ---- @param #SET_UNIT self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. + -- @param #SET_UNIT self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. ---- @return #SET_UNIT self -- function SET_UNIT:ForEachClient( IteratorFunction, ... ) -- self:F2( arg ) @@ -3192,7 +3192,7 @@ end do -- SET_STATIC - --- @type SET_STATIC + -- @type SET_STATIC -- @extends Core.Set#SET_BASE --- Mission designers can use the SET_STATIC class to build sets of Statics belonging to certain: @@ -3612,7 +3612,7 @@ do -- SET_STATIC self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject function( ZoneObject, StaticObject ) if StaticObject:IsInZone( ZoneObject ) then @@ -3634,7 +3634,7 @@ do -- SET_STATIC self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject function( ZoneObject, StaticObject ) if StaticObject:IsNotInZone( ZoneObject ) then @@ -3946,7 +3946,7 @@ end do -- SET_CLIENT - --- @type SET_CLIENT + -- @type SET_CLIENT -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -4369,7 +4369,7 @@ do -- SET_CLIENT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsInZone( ZoneObject ) then @@ -4391,7 +4391,7 @@ do -- SET_CLIENT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsNotInZone( ZoneObject ) then @@ -4573,7 +4573,7 @@ end do -- SET_PLAYER - --- @type SET_PLAYER + -- @type SET_PLAYER -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_PLAYER} class to build sets of units belonging to alive players: @@ -4868,7 +4868,7 @@ do -- SET_PLAYER self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsInZone( ZoneObject ) then @@ -4890,7 +4890,7 @@ do -- SET_PLAYER self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsNotInZone( ZoneObject ) then @@ -4999,7 +4999,7 @@ end do -- SET_AIRBASE - --- @type SET_AIRBASE + -- @type SET_AIRBASE -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: @@ -5342,7 +5342,7 @@ end do -- SET_CARGO - --- @type SET_CARGO + -- @type SET_CARGO -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_CARGO} class to build sets of cargos optionally belonging to certain: @@ -5768,7 +5768,7 @@ end do -- SET_ZONE - --- @type SET_ZONE + -- @type SET_ZONE -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_ZONE} class to build sets of zones of various types. @@ -6136,7 +6136,7 @@ end do -- SET_ZONE_GOAL - --- @type SET_ZONE_GOAL + -- @type SET_ZONE_GOAL -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_ZONE_GOAL} class to build sets of zones of various types. @@ -6447,7 +6447,7 @@ end do -- SET_OPSZONE - --- @type SET_OPSZONE + -- @type SET_OPSZONE -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_OPSZONE} class to build sets of zones of various types. @@ -6904,7 +6904,7 @@ end do -- SET_OPSGROUP - --- @type SET_OPSGROUP + -- @type SET_OPSGROUP -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_OPSGROUP} class to build sets of OPS groups belonging to certain: diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 671b4cfb0..73cbd2a98 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -29,7 +29,7 @@ -- @module Core.Settings -- @image Core_Settings.JPG ---- @type SETTINGS +-- @type SETTINGS -- @extends Core.Base#BASE --- Takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework. @@ -218,7 +218,7 @@ SETTINGS = { SETTINGS.__Enum = {} ---- @type SETTINGS.__Enum.Era +-- @type SETTINGS.__Enum.Era -- @field #number WWII -- @field #number Korea -- @field #number Cold @@ -491,7 +491,7 @@ do -- SETTINGS return (self.A2ASystem and self.A2ASystem == "MGRS") or (not self.A2ASystem and _SETTINGS:IsA2A_MGRS()) end - --- @param #SETTINGS self + -- @param #SETTINGS self -- @param Wrapper.Group#GROUP MenuGroup Group for which to add menus. -- @param #table RootMenu Root menu table -- @return #SETTINGS @@ -945,49 +945,49 @@ do -- SETTINGS return self end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem ) self.A2GSystem = A2GSystem MESSAGE:New( string.format( "Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll() self:SetSystemMenu( MenuGroup, RootMenu ) end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem ) self.A2ASystem = A2ASystem MESSAGE:New( string.format( "Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll() self:SetSystemMenu( MenuGroup, RootMenu ) end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy ) self.LL_Accuracy = LL_Accuracy MESSAGE:New( string.format( "Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll() self:SetSystemMenu( MenuGroup, RootMenu ) end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy ) self.MGRS_Accuracy = MGRS_Accuracy MESSAGE:New( string.format( "Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll() self:SetSystemMenu( MenuGroup, RootMenu ) end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW ) self.Metric = MW MESSAGE:New( string.format( "Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll() self:SetSystemMenu( MenuGroup, RootMenu ) end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuMessageTimingsSystem( MenuGroup, RootMenu, MessageType, MessageTime ) self:SetMessageTime( MessageType, MessageTime ) MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToAll() end do - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem ) BASE:E( { self, PlayerUnit:GetName(), A2GSystem } ) self.A2GSystem = A2GSystem @@ -998,7 +998,7 @@ do -- SETTINGS end end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem ) self.A2ASystem = A2ASystem MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup ) @@ -1008,7 +1008,7 @@ do -- SETTINGS end end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy ) self.LL_Accuracy = LL_Accuracy MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup ) @@ -1018,7 +1018,7 @@ do -- SETTINGS end end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy ) self.MGRS_Accuracy = MGRS_Accuracy MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup ) @@ -1028,7 +1028,7 @@ do -- SETTINGS end end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW ) self.Metric = MW MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup ) @@ -1038,7 +1038,7 @@ do -- SETTINGS end end - --- @param #SETTINGS self + -- @param #SETTINGS self function SETTINGS:MenuGroupMessageTimingsSystem( PlayerUnit, PlayerGroup, PlayerName, MessageType, MessageTime ) self:SetMessageTime( MessageType, MessageTime ) MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToGroup( PlayerGroup ) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index b1370313f..0c3958929 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -268,7 +268,7 @@ SPAWN = { -- @type SPAWN.Takeoff -- @extends Wrapper.Group#GROUP.Takeoff ---- @field #SPAWN.Takeoff Takeoff +-- @field #SPAWN.Takeoff Takeoff SPAWN.Takeoff = { Air = 1, Runway = 2, @@ -276,7 +276,7 @@ SPAWN.Takeoff = { Cold = 4, } ---- @type SPAWN.SpawnZoneTable +-- @type SPAWN.SpawnZoneTable -- @list SpawnZone --- Creates the main object to spawn a @{Wrapper.Group} defined in the DCS ME. @@ -3419,7 +3419,7 @@ end -- TODO Need to delete this... _DATABASE does this now ... ---- @param #SPAWN self +-- @param #SPAWN self -- @param Core.Event#EVENTDATA EventData function SPAWN:_OnBirth( EventData ) self:F( self.SpawnTemplatePrefix ) @@ -3439,7 +3439,7 @@ function SPAWN:_OnBirth( EventData ) end ---- @param #SPAWN self +-- @param #SPAWN self -- @param Core.Event#EVENTDATA EventData function SPAWN:_OnDeadOrCrash( EventData ) self:F( self.SpawnTemplatePrefix ) diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 4420f70ed..2c38d389e 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -34,7 +34,7 @@ -- @module Core.SpawnStatic -- @image Core_Spawnstatic.JPG ---- @type SPAWNSTATIC +-- @type SPAWNSTATIC -- @field #string SpawnTemplatePrefix Name of the template group. -- @field #number CountryID Country ID. -- @field #number CoalitionID Coalition ID. diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index d87606937..48bb2cb84 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -33,7 +33,7 @@ do - --- @type SPOT + -- @type SPOT -- @extends Core.Fsm#FSM @@ -292,7 +292,7 @@ do self:__Lasing(-1) end - --- @param #SPOT self + -- @param #SPOT self -- @param Core.Event#EVENTDATA EventData function SPOT:OnEventDead(EventData) self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } ) @@ -311,7 +311,7 @@ do end end - --- @param #SPOT self + -- @param #SPOT self -- @param From -- @param Event -- @param To @@ -337,7 +337,7 @@ do end - --- @param #SPOT self + -- @param #SPOT self -- @param From -- @param Event -- @param To diff --git a/Moose Development/Moose/Core/UserFlag.lua b/Moose Development/Moose/Core/UserFlag.lua index 5ed5d6cd7..3a277e49b 100644 --- a/Moose Development/Moose/Core/UserFlag.lua +++ b/Moose Development/Moose/Core/UserFlag.lua @@ -18,7 +18,7 @@ do -- UserFlag - --- @type USERFLAG + -- @type USERFLAG -- @field #string ClassName Name of the class -- @field #string UserFlagName Name of the flag. -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Core/Velocity.lua b/Moose Development/Moose/Core/Velocity.lua index e71282208..1181271c7 100644 --- a/Moose Development/Moose/Core/Velocity.lua +++ b/Moose Development/Moose/Core/Velocity.lua @@ -20,7 +20,7 @@ do -- Velocity - --- @type VELOCITY + -- @type VELOCITY -- @extends Core.Base#BASE @@ -127,7 +127,7 @@ end do -- VELOCITY_POSITIONABLE - --- @type VELOCITY_POSITIONABLE + -- @type VELOCITY_POSITIONABLE -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 12391027c..8859ca7ed 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -53,7 +53,7 @@ -- @module Core.Zone -- @image Core_Zones.JPG ---- @type ZONE_BASE +-- @type ZONE_BASE -- @field #string ZoneName Name of the zone. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -- @field #number DrawID Unique ID of the drawn zone on the F10 map. @@ -1496,7 +1496,7 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma end ---- @type ZONE +-- @type ZONE -- @extends #ZONE_RADIUS @@ -1581,7 +1581,7 @@ end ---- @type ZONE_UNIT +-- @type ZONE_UNIT -- @field Wrapper.Unit#UNIT ZoneUNIT -- @extends Core.Zone#ZONE_RADIUS @@ -1723,7 +1723,7 @@ function ZONE_UNIT:GetVec3( Height ) return Vec3 end ---- @type ZONE_GROUP +-- @type ZONE_GROUP -- @extends #ZONE_RADIUS @@ -1809,7 +1809,7 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer ) end ---- @type ZONE_POLYGON_BASE +-- @type ZONE_POLYGON_BASE -- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}. -- @extends #ZONE_BASE @@ -2458,7 +2458,7 @@ function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, C return self end ---- @type ZONE_POLYGON +-- @type ZONE_POLYGON -- @extends #ZONE_POLYGON_BASE @@ -2909,7 +2909,7 @@ end do -- ZONE_ELASTIC - --- @type ZONE_ELASTIC + -- @type ZONE_ELASTIC -- @field #table points Points in 2D. -- @field #table setGroups Set of GROUPs. -- @field #table setOpsGroups Set of OPSGROUPS. @@ -3109,7 +3109,7 @@ end do -- ZONE_AIRBASE - --- @type ZONE_AIRBASE + -- @type ZONE_AIRBASE -- @field #boolean isShip If `true`, airbase is a ship. -- @field #boolean isHelipad If `true`, airbase is a helipad. -- @field #boolean isAirdrome If `true`, airbase is an airdrome. diff --git a/Moose Development/Moose/Core/Zone_Detection.lua b/Moose Development/Moose/Core/Zone_Detection.lua index bb5424a37..b7f453952 100644 --- a/Moose Development/Moose/Core/Zone_Detection.lua +++ b/Moose Development/Moose/Core/Zone_Detection.lua @@ -2,7 +2,7 @@ -- @module Core.Zone_Detection -- @image MOOSE.JPG ---- @type ZONE_DETECTION +-- @type ZONE_DETECTION -- @field DCS#Vec2 Vec2 The current location of the zone. -- @field DCS#Distance Radius The radius of the zone. -- @extends #ZONE_BASE diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 7635d03a9..605b008be 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -345,11 +345,11 @@ end -- country do -- Command - --- @type Command + -- @type Command -- @field #string id -- @field #Command.params params - --- @type Command.params + -- @type Command.params end -- Command @@ -390,7 +390,7 @@ end -- coalition do -- Types - --- @type Desc + -- @type Desc -- @field #number speedMax0 Max speed in meters/second at zero altitude. -- @field #number massEmpty Empty mass in kg. -- @field #number tankerType Type of refueling system: 0=boom, 1=probe. @@ -487,16 +487,16 @@ do -- Types -- @type AttributeNameArray -- @list <#AttributeName> - --- @type Zone + -- @type Zone -- @field DCSVec3#Vec3 point -- @field #number radius Zone = {} - --- @type ModelTime + -- @type ModelTime -- @extends #number - --- @type Time + -- @type Time -- @extends #number --- A task descriptor (internal structure for DCS World). See [https://wiki.hoggitworld.com/view/Category:Tasks](https://wiki.hoggitworld.com/view/Category:Tasks). @@ -505,7 +505,7 @@ do -- Types -- @field #string id -- @field #Task.param param - --- @type Task.param + -- @type Task.param --- List of @{#Task} -- @type TaskArray @@ -552,7 +552,7 @@ do -- Object -- @field SCENERY -- @field CARGO - --- @type Object.Desc + -- @type Object.Desc -- @extends #Desc -- @field #number life initial life level -- @field #Box3 box bounding box of collision geometry @@ -994,7 +994,7 @@ end -- Controller do -- Unit - --- @type Unit + -- @type Unit -- @extends #CoalitionObject -- @field ID Identifier of an unit. It assigned to an unit by the Mission Editor automatically. -- @field #Unit.Category Category @@ -1109,15 +1109,15 @@ do -- Unit -- @field #Distance detectionDistanceHRM detection distance for RCS=1m^2 in high-resolution mapping mode, nil if radar has no HRM -- @field #Unit.Radar.detectionDistanceAir detectionDistanceAir detection distance for RCS=1m^2 airborne target, nil if radar doesn't support air search - --- @type Unit.Radar.detectionDistanceAir + -- @type Unit.Radar.detectionDistanceAir -- @field #Unit.Radar.detectionDistanceAir.upperHemisphere upperHemisphere -- @field #Unit.Radar.detectionDistanceAir.lowerHemisphere lowerHemisphere - --- @type Unit.Radar.detectionDistanceAir.upperHemisphere + -- @type Unit.Radar.detectionDistanceAir.upperHemisphere -- @field #Distance headOn -- @field #Distance tailOn - --- @type Unit.Radar.detectionDistanceAir.lowerHemisphere + -- @type Unit.Radar.detectionDistanceAir.lowerHemisphere -- @field #Distance headOn -- @field #Distance tailOn @@ -1419,22 +1419,22 @@ do -- AI -- @field IR_POINTER -- @field LASER - --- @type AI.Task.WaypointType + -- @type AI.Task.WaypointType -- @field TAKEOFF -- @field TAKEOFF_PARKING -- @field TURNING_POINT -- @field TAKEOFF_PARKING_HOT -- @field LAND - --- @type AI.Task.TurnMethod + -- @type AI.Task.TurnMethod -- @field FLY_OVER_POINT -- @field FIN_POINT - --- @type AI.Task.AltitudeType + -- @type AI.Task.AltitudeType -- @field BARO -- @field RADIO - --- @type AI.Task.VehicleFormation + -- @type AI.Task.VehicleFormation -- @field OFF_ROAD -- @field ON_ROAD -- @field RANK @@ -1444,27 +1444,27 @@ do -- AI -- @field ECHELON_LEFT -- @field ECHELON_RIGHT - --- @type AI.Option + -- @type AI.Option -- @field #AI.Option.Air Air -- @field #AI.Option.Ground Ground -- @field #AI.Option.Naval Naval - --- @type AI.Option.Air + -- @type AI.Option.Air -- @field #AI.Option.Air.id id -- @field #AI.Option.Air.val val - --- @type AI.Option.Ground + -- @type AI.Option.Ground -- @field #AI.Option.Ground.id id -- @field #AI.Option.Ground.val val -- @field #AI.Option.Ground.mid mid -- @field #AI.Option.Ground.mval mval -- - --- @type AI.Option.Naval + -- @type AI.Option.Naval -- @field #AI.Option.Naval.id id -- @field #AI.Option.Naval.val val - --- @type AI.Option.Air.id + -- @type AI.Option.Air.id -- @field NO_OPTION -- @field ROE -- @field REACTION_ON_THREAT @@ -1487,7 +1487,7 @@ do -- AI -- @field JETT_TANKS_IF_EMPTY -- @field FORCED_ATTACK - --- @type AI.Option.Air.id.FORMATION + -- @type AI.Option.Air.id.FORMATION -- @field LINE_ABREAST -- @field TRAIL -- @field WEDGE @@ -1506,45 +1506,45 @@ do -- AI -- @field JAVELIN_DOWN - --- @type AI.Option.Air.val + -- @type AI.Option.Air.val -- @field #AI.Option.Air.val.ROE ROE -- @field #AI.Option.Air.val.REACTION_ON_THREAT REACTION_ON_THREAT -- @field #AI.Option.Air.val.RADAR_USING RADAR_USING -- @field #AI.Option.Air.val.FLARE_USING FLARE_USING - --- @type AI.Option.Air.val.ROE + -- @type AI.Option.Air.val.ROE -- @field WEAPON_FREE -- @field OPEN_FIRE_WEAPON_FREE -- @field OPEN_FIRE -- @field RETURN_FIRE -- @field WEAPON_HOLD - --- @type AI.Option.Air.val.REACTION_ON_THREAT + -- @type AI.Option.Air.val.REACTION_ON_THREAT -- @field NO_REACTION -- @field PASSIVE_DEFENCE -- @field EVADE_FIRE -- @field BYPASS_AND_ESCAPE -- @field ALLOW_ABORT_MISSION - --- @type AI.Option.Air.val.RADAR_USING + -- @type AI.Option.Air.val.RADAR_USING -- @field NEVER -- @field FOR_ATTACK_ONLY -- @field FOR_SEARCH_IF_REQUIRED -- @field FOR_CONTINUOUS_SEARCH - --- @type AI.Option.Air.val.FLARE_USING + -- @type AI.Option.Air.val.FLARE_USING -- @field NEVER -- @field AGAINST_FIRED_MISSILE -- @field WHEN_FLYING_IN_SAM_WEZ -- @field WHEN_FLYING_NEAR_ENEMIES - --- @type AI.Option.Air.val.ECM_USING + -- @type AI.Option.Air.val.ECM_USING -- @field NEVER_USE -- @field USE_IF_ONLY_LOCK_BY_RADAR -- @field USE_IF_DETECTED_LOCK_BY_RADAR -- @field ALWAYS_USE - --- @type AI.Option.Air.val.MISSILE_ATTACK + -- @type AI.Option.Air.val.MISSILE_ATTACK -- @field MAX_RANGE -- @field NEZ_RANGE -- @field HALF_WAY_RMAX_NEZ @@ -1552,7 +1552,7 @@ do -- AI -- @field RANDOM_RANGE - --- @type AI.Option.Ground.id + -- @type AI.Option.Ground.id -- @field NO_OPTION -- @field ROE @{#AI.Option.Ground.val.ROE} -- @field FORMATION @@ -1561,42 +1561,42 @@ do -- AI -- @field ENGAGE_AIR_WEAPONS -- @field AC_ENGAGEMENT_RANGE_RESTRICTION - --- @type AI.Option.Ground.mid -- Moose added + -- @type AI.Option.Ground.mid -- Moose added -- @field RESTRICT_AAA_MIN 27 -- @field RESTRICT_AAA_MAX 29 -- @field RESTRICT_TARGETS @{#AI.Option.Ground.mval.ENGAGE_TARGETS} 28 - --- @type AI.Option.Ground.val + -- @type AI.Option.Ground.val -- @field #AI.Option.Ground.val.ROE ROE -- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE -- @field #AI.Option.Ground.val.ENGAGE_TARGETS RESTRICT_TARGETS - --- @type AI.Option.Ground.val.ROE + -- @type AI.Option.Ground.val.ROE -- @field OPEN_FIRE -- @field RETURN_FIRE -- @field WEAPON_HOLD - --- @type AI.Option.Ground.mval -- Moose added + -- @type AI.Option.Ground.mval -- Moose added -- @field #AI.Option.Ground.mval.ENGAGE_TARGETS ENGAGE_TARGETS - --- @type AI.Option.Ground.mval.ENGAGE_TARGETS -- Moose added + -- @type AI.Option.Ground.mval.ENGAGE_TARGETS -- Moose added -- @field ANY_TARGET -- 0 -- @field AIR_UNITS_ONLY -- 1 -- @field GROUND_UNITS_ONLY -- 2 - --- @type AI.Option.Ground.val.ALARM_STATE + -- @type AI.Option.Ground.val.ALARM_STATE -- @field AUTO -- @field GREEN -- @field RED - --- @type AI.Option.Naval.id + -- @type AI.Option.Naval.id -- @field NO_OPTION -- @field ROE - --- @type AI.Option.Naval.val + -- @type AI.Option.Naval.val -- @field #AI.Option.Naval.val.ROE ROE - --- @type AI.Option.Naval.val.ROE + -- @type AI.Option.Naval.val.ROE -- @field OPEN_FIRE -- @field RETURN_FIRE -- @field WEAPON_HOLD diff --git a/Moose Development/Moose/Functional/ATC_Ground.lua b/Moose Development/Moose/Functional/ATC_Ground.lua index 5e89ff144..74cdae7cd 100644 --- a/Moose Development/Moose/Functional/ATC_Ground.lua +++ b/Moose Development/Moose/Functional/ATC_Ground.lua @@ -26,7 +26,7 @@ -- @module Functional.ATC_Ground -- @image Air_Traffic_Control_Ground_Operations.JPG ---- @type ATC_GROUND +-- @type ATC_GROUND -- @field Core.Set#SET_CLIENT SetClient -- @extends Core.Base#BASE @@ -39,7 +39,7 @@ ATC_GROUND = { AirbaseNames = nil, } ---- @type ATC_GROUND.AirbaseNames +-- @type ATC_GROUND.AirbaseNames -- @list <#string> @@ -82,7 +82,7 @@ function ATC_GROUND:New( Airbases, AirbaseList ) end self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Client#CLIENT Client function( Client ) Client:SetState( self, "Speeding", false ) Client:SetState( self, "Warnings", 0) @@ -246,11 +246,11 @@ function ATC_GROUND:SetMaximumKickSpeedMiph( MaximumKickSpeedMiph, Airbase ) return self end ---- @param #ATC_GROUND self +-- @param #ATC_GROUND self function ATC_GROUND:_AirbaseMonitor() self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Client#CLIENT Client function( Client ) if Client:IsAlive() then @@ -331,7 +331,7 @@ function ATC_GROUND:_AirbaseMonitor() Client:SetState( self, "Warnings", SpeedingWarnings + 1 ) else MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() - --- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Client#CLIENT Client Client:Destroy() Client:SetState( self, "Speeding", false ) Client:SetState( self, "Warnings", 0 ) @@ -363,7 +363,7 @@ function ATC_GROUND:_AirbaseMonitor() Client:SetState( self, "OffRunwayWarnings", OffRunwayWarnings + 1 ) else MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() - --- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Client#CLIENT Client Client:Destroy() Client:SetState( self, "IsOffRunway", false ) Client:SetState( self, "OffRunwayWarnings", 0 ) @@ -461,7 +461,7 @@ function ATC_GROUND_UNIVERSAL:New(AirbaseList) end self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Client#CLIENT Client function( Client ) Client:SetState( self, "Speeding", false ) Client:SetState( self, "Warnings", 0) @@ -681,7 +681,7 @@ end function ATC_GROUND_UNIVERSAL:_AirbaseMonitor() self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Client#CLIENT Client function( Client ) if Client:IsAlive() then @@ -766,7 +766,7 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor() Client:SetState( self, "Warnings", SpeedingWarnings + 1 ) else MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() - --- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Client#CLIENT Client Client:Destroy() Client:SetState( self, "Speeding", false ) Client:SetState( self, "Warnings", 0 ) @@ -798,7 +798,7 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor() Client:SetState( self, "OffRunwayWarnings", OffRunwayWarnings + 1 ) else MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() - --- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Client#CLIENT Client Client:Destroy() Client:SetState( self, "IsOffRunway", false ) Client:SetState( self, "OffRunwayWarnings", 0 ) @@ -846,7 +846,7 @@ function ATC_GROUND_UNIVERSAL:Start( RepeatScanSeconds ) return self end ---- @type ATC_GROUND_CAUCASUS +-- @type ATC_GROUND_CAUCASUS -- @extends #ATC_GROUND --- # ATC\_GROUND\_CAUCASUS, extends @{#ATC_GROUND_UNIVERSAL} @@ -986,7 +986,7 @@ end ---- @type ATC_GROUND_NEVADA +-- @type ATC_GROUND_NEVADA -- @extends #ATC_GROUND @@ -1124,7 +1124,7 @@ function ATC_GROUND_NEVADA:Start( RepeatScanSeconds ) end ---- @type ATC_GROUND_NORMANDY +-- @type ATC_GROUND_NORMANDY -- @extends #ATC_GROUND @@ -1280,7 +1280,7 @@ function ATC_GROUND_NORMANDY:Start( RepeatScanSeconds ) self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) end ---- @type ATC_GROUND_PERSIANGULF +-- @type ATC_GROUND_PERSIANGULF -- @extends #ATC_GROUND @@ -1423,7 +1423,7 @@ function ATC_GROUND_PERSIANGULF:Start( RepeatScanSeconds ) end - --- @type ATC_GROUND_MARIANAISLANDS + -- @type ATC_GROUND_MARIANAISLANDS -- @extends #ATC_GROUND @@ -1517,7 +1517,7 @@ end -- * @{#ATC_GROUND.SetMaximumKickSpeedKmph}(): Set the maximum speed allowed at an airbase in kilometers per hour. -- * @{#ATC_GROUND.SetMaximumKickSpeedMiph}(): Set the maximum speed allowed at an airbase in miles per hour. -- ----- @field #ATC_GROUND_MARIANAISLANDS +-- @field #ATC_GROUND_MARIANAISLANDS ATC_GROUND_MARIANAISLANDS = { ClassName = "ATC_GROUND_MARIANAISLANDS", } @@ -1529,7 +1529,7 @@ ATC_GROUND_MARIANAISLANDS = { function ATC_GROUND_MARIANAISLANDS:New( AirbaseNames ) -- Inherits from BASE - local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( self.Airbases, AirbaseNames ) ) + local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( AirbaseNames ) ) self:SetKickSpeedKmph( 50 ) self:SetMaximumKickSpeedKmph( 150 ) diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index 994147589..4ba1d3e4c 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -52,11 +52,11 @@ -- @module Functional.CleanUp -- @image CleanUp_Airbases.JPG ---- @type CLEANUP_AIRBASE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) +-- @type CLEANUP_AIRBASE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) -- @field #map<#string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases. -- @extends Core.Base#BASE ---- @type CLEANUP_AIRBASE +-- @type CLEANUP_AIRBASE -- @extends #CLEANUP_AIRBASE.__ --- Keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat. @@ -93,7 +93,7 @@ CLEANUP_AIRBASE = { -- @field #CLEANUP_AIRBASE.__ CLEANUP_AIRBASE.__ = {} ---- @field #CLEANUP_AIRBASE.__.Airbases +-- @field #CLEANUP_AIRBASE.__.Airbases CLEANUP_AIRBASE.__.Airbases = {} --- Creates the main object which is handling the cleaning of the debris within the given Zone Names. @@ -240,7 +240,8 @@ function CLEANUP_AIRBASE.__:DestroyMissile( MissileObject ) end end ---- @param #CLEANUP_AIRBASE self +--- +-- @param #CLEANUP_AIRBASE self -- @param Core.Event#EVENTDATA EventData function CLEANUP_AIRBASE.__:OnEventBirth( EventData ) self:F( { EventData } ) diff --git a/Moose Development/Moose/Functional/Designate.lua b/Moose Development/Moose/Functional/Designate.lua index 7bc0c7a82..ec5676b40 100644 --- a/Moose Development/Moose/Functional/Designate.lua +++ b/Moose Development/Moose/Functional/Designate.lua @@ -182,7 +182,7 @@ do -- DESIGNATE - --- @type DESIGNATE + -- @type DESIGNATE -- @extends Core.Fsm#FSM_PROCESS --- Manage the designation of detected targets. @@ -523,7 +523,7 @@ do -- DESIGNATE self.AttackSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP AttackGroup + -- @param Wrapper.Group#GROUP AttackGroup function( AttackGroup ) self.FlashStatusMenu[AttackGroup] = FlashMenu end @@ -552,7 +552,7 @@ do -- DESIGNATE self.AttackSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP AttackGroup + -- @param Wrapper.Group#GROUP AttackGroup function( AttackGroup ) self.FlashDetectionMessage[AttackGroup] = FlashDetectionMessage end @@ -824,7 +824,7 @@ do -- DESIGNATE -- This Detection is obsolete, remove from the designate scope self.Designating[DesignateIndex] = nil self.AttackSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP AttackGroup + -- @param Wrapper.Group#GROUP AttackGroup function( AttackGroup ) if AttackGroup:IsAlive() == true then local DetectionText = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup ):Text( ", " ) @@ -901,7 +901,7 @@ do -- DESIGNATE self.AttackSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP GroupReport + -- @param Wrapper.Group#GROUP GroupReport function( AttackGroup ) if self.FlashStatusMenu[AttackGroup] or ( MenuAttackGroup and ( AttackGroup:GetName() == MenuAttackGroup:GetName() ) ) then @@ -1058,7 +1058,7 @@ do -- DESIGNATE self.AttackSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP GroupReport + -- @param Wrapper.Group#GROUP GroupReport function( AttackGroup ) self:ScheduleOnce( Delay, self.SetMenu, self, AttackGroup ) @@ -1230,7 +1230,7 @@ do -- DESIGNATE if self.AutoLase or ( not self.AutoLase and ( self.LaseStart + Duration >= timer.getTime() ) ) then TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0, - --- @param Wrapper.Unit#UNIT SmokeUnit + -- @param Wrapper.Unit#UNIT SmokeUnit function( TargetUnit ) self:F( { TargetUnit = TargetUnit:GetName() } ) @@ -1390,7 +1390,7 @@ do -- DESIGNATE local MarkedCount = 0 TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0, - --- @param Wrapper.Unit#UNIT SmokeUnit + -- @param Wrapper.Unit#UNIT SmokeUnit function( SmokeUnit ) if MarkedCount < self.MaximumMarkings then diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index e7f76ef0f..24e3577dc 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -39,7 +39,7 @@ do -- DETECTION_BASE - --- @type DETECTION_BASE + -- @type DETECTION_BASE -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role. -- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected. -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. @@ -269,10 +269,10 @@ do -- DETECTION_BASE DetectedItemsByIndex = {}, } - --- @type DETECTION_BASE.DetectedObjects + -- @type DETECTION_BASE.DetectedObjects -- @list <#DETECTION_BASE.DetectedObject> - --- @type DETECTION_BASE.DetectedObject + -- @type DETECTION_BASE.DetectedObject -- @field #string Name -- @field #boolean IsVisible -- @field #boolean KnowType @@ -284,7 +284,7 @@ do -- DETECTION_BASE -- @field #boolean LastPos -- @field #number LastVelocity - --- @type DETECTION_BASE.DetectedItems + -- @type DETECTION_BASE.DetectedItems -- @list <#DETECTION_BASE.DetectedItem> --- Detected item data structure. @@ -522,7 +522,7 @@ do -- DETECTION_BASE do -- State Transition Handling - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. @@ -530,7 +530,7 @@ do -- DETECTION_BASE self:__Detect( 1 ) end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. @@ -570,7 +570,7 @@ do -- DETECTION_BASE end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #number The amount of alive recce. function DETECTION_BASE:CountAliveRecce() @@ -578,7 +578,7 @@ do -- DETECTION_BASE end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self function DETECTION_BASE:ForEachAliveRecce( IteratorFunction, ... ) self:F2( arg ) @@ -587,7 +587,7 @@ do -- DETECTION_BASE return self end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. @@ -1354,7 +1354,7 @@ do -- DETECTION_BASE } } - --- @param DCS#Unit FoundDCSUnit + -- @param DCS#Unit FoundDCSUnit -- @param Wrapper.Group#GROUP ReportGroup -- @param Core.Set#SET_GROUP ReportSetGroup local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) @@ -1419,7 +1419,7 @@ do -- DETECTION_BASE DetectedItem.PlayersNearBy = nil _DATABASE:ForEachPlayer( - --- @param Wrapper.Unit#UNIT PlayerUnit + -- @param Wrapper.Unit#UNIT PlayerUnit function( PlayerUnitName ) local PlayerUnit = UNIT:FindByName( PlayerUnitName ) @@ -1976,7 +1976,7 @@ end do -- DETECTION_UNITS - --- @type DETECTION_UNITS + -- @type DETECTION_UNITS -- @field DCS#Distance DetectionRange The range till which targets are detected. -- @extends Functional.Detection#DETECTION_BASE @@ -2232,7 +2232,7 @@ end do -- DETECTION_TYPES - --- @type DETECTION_TYPES + -- @type DETECTION_TYPES -- @extends Functional.Detection#DETECTION_BASE --- Will detect units within the battle zone. @@ -2434,7 +2434,7 @@ end do -- DETECTION_AREAS - --- @type DETECTION_AREAS + -- @type DETECTION_AREAS -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. -- @extends Functional.Detection#DETECTION_BASE @@ -2960,7 +2960,7 @@ do -- DETECTION_AREAS -- DetectedSet:Flush( self ) - DetectedSet:ForEachUnit( --- @param Wrapper.Unit#UNIT DetectedUnit + DetectedSet:ForEachUnit( -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit ) if DetectedUnit:IsAlive() then -- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() ) diff --git a/Moose Development/Moose/Functional/DetectionZones.lua b/Moose Development/Moose/Functional/DetectionZones.lua index 8cb9b9d10..2cef564e4 100644 --- a/Moose Development/Moose/Functional/DetectionZones.lua +++ b/Moose Development/Moose/Functional/DetectionZones.lua @@ -4,7 +4,7 @@ do -- DETECTION_ZONES - --- @type DETECTION_ZONES + -- @type DETECTION_ZONES -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. -- @extends Functional.Detection#DETECTION_BASE @@ -68,7 +68,7 @@ do -- DETECTION_ZONES return self end - --- @param #DETECTION_ZONES self + -- @param #DETECTION_ZONES self -- @param #number The amount of alive recce. function DETECTION_ZONES:CountAliveRecce() @@ -76,7 +76,7 @@ do -- DETECTION_ZONES end - --- @param #DETECTION_ZONES self + -- @param #DETECTION_ZONES self function DETECTION_ZONES:ForEachAliveRecce( IteratorFunction, ... ) self:F2( arg ) @@ -352,7 +352,7 @@ do -- DETECTION_ZONES --DetectedSet:Flush( self ) DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit ) if DetectedUnit:IsAlive() then --self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() ) @@ -380,7 +380,7 @@ do -- DETECTION_ZONES end - --- @param #DETECTION_ZONES self + -- @param #DETECTION_ZONES self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. diff --git a/Moose Development/Moose/Functional/Escort.lua b/Moose Development/Moose/Functional/Escort.lua index 57a9ed5da..90f9f925a 100644 --- a/Moose Development/Moose/Functional/Escort.lua +++ b/Moose Development/Moose/Functional/Escort.lua @@ -109,7 +109,7 @@ ---- @type ESCORT +-- @type ESCORT -- @extends Core.Base#BASE -- @field Wrapper.Client#CLIENT EscortClient -- @field Wrapper.Group#GROUP EscortGroup @@ -693,7 +693,7 @@ function ESCORT:MenuResumeMission() end ---- @param #MENUPARAM MenuParam +-- @param #MENUPARAM MenuParam function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) local EscortGroup = self.EscortGroup @@ -733,7 +733,7 @@ function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) end ---- @param #MENUPARAM MenuParam +-- @param #MENUPARAM MenuParam function ESCORT:_JoinUpAndFollow( Distance ) local EscortGroup = self.EscortGroup @@ -766,7 +766,7 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) end ---- @param #MENUPARAM MenuParam +-- @param #MENUPARAM MenuParam function ESCORT:_Flare( Color, Message ) local EscortGroup = self.EscortGroup @@ -776,7 +776,7 @@ function ESCORT:_Flare( Color, Message ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end ---- @param #MENUPARAM MenuParam +-- @param #MENUPARAM MenuParam function ESCORT:_Smoke( Color, Message ) local EscortGroup = self.EscortGroup @@ -787,7 +787,7 @@ function ESCORT:_Smoke( Color, Message ) end ---- @param #MENUPARAM MenuParam +-- @param #MENUPARAM MenuParam function ESCORT:_ReportNearbyTargetsNow() local EscortGroup = self.EscortGroup @@ -814,7 +814,7 @@ function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) end end ---- @param #MENUPARAM MenuParam +-- @param #MENUPARAM MenuParam function ESCORT:_ScanTargets( ScanDuration ) local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP @@ -844,7 +844,7 @@ function ESCORT:_ScanTargets( ScanDuration ) end ---- @param Wrapper.Group#GROUP EscortGroup +-- @param Wrapper.Group#GROUP EscortGroup function _Resume( EscortGroup ) env.info( '_Resume' ) @@ -856,7 +856,7 @@ function _Resume( EscortGroup ) end ---- @param #ESCORT self +-- @param #ESCORT self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem function ESCORT:_AttackTarget( DetectedItem ) @@ -877,7 +877,7 @@ function ESCORT:_AttackTarget( DetectedItem ) local Tasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then Tasks[#Tasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit ) @@ -900,7 +900,7 @@ function ESCORT:_AttackTarget( DetectedItem ) local Tasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) @@ -921,7 +921,7 @@ function ESCORT:_AttackTarget( DetectedItem ) end --- ---- @param #ESCORT self +-- @param #ESCORT self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItem ) @@ -939,7 +939,7 @@ function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItem ) local Tasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then Tasks[#Tasks+1] = EscortGroupAttack:TaskAttackUnit( DetectedUnit ) @@ -961,7 +961,7 @@ function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItem ) local Tasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then Tasks[#Tasks+1] = EscortGroupAttack:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) @@ -981,7 +981,7 @@ function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItem ) end ---- @param #MENUPARAM MenuParam +-- @param #MENUPARAM MenuParam function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) local EscortGroup = self.EscortGroup @@ -991,7 +991,7 @@ function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) end ---- @param #MENUPARAM MenuParam +-- @param #MENUPARAM MenuParam function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) local EscortGroup = self.EscortGroup @@ -1001,7 +1001,7 @@ function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) end ---- @param #MENUPARAM MenuParam +-- @param #MENUPARAM MenuParam function ESCORT:_ResumeMission( WayPoint ) local EscortGroup = self.EscortGroup @@ -1036,7 +1036,7 @@ function ESCORT:RegisterRoute() return TaskPoints end ---- @param Functional.Escort#ESCORT self +-- @param Functional.Escort#ESCORT self function ESCORT:_FollowScheduler() self:F( { self.FollowDistance } ) diff --git a/Moose Development/Moose/Functional/Fox.lua b/Moose Development/Moose/Functional/Fox.lua index 5d2e35865..6644890a5 100644 --- a/Moose Development/Moose/Functional/Fox.lua +++ b/Moose Development/Moose/Functional/Fox.lua @@ -142,7 +142,7 @@ FOX = { explosiondist = 200, explosiondist2 = 500, bigmissilemass = 50, - destroy = nil, + --destroy = nil, dt50 = 5, dt10 = 1, dt05 = 0.5, diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index 24f2046bc..72e9ddc97 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -68,7 +68,7 @@ -- @image Missile_Trainer.JPG ---- @type MISSILETRAINER +-- @type MISSILETRAINER -- @field Core.Set#SET_CLIENT DBClients -- @extends Core.Base#BASE @@ -205,7 +205,7 @@ function MISSILETRAINER:New( Distance, Briefing ) -- self.DB:ForEachClient( --- --- @param Wrapper.Client#CLIENT Client +-- -- @param Wrapper.Client#CLIENT Client -- function( Client ) -- -- ... actions ... diff --git a/Moose Development/Moose/Functional/Movement.lua b/Moose Development/Moose/Functional/Movement.lua index bcb8bb9c1..3b4031433 100644 --- a/Moose Development/Moose/Functional/Movement.lua +++ b/Moose Development/Moose/Functional/Movement.lua @@ -10,7 +10,7 @@ -- @module Functional.Movement -- @image MOOSE.JPG ---- @type MOVEMENT +-- @type MOVEMENT -- @extends Core.Base#BASE --- diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 950ccd3c0..33d50f923 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -88,7 +88,7 @@ -- @module Functional.Scoring -- @image Scoring.JPG ---- @type SCORING +-- @type SCORING -- @field Players A collection of the current players that have joined the game. -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index ebcd2228c..0ed37dadb 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -66,7 +66,7 @@ SEAD = { -- @field Harms SEAD.Harms = { ["AGM_88"] = "AGM_88", - ["AGM_45"] = "AGM_45", + --["AGM_45"] = "AGM_45", ["AGM_122"] = "AGM_122", ["AGM_84"] = "AGM_84", ["AGM_45"] = "AGM_45", @@ -369,7 +369,7 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP local reach = 10 if hit then local wpndata = SEAD.HarmData[data] - reach = wpndata[1] * 1,1 + reach = wpndata[1] * 1.1 local mach = wpndata[2] wpnspeed = math.floor(mach * 340.29) end diff --git a/Moose Development/Moose/Functional/Shorad.lua b/Moose Development/Moose/Functional/Shorad.lua index e08f7fa29..8affe3cf6 100644 --- a/Moose Development/Moose/Functional/Shorad.lua +++ b/Moose Development/Moose/Functional/Shorad.lua @@ -112,7 +112,7 @@ do -- @field Harms SHORAD.Harms = { ["AGM_88"] = "AGM_88", - ["AGM_45"] = "AGM_45", + --["AGM_45"] = "AGM_45", ["AGM_122"] = "AGM_122", ["AGM_84"] = "AGM_84", ["AGM_45"] = "AGM_45", @@ -213,7 +213,7 @@ do if onoff then self:SwitchDebugOn() else - self.SwitchDebugOff() + self:SwitchDebugOff() end return self end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index f420a770e..3820c947b 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -8229,7 +8229,7 @@ end -- @return #number Request ID. function WAREHOUSE:_GetIDsFromGroupName(groupname) - ---@param #string text The text to analyse. + -- @param #string text The text to analyse. local function analyse(text) -- Get rid of #0001 tail from spawn. diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index 3215b9a12..eef5c72ff 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -48,7 +48,7 @@ do -- ZONE_CAPTURE_COALITION - --- @type ZONE_CAPTURE_COALITION + -- @type ZONE_CAPTURE_COALITION -- @field #string ClassName Name of the class. -- @field #number MarkBlue ID of blue F10 mark. -- @field #number MarkRed ID of red F10 mark. @@ -161,7 +161,7 @@ do -- ZONE_CAPTURE_COALITION -- The mission designer can use these values to alter the logic. -- For example: -- - -- --- @param Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION self + -- -- @param Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION self -- function ZoneCaptureCoalition:OnEnterGuarded( From, Event, To ) -- if From ~= "Empty" then -- -- Display a message @@ -172,7 +172,7 @@ do -- ZONE_CAPTURE_COALITION -- -- ## Example Event Handler. -- - -- --- @param Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION self + -- -- @param Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION self -- function ZoneCaptureCoalition:OnEnterGuarded( From, Event, To ) -- if From ~= To then -- local Coalition = self:GetCoalition() @@ -273,7 +273,7 @@ do -- ZONE_CAPTURE_COALITION -- Depending on the zone ownership, different messages are sent. -- Note the methods `ZoneCaptureCoalition:GetZoneName()`. -- - -- --- @param Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION self + -- -- @param Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION self -- function ZoneCaptureCoalition:OnEnterGuarded( From, Event, To ) -- if From ~= To then -- local Coalition = self:GetCoalition() @@ -294,7 +294,7 @@ do -- ZONE_CAPTURE_COALITION -- Next is the Event Handler when the **Empty** state transition is triggered. -- Now we smoke the ZoneCaptureCoalition with a green color, using `self:Smoke( SMOKECOLOR.Green )`. -- - -- --- @param Functional.Protect#ZONE_CAPTURE_COALITION self + -- -- @param Functional.Protect#ZONE_CAPTURE_COALITION self -- function ZoneCaptureCoalition:OnEnterEmpty() -- self:Smoke( SMOKECOLOR.Green ) -- US_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) @@ -304,7 +304,7 @@ do -- ZONE_CAPTURE_COALITION -- The next Event Handlers speak for itself. -- When the zone is Attacked, we smoke the zone white and send some messages to each coalition. -- - -- --- @param Functional.Protect#ZONE_CAPTURE_COALITION self + -- -- @param Functional.Protect#ZONE_CAPTURE_COALITION self -- function ZoneCaptureCoalition:OnEnterAttacked() -- ZoneCaptureCoalition:Smoke( SMOKECOLOR.White ) -- local Coalition = self:GetCoalition() @@ -321,7 +321,7 @@ do -- ZONE_CAPTURE_COALITION -- When the zone is Captured, we send some victory or loss messages to the correct coalition. -- And we add some score. -- - -- --- @param Functional.Protect#ZONE_CAPTURE_COALITION self + -- -- @param Functional.Protect#ZONE_CAPTURE_COALITION self -- function ZoneCaptureCoalition:OnEnterCaptured() -- local Coalition = self:GetCoalition() -- self:E({Coalition = Coalition}) @@ -641,7 +641,7 @@ do -- ZONE_CAPTURE_COALITION -- -- @usage -- -- For example, one could stop the monitoring when the zone was captured! - -- --- @param Functional.Protect#ZONE_CAPTURE_COALITION self + -- -- @param Functional.Protect#ZONE_CAPTURE_COALITION self -- function ZoneCaptureCoalition:OnEnterCaptured() -- local Coalition = self:GetCoalition() -- self:E({Coalition = Coalition}) diff --git a/Moose Development/Moose/Functional/ZoneGoal.lua b/Moose Development/Moose/Functional/ZoneGoal.lua index 1044e7920..2d0b5a4b3 100644 --- a/Moose Development/Moose/Functional/ZoneGoal.lua +++ b/Moose Development/Moose/Functional/ZoneGoal.lua @@ -17,7 +17,7 @@ do -- Zone - --- @type ZONE_GOAL + -- @type ZONE_GOAL -- @field #string ClassName Name of the class. -- @field Core.Goal#GOAL Goal The goal object. -- @field #number SmokeTime Time stamp in seconds when the last smoke of the zone was triggered. @@ -178,7 +178,7 @@ do -- Zone end - --- @param #ZONE_GOAL self + -- @param #ZONE_GOAL self -- @param Core.Event#EVENTDATA EventData Event data table. function ZONE_GOAL:__Destroyed( EventData ) self:F( { "EventDead", EventData } ) diff --git a/Moose Development/Moose/Functional/ZoneGoalCargo.lua b/Moose Development/Moose/Functional/ZoneGoalCargo.lua index fdd0deedf..3295cc2a4 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCargo.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCargo.lua @@ -16,7 +16,7 @@ do -- ZoneGoal - --- @type ZONE_GOAL_CARGO + -- @type ZONE_GOAL_CARGO -- @extends Functional.ZoneGoal#ZONE_GOAL @@ -50,7 +50,7 @@ do -- ZoneGoal ClassName = "ZONE_GOAL_CARGO", } - --- @field #table ZONE_GOAL_CARGO.States + -- @field #table ZONE_GOAL_CARGO.States ZONE_GOAL_CARGO.States = {} --- ZONE_GOAL_CARGO Constructor. diff --git a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua index 1f5da5440..df60a05eb 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua @@ -16,7 +16,7 @@ do -- ZoneGoal - --- @type ZONE_GOAL_COALITION + -- @type ZONE_GOAL_COALITION -- @field #string ClassName Name of the Class. -- @field #number Coalition The current coalition ID of the zone owner. -- @field #number PreviousCoalition The previous owner of the zone. @@ -48,7 +48,7 @@ do -- ZoneGoal ObjectCategories = nil, } - --- @field #table ZONE_GOAL_COALITION.States + -- @field #table ZONE_GOAL_COALITION.States ZONE_GOAL_COALITION.States = {} --- ZONE_GOAL_COALITION Constructor. diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 3780ba573..25b8dc81c 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: cast-local-type --- **Ops** - Automatic Terminal Information Service (ATIS). -- -- === @@ -2139,6 +2140,8 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle + + local _RUNACT if not self.ATISforFARPs then -- Active runway. @@ -2148,7 +2151,7 @@ function ATIS:onafterBroadcast( From, Event, To ) elseif rwyLandingLeft==false then subtitle=subtitle.." Right" end - local _RUNACT = subtitle + _RUNACT = subtitle if not self.useSRS then self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle) self.radioqueue:Number2Transmission(runwayLanding) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 00fb6ddbd..7f3ff6337 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: undefined-global --- **Ops** - Auftrag (mission) for Ops. -- -- ## Main Features: @@ -477,7 +478,7 @@ AUFTRAG.Type={ RELOCATECOHORT="Relocate Cohort", AIRDEFENSE="Air Defence", EWR="Early Warning Radar", - RECOVERYTANKER="Recovery Tanker", + --RECOVERYTANKER="Recovery Tanker", REARMING="Rearming", CAPTUREZONE="Capture Zone", NOTHING="Nothing", diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 0407a445d..b99e16a5c 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -2027,9 +2027,9 @@ function AWACS:_StartEscorts(Shiftchange) self.CatchAllMissions[#self.CatchAllMissions+1] = escort if Shiftchange then - self.EscortMissionReplacement[i] = mission + self.EscortMissionReplacement[i] = escort else - self.EscortMission[i] = mission + self.EscortMission[i] = escort end end @@ -3597,10 +3597,13 @@ function AWACS:_SetClientMenus() local tasking = MENU_GROUP:New(cgrp,"Tasking",basemenu) local showtask = MENU_GROUP_COMMAND:New(cgrp,"Showtask",tasking,self._Showtask,self,cgrp) + local commit + local unable + local abort if self.PlayerCapAssignment then - local commit = MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp) - local unable = MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp) - local abort = MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp) + commit = MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp) + unable = MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp) + abort = MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp) --local judy = MENU_GROUP_COMMAND:New(cgrp,"Judy",tasking,self._Judy,self,cgrp) end @@ -4933,8 +4936,8 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo end end - string.gsub(BRAText,"BRAA","brah") - string.gsub(BRAText,"BRA","brah") + BRAText = string.gsub(BRAText,"BRAA","brah") + BRAText = string.gsub(BRAText,"BRA","brah") local prio = IsNew or IsBogeyDope self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio) @@ -5547,7 +5550,7 @@ end -- @param #string Event -- @param #string To -- @return #AWACS self -function AWACS:onbeforeStart(From,Event,to) +function AWACS:onbeforeStart(From,Event,To) self:T({From, Event, To}) if self.IncludeHelicopters then self.clientset:FilterCategories("helicopter") diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index f9ec9e5f5..450311cbb 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -230,7 +230,7 @@ CSAR = { takenOff = {}, csarUnits = {}, -- table of unit names downedPilots = {}, - woundedGroups = {}, + -- = {}, landedStatus = {}, addedTo = {}, woundedGroups = {}, -- contains the new group of units @@ -1306,7 +1306,7 @@ end -- @param #string UnitName -- @return #string CallSign function CSAR:_GetCustomCallSign(UnitName) - local callsign = Unitname + local callsign = UnitName local unit = UNIT:FindByName(UnitName) if unit and unit:IsAlive() then local group = unit:GetGroup() @@ -1876,7 +1876,7 @@ function CSAR:_SignalFlare(_unitName) if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then local _clockDir = self:_GetClockDirection(_heli, _closest.pilot) - local _distance = 0 + local _distance = "" if _SETTINGS:IsImperial() then _distance = string.format("%.1fnm",UTILS.MetersToNM(_closest.distance)) else @@ -1889,12 +1889,13 @@ function CSAR:_SignalFlare(_unitName) _coord:FlareRed(_clockDir) else local _distance = smokedist + local dtext = "" if _SETTINGS:IsImperial() then - _distance = string.format("%.1fnm",UTILS.MetersToNM(smokedist)) + dtext = string.format("%.1fnm",UTILS.MetersToNM(smokedist)) else - _distance = string.format("%.1fkm",smokedist/1000) + dtext = string.format("%.1fkm",smokedist/1000) end - self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime, false, false, true) + self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",dtext), self.messageTime, false, false, true) end return self end @@ -1930,7 +1931,7 @@ function CSAR:_Reqsmoke( _unitName ) local _closest = self:_GetClosestDownedPilot(_heli) if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then local _clockDir = self:_GetClockDirection(_heli, _closest.pilot) - local _distance = 0 + local _distance = string.format("%.1fkm",_closest.distance/1000) if _SETTINGS:IsImperial() then _distance = string.format("%.1fnm",UTILS.MetersToNM(_closest.distance)) else @@ -1942,7 +1943,7 @@ function CSAR:_Reqsmoke( _unitName ) local color = self.smokecolor _coord:Smoke(color) else - local _distance = 0 + local _distance = string.format("%.1fkm",smokedist/1000) if _SETTINGS:IsImperial() then _distance = string.format("%.1fnm",UTILS.MetersToNM(smokedist)) else diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 298f7b1dd..1ced7de11 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -204,7 +204,7 @@ CTLD_CARGO = { -- @param #CTLD_CARGO self -- @param #boolean loaded function CTLD_CARGO:Isloaded() - if self.HasBeenMoved and not self.WasDropped() then + if self.HasBeenMoved and not self:WasDropped() then return true else return false diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index 5ab2e97da..afd37f936 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -75,7 +75,7 @@ COHORT = { livery = nil, skill = nil, legion = nil, - Ngroups = nil, + --Ngroups = nil, Ngroups = 0, engageRange = nil, tacanChannel = {}, diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index df55a88d4..d8e9d8dc8 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1068,7 +1068,7 @@ self:T(self.lid.."_ReportLaserTargets") if number > 0 and self.AutoLase[playername] then local Settings = ( client and _DATABASE:GetPlayerSettings( playername ) ) or _SETTINGS local target = self:_GetHVTTarget(targetset) -- the one we're lasing - local ThreatLevel = target:GetThreatLevel() + local ThreatLevel = target:GetThreatLevel() or 1 local ThreatLevelText = "high" if ThreatLevel > 3 and ThreatLevel < 8 then ThreatLevelText = "medium" @@ -1078,7 +1078,7 @@ self:T(self.lid.."_ReportLaserTargets") local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel local report = REPORT:New("Lasing Report") report:Add(string.rep("-",15)) - report:Add("Target type: "..target:GetTypeName()) + report:Add("Target type: "..target:GetTypeName() or "unknown") report:Add("Threat Level: "..ThreatGraph.." ("..ThreatLevelText..")") if not self.ReferencePoint then report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings)) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index a7c127b1f..94f1b35c8 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -522,7 +522,7 @@ function PLAYERTASK:MarkTargetOnF10Map(Text,Coalition,ReadOnly) -- Marker exists, delete one first self.TargetMarker:Remove() end - local text = Text or "Target of "..self.lid + local text = Text or ("Target of "..self.lid) self.TargetMarker = MARKER:New(coordinate,text) if ReadOnly then self.TargetMarker:ReadOnly() diff --git a/Moose Development/Moose/Sound/SoundOutput.lua b/Moose Development/Moose/Sound/SoundOutput.lua index 57ce2f9a4..bf3fc4d6d 100644 --- a/Moose Development/Moose/Sound/SoundOutput.lua +++ b/Moose Development/Moose/Sound/SoundOutput.lua @@ -24,7 +24,7 @@ do -- Sound Base - --- @type SOUNDBASE + -- @type SOUNDBASE -- @field #string ClassName Name of the class. -- @extends Core.Base#BASE @@ -100,7 +100,7 @@ end do -- Sound File - --- @type SOUNDFILE + -- @type SOUNDFILE -- @field #string ClassName Name of the class -- @field #string filename Name of the flag. -- @field #string path Directory path, where the sound file is located. This includes the final slash "/". @@ -276,7 +276,7 @@ end do -- Text-To-Speech - --- @type SOUNDTEXT + -- @type SOUNDTEXT -- @field #string ClassName Name of the class -- @field #string text Text to speak. -- @field #number duration Duration in seconds. diff --git a/Moose Development/Moose/Sound/UserSound.lua b/Moose Development/Moose/Sound/UserSound.lua index cbccffe12..1cef70970 100644 --- a/Moose Development/Moose/Sound/UserSound.lua +++ b/Moose Development/Moose/Sound/UserSound.lua @@ -21,7 +21,7 @@ do -- UserSound - --- @type USERSOUND + -- @type USERSOUND -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 8af4786bc..4d80a5d6b 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -176,7 +176,7 @@ COMMANDCENTER = { } ---- @type COMMANDCENTER.AutoAssignMethods +-- @type COMMANDCENTER.AutoAssignMethods COMMANDCENTER.AutoAssignMethods = { ["Random"] = 1, ["Distance"] = 2, @@ -205,7 +205,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self:SetMessageDuration(10) self:HandleEvent( EVENTS.Birth, - --- @param #COMMANDCENTER self + -- @param #COMMANDCENTER self -- @param Core.Event#EVENTDATA EventData function( self, EventData ) if EventData.IniObjectCategory == 1 then @@ -236,7 +236,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) -- -- - Assign the PlayerUnit to the Task if required. -- -- - Send a message to the other players in the group that this player has joined. -- self:HandleEvent( EVENTS.PlayerEnterUnit, --- --- @param #COMMANDCENTER self +-- -- @param #COMMANDCENTER self -- -- @param Core.Event#EVENTDATA EventData -- function( self, EventData ) -- local PlayerUnit = EventData.IniUnit @@ -253,7 +253,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) -- The PlayerUnit will be UnAssigned from the Task. -- When there is no Unit left running the Task, the Task goes into Abort... self:HandleEvent( EVENTS.MissionEnd, - --- @param #TASK self + -- @param #TASK self -- @param Core.Event#EVENTDATA EventData function( self, EventData ) local PlayerUnit = EventData.IniUnit @@ -268,7 +268,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) -- The PlayerUnit will be UnAssigned from the Task. -- When there is no Unit left running the Task, the Task goes into Abort... self:HandleEvent( EVENTS.PlayerLeaveUnit, - --- @param #TASK self + -- @param #TASK self -- @param Core.Event#EVENTDATA EventData function( self, EventData ) local PlayerUnit = EventData.IniUnit @@ -285,7 +285,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) -- The PlayerUnit will be UnAssigned from the Task. -- When there is no Unit left running the Task, the Task goes into Abort... self:HandleEvent( EVENTS.Crash, - --- @param #TASK self + -- @param #TASK self -- @param Core.Event#EVENTDATA EventData function( self, EventData ) local PlayerUnit = EventData.IniUnit diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 6005a9ffa..d48f49f98 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -43,7 +43,7 @@ do -- DETECTION MANAGER - --- @type DETECTION_MANAGER + -- @type DETECTION_MANAGER -- @field Core.Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. -- @field Tasking.CommandCenter#COMMANDCENTER CC The command center that is used to communicate with the players. @@ -57,7 +57,7 @@ do -- DETECTION MANAGER Detection = nil, } - --- @field Tasking.CommandCenter#COMMANDCENTER + -- @field Tasking.CommandCenter#COMMANDCENTER DETECTION_MANAGER.CC = nil --- FAC constructor. diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index b0f39072f..7ad1de610 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -19,7 +19,7 @@ -- @module Tasking.Mission -- @image Task_Mission.JPG ---- @type MISSION +-- @type MISSION -- @field #MISSION.Clients _Clients -- @field Core.Menu#MENU_COALITION MissionMenu -- @field #string MissionBriefing @@ -785,7 +785,7 @@ function MISSION:HasGroup( TaskGroup ) return Has end ---- @param #MISSION self +-- @param #MISSION self -- @return #number function MISSION:GetTasksRemaining() -- Determine how many tasks are remaining. @@ -800,7 +800,7 @@ function MISSION:GetTasksRemaining() return TasksRemaining end ---- @param #MISSION self +-- @param #MISSION self -- @return #number function MISSION:GetTaskTypes() -- Determine how many tasks are remaining. @@ -860,7 +860,7 @@ end ---- - Aborted Tasks (xp) ---- - Failed Tasks (xp) ---- ----- @param #MISSION self +-- @param #MISSION self ---- @return #string --function MISSION:ReportSummary() -- @@ -1170,7 +1170,7 @@ end ---- @param #MISSION self +-- @param #MISSION self -- @param #string TaskStatus The status -- @param Wrapper.Group#GROUP ReportGroup function MISSION:MenuReportTasksPerStatus( ReportGroup, TaskStatus ) @@ -1181,7 +1181,7 @@ function MISSION:MenuReportTasksPerStatus( ReportGroup, TaskStatus ) end ---- @param #MISSION self +-- @param #MISSION self -- @param Wrapper.Group#GROUP ReportGroup function MISSION:MenuReportPlayersPerTask( ReportGroup ) @@ -1190,7 +1190,7 @@ function MISSION:MenuReportPlayersPerTask( ReportGroup ) self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview ) end ---- @param #MISSION self +-- @param #MISSION self -- @param Wrapper.Group#GROUP ReportGroup function MISSION:MenuReportPlayersProgress( ReportGroup ) diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 950ca6656..5a2dfd709 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -215,7 +215,7 @@ -- @module Tasking.Task -- @image MOOSE.JPG ---- @type TASK +-- @type TASK -- @field Core.Scheduler#SCHEDULER TaskScheduler -- @field Tasking.Mission#MISSION Mission -- @field Core.Set#SET_GROUP SetGroup The Set of Groups assigned to the Task @@ -721,7 +721,7 @@ function TASK:AddGroups( GroupSet ) GroupSet = GroupSet or SET_GROUP:New() self.SetGroup:ForEachGroup( - --- @param Wrapper.Group#GROUP GroupSet + -- @param Wrapper.Group#GROUP GroupSet function( GroupItem ) GroupSet:Add( GroupItem:GetName(), GroupItem) end @@ -814,7 +814,7 @@ end do -- Group Assignment - --- @param #TASK self + -- @param #TASK self -- @param Actions.Act_Assign#ACT_ASSIGN AcceptClass function TASK:SetAssignMethod( AcceptClass ) @@ -1194,7 +1194,7 @@ function TASK:RemoveAssignedMenuForGroup( TaskGroup ) end ---- @param #TASK self +-- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup function TASK:MenuAssignToGroup( TaskGroup ) @@ -1203,7 +1203,7 @@ function TASK:MenuAssignToGroup( TaskGroup ) self:AssignToGroup( TaskGroup ) end ---- @param #TASK self +-- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup function TASK:MenuMarkToGroup( TaskGroup ) self:F() diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 9c1eb7a18..408067981 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -11,7 +11,7 @@ -- @module Tasking.TaskInfo -- @image MOOSE.JPG ---- @type TASKINFO +-- @type TASKINFO -- @extends Core.Base#BASE --- @@ -24,7 +24,7 @@ TASKINFO = { ClassName = "TASKINFO", } ---- @type TASKINFO.Detail #string A string that flags to document which level of detail needs to be shown in the report. +-- @type TASKINFO.Detail #string A string that flags to document which level of detail needs to be shown in the report. -- -- - "M" for Markings on the Map (F10). -- - "S" for Summary Reports. @@ -274,7 +274,7 @@ function TASKINFO:AddCargoSet( SetCargo, Order, Detail, Keep ) local CargoReport = REPORT:New() CargoReport:Add( "" ) SetCargo:ForEachCargo( - --- @param Cargo.Cargo#CARGO Cargo + -- @param Cargo.Cargo#CARGO Cargo function( Cargo ) CargoReport:Add( string.format( ' - %s (%s) %s - status %s ', Cargo:GetName(), Cargo:GetType(), Cargo:GetTransportationMethod(), Cargo:GetCurrentState() ) ) end diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index b9af14ef2..23d919190 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -168,19 +168,19 @@ do -- TASK_A2A end - --- @param #TASK_A2A self + -- @param #TASK_A2A self -- @param Core.Set#SET_UNIT TargetSetUnit The set of targets. function TASK_A2A:SetTargetSetUnit( TargetSetUnit ) self.TargetSetUnit = TargetSetUnit end - --- @param #TASK_A2A self + -- @param #TASK_A2A self function TASK_A2A:GetPlannedMenuText() return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" end - --- @param #TASK_A2A self + -- @param #TASK_A2A self -- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. -- @param Wrapper.Unit#UNIT TaskUnit @@ -193,7 +193,7 @@ do -- TASK_A2A ActRouteRendezVous:SetRange( RendezVousRange ) end - --- @param #TASK_A2A self + -- @param #TASK_A2A self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. @@ -205,7 +205,7 @@ do -- TASK_A2A return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() end - --- @param #TASK_A2A self + -- @param #TASK_A2A self -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2A:SetRendezVousZone( RendezVousZone, TaskUnit ) @@ -216,7 +216,7 @@ do -- TASK_A2A ActRouteRendezVous:SetZone( RendezVousZone ) end - --- @param #TASK_A2A self + -- @param #TASK_A2A self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. function TASK_A2A:GetRendezVousZone( TaskUnit ) @@ -227,7 +227,7 @@ do -- TASK_A2A return ActRouteRendezVous:GetZone() end - --- @param #TASK_A2A self + -- @param #TASK_A2A self -- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2A:SetTargetCoordinate( TargetCoordinate, TaskUnit ) @@ -238,7 +238,7 @@ do -- TASK_A2A ActRouteTarget:SetCoordinate( TargetCoordinate ) end - --- @param #TASK_A2A self + -- @param #TASK_A2A self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map. function TASK_A2A:GetTargetCoordinate( TaskUnit ) @@ -249,7 +249,7 @@ do -- TASK_A2A return ActRouteTarget:GetCoordinate() end - --- @param #TASK_A2A self + -- @param #TASK_A2A self -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2A:SetTargetZone( TargetZone, Altitude, Heading, TaskUnit ) @@ -260,7 +260,7 @@ do -- TASK_A2A ActRouteTarget:SetZone( TargetZone, Altitude, Heading ) end - --- @param #TASK_A2A self + -- @param #TASK_A2A self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. function TASK_A2A:GetTargetZone( TaskUnit ) @@ -304,7 +304,7 @@ do -- TASK_A2A self:__Goal( -10 ) end - --- @param #TASK_A2A self + -- @param #TASK_A2A self function TASK_A2A:UpdateTaskInfo( DetectedItem ) if self:IsStatePlanned() or self:IsStateAssigned() then @@ -496,7 +496,7 @@ do -- TASK_A2A_SWEEP return self end - --- @param #TASK_A2A_SWEEP self + -- @param #TASK_A2A_SWEEP self function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To ) local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index 52f2317aa..7eb627760 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -148,7 +148,7 @@ do -- TASK_A2A_DISPATCHER -- -- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... ) -- - -- --- @param #TaskDispatcher self + -- -- @param #TaskDispatcher self -- -- @param #string From Contains the name of the state from where the Event was triggered. -- -- @param #string Event Contains the name of the event that was triggered. In this case Assign. -- -- @param #string To Contains the name of the state that will be transitioned to. diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index d6c8dd613..bf4a3c580 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -170,19 +170,19 @@ do -- TASK_A2G end - --- @param #TASK_A2G self + -- @param #TASK_A2G self -- @param Core.Set#SET_UNIT TargetSetUnit The set of targets. function TASK_A2G:SetTargetSetUnit( TargetSetUnit ) self.TargetSetUnit = TargetSetUnit end - --- @param #TASK_A2G self + -- @param #TASK_A2G self function TASK_A2G:GetPlannedMenuText() return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" end - --- @param #TASK_A2G self + -- @param #TASK_A2G self -- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. -- @param Wrapper.Unit#UNIT TaskUnit @@ -195,7 +195,7 @@ do -- TASK_A2G ActRouteRendezVous:SetRange( RendezVousRange ) end - --- @param #TASK_A2G self + -- @param #TASK_A2G self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. @@ -207,7 +207,7 @@ do -- TASK_A2G return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() end - --- @param #TASK_A2G self + -- @param #TASK_A2G self -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit ) @@ -218,7 +218,7 @@ do -- TASK_A2G ActRouteRendezVous:SetZone( RendezVousZone ) end - --- @param #TASK_A2G self + -- @param #TASK_A2G self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. function TASK_A2G:GetRendezVousZone( TaskUnit ) @@ -229,7 +229,7 @@ do -- TASK_A2G return ActRouteRendezVous:GetZone() end - --- @param #TASK_A2G self + -- @param #TASK_A2G self -- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2G:SetTargetCoordinate( TargetCoordinate, TaskUnit ) @@ -240,7 +240,7 @@ do -- TASK_A2G ActRouteTarget:SetCoordinate( TargetCoordinate ) end - --- @param #TASK_A2G self + -- @param #TASK_A2G self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map. function TASK_A2G:GetTargetCoordinate( TaskUnit ) @@ -251,7 +251,7 @@ do -- TASK_A2G return ActRouteTarget:GetCoordinate() end - --- @param #TASK_A2G self + -- @param #TASK_A2G self -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2G:SetTargetZone( TargetZone, TaskUnit ) @@ -262,7 +262,7 @@ do -- TASK_A2G ActRouteTarget:SetZone( TargetZone ) end - --- @param #TASK_A2G self + -- @param #TASK_A2G self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. function TASK_A2G:GetTargetZone( TaskUnit ) @@ -306,7 +306,7 @@ do -- TASK_A2G self:__Goal( -10 ) end - --- @param #TASK_A2G self + -- @param #TASK_A2G self function TASK_A2G:UpdateTaskInfo( DetectedItem ) if self:IsStatePlanned() or self:IsStateAssigned() then diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 3c1923b3c..e18d0ee16 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -405,7 +405,7 @@ do -- TASK_CARGO - --- @type TASK_CARGO + -- @type TASK_CARGO -- @extends Tasking.Task#TASK --- Model tasks for players to transport Cargo. @@ -616,7 +616,7 @@ do -- TASK_CARGO Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) - ---- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param #TASK_CARGO Task function Fsm:OnAfterAssigned( TaskUnit, Task ) @@ -639,7 +639,7 @@ do -- TASK_CARGO Task.SetCargo:ForEachCargo( - --- @param Cargo.Cargo#CARGO Cargo + -- @param Cargo.Cargo#CARGO Cargo function( Cargo ) if Cargo:IsAlive() then @@ -833,7 +833,7 @@ do -- TASK_CARGO --#Wrapper.Unit#UNIT - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task -- @param From @@ -853,7 +853,7 @@ do -- TASK_CARGO - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterArriveAtPickup( TaskUnit, Task ) @@ -869,7 +869,7 @@ do -- TASK_CARGO end - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterCancelRouteToPickup( TaskUnit, Task ) @@ -880,7 +880,7 @@ do -- TASK_CARGO end - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit function Fsm:onafterRouteToDeploy( TaskUnit, Task, From, Event, To, DeployZone ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) @@ -892,7 +892,7 @@ do -- TASK_CARGO end - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterArriveAtDeploy( TaskUnit, Task ) @@ -907,7 +907,7 @@ do -- TASK_CARGO end - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterCancelRouteToDeploy( TaskUnit, Task ) @@ -919,7 +919,7 @@ do -- TASK_CARGO - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action ) @@ -954,7 +954,7 @@ do -- TASK_CARGO end end - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action ) @@ -987,7 +987,7 @@ do -- TASK_CARGO end end - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterPrepareBoarding( TaskUnit, Task, From, Event, To, Cargo ) @@ -999,7 +999,7 @@ do -- TASK_CARGO end - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterBoard( TaskUnit, Task, From, Event, To, Cargo ) @@ -1027,7 +1027,7 @@ do -- TASK_CARGO end - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterBoarded( TaskUnit, Task, From, Event, To, Cargo ) @@ -1042,7 +1042,7 @@ do -- TASK_CARGO end - --- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterLoad( TaskUnit, Task, From, Event, To, Cargo ) @@ -1191,26 +1191,26 @@ do -- TASK_CARGO return self.SmokeColor end - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self function TASK_CARGO:GetPlannedMenuText() return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" end - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self -- @return Core.Set#SET_CARGO The Cargo Set. function TASK_CARGO:GetCargoSet() return self.SetCargo end - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self -- @return #list The Deployment Zones. function TASK_CARGO:GetDeployZones() return self.DeployZones end - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self -- @param AI.AI_Cargo#AI_CARGO Cargo The cargo. -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK_CARGO @@ -1233,7 +1233,7 @@ do -- TASK_CARGO end - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self -- @param Core.Zone#ZONE DeployZone -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK_CARGO @@ -1254,7 +1254,7 @@ do -- TASK_CARGO end - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self -- @param Core.Zone#ZONE DeployZone -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK_CARGO @@ -1265,7 +1265,7 @@ do -- TASK_CARGO return self end - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self -- @param Core.Zone#ZONE DeployZone -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK_CARGO @@ -1276,7 +1276,7 @@ do -- TASK_CARGO return self end - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self -- @param #list DeployZones -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK_CARGO @@ -1291,7 +1291,7 @@ do -- TASK_CARGO - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. function TASK_CARGO:GetTargetZone( TaskUnit ) @@ -1360,7 +1360,7 @@ do -- TASK_CARGO return self.GoalTotal end - --- @param #TASK_CARGO self + -- @param #TASK_CARGO self function TASK_CARGO:UpdateTaskInfo() if self:IsStatePlanned() or self:IsStateAssigned() then diff --git a/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua index 87046e1ff..ee9b11983 100644 --- a/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua @@ -64,7 +64,7 @@ do -- TASK_CAPTURE_DISPATCHER -- @extends Tasking.Task_Manager#TASK_MANAGER -- @field TASK_CAPTURE_DISPATCHER.ZONE ZONE - --- @type TASK_CAPTURE_DISPATCHER.CSAR + -- @type TASK_CAPTURE_DISPATCHER.CSAR -- @field Wrapper.Unit#UNIT PilotUnit -- @field Tasking.Task#TASK Task diff --git a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua b/Moose Development/Moose/Tasking/Task_Capture_Zone.lua index 9a7a30a9b..956b28f15 100644 --- a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua +++ b/Moose Development/Moose/Tasking/Task_Capture_Zone.lua @@ -114,7 +114,7 @@ do -- TASK_ZONE_GOAL end - --- @param #TASK_ZONE_GOAL self + -- @param #TASK_ZONE_GOAL self -- @param Functional.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal Engine. function TASK_ZONE_GOAL:SetProtect( ZoneGoal ) @@ -123,13 +123,13 @@ do -- TASK_ZONE_GOAL - --- @param #TASK_ZONE_GOAL self + -- @param #TASK_ZONE_GOAL self function TASK_ZONE_GOAL:GetPlannedMenuText() return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.ZoneGoal:GetZoneName() .. " )" end - --- @param #TASK_ZONE_GOAL self + -- @param #TASK_ZONE_GOAL self -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_ZONE_GOAL:SetTargetZone( TargetZone, TaskUnit ) @@ -141,7 +141,7 @@ do -- TASK_ZONE_GOAL end - --- @param #TASK_ZONE_GOAL self + -- @param #TASK_ZONE_GOAL self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. function TASK_ZONE_GOAL:GetTargetZone( TaskUnit ) @@ -281,7 +281,7 @@ do -- TASK_CAPTURE_ZONE end - --- @param #TASK_CAPTURE_ZONE self + -- @param #TASK_CAPTURE_ZONE self -- @param Wrapper.Unit#UNIT TaskUnit function TASK_CAPTURE_ZONE:OnAfterGoal( From, Event, To, PlayerUnit, PlayerName ) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index c48c27a22..1790511cd 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -67,7 +67,7 @@ do -- TASK_CARGO_CSAR - --- @type TASK_CARGO_CSAR + -- @type TASK_CARGO_CSAR -- @extends Tasking.Task_Cargo#TASK_CARGO --- Orchestrates the task for players to execute CSAR for downed pilots. @@ -313,7 +313,7 @@ do -- TASK_CARGO_CSAR local CargoReport = REPORT:New( "Rescue a downed pilot from the following position:") SetCargo:ForEachCargo( - --- @param Core.Cargo#CARGO Cargo + -- @param Core.Cargo#CARGO Cargo function( Cargo ) local CargoType = Cargo:GetType() local CargoName = Cargo:GetName() @@ -378,7 +378,7 @@ do -- TASK_CARGO_CSAR return CargoDeployed end - --- @param #TASK_CARGO_CSAR self + -- @param #TASK_CARGO_CSAR self function TASK_CARGO_CSAR:onafterGoal( TaskUnit, From, Event, To ) local CargoSet = self.CargoSet diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index fd3be1018..7121cd25c 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -72,7 +72,7 @@ do -- TASK_CARGO_DISPATCHER -- @field TASK_CARGO_DISPATCHER.CSAR CSAR -- @field Core.Set#SET_ZONE SetZonesCSAR - --- @type TASK_CARGO_DISPATCHER.CSAR + -- @type TASK_CARGO_DISPATCHER.CSAR -- @field Wrapper.Unit#UNIT PilotUnit -- @field Tasking.Task#TASK Task diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index 25bcb2c9c..c8a746ddd 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -280,7 +280,7 @@ do -- TASK_CARGO_TRANSPORT local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:") SetCargo:ForEachCargo( - --- @param Core.Cargo#CARGO Cargo + -- @param Core.Cargo#CARGO Cargo function( Cargo ) local CargoType = Cargo:GetType() local CargoName = Cargo:GetName() @@ -343,7 +343,7 @@ do -- TASK_CARGO_TRANSPORT return CargoDeployed end - --- @param #TASK_CARGO_TRANSPORT self + -- @param #TASK_CARGO_TRANSPORT self function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To ) local CargoSet = self.CargoSet diff --git a/Moose Development/Moose/Utilities/Routines.lua b/Moose Development/Moose/Utilities/Routines.lua index 8545a1dd7..e774a290f 100644 --- a/Moose Development/Moose/Utilities/Routines.lua +++ b/Moose Development/Moose/Utilities/Routines.lua @@ -54,7 +54,7 @@ routines.utils.oneLineSerialize = function( tbl ) -- serialization of a table al if type( tbl ) == 'table' then -- function only works for tables! if lookup_table[tbl] then - return lookup_table[object] + return lookup_table[tbl] end local tbl_str = {} diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 891718da7..74307d9eb 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -13,7 +13,7 @@ -- @image MOOSE.JPG ---- @type SMOKECOLOR +-- @type SMOKECOLOR -- @field Green -- @field Red -- @field White @@ -22,7 +22,7 @@ SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR ---- @type FLARECOLOR +-- @type FLARECOLOR -- @field Green -- @field Red -- @field White diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 66aca8d82..60c47d7e8 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -12,7 +12,7 @@ -- @image Wrapper_Airbase.JPG ---- @type AIRBASE +-- @type AIRBASE -- @field #string ClassName Name of the class, i.e. "AIRBASE". -- @field #table CategoryName Names of airbase categories. -- @field #string AirbaseName Name of the airbase. diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 01dce4828..d53630890 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -323,7 +323,7 @@ function CLIENT:Alive( CallBackFunction, ... ) return self end ---- @param #CLIENT self +-- @param #CLIENT self function CLIENT:_AliveCheckScheduler( SchedulerName ) self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } ) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index ba53f6f60..f62cb0ed4 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -11,7 +11,7 @@ -- @module Wrapper.Controllable -- @image Wrapper_Controllable.JPG ---- @type CONTROLLABLE +-- @type CONTROLLABLE -- @field DCS#Controllable DCSControllable The DCS controllable class. -- @field #string ControllableName The name of the controllable. -- @extends Wrapper.Positionable#POSITIONABLE @@ -1906,7 +1906,7 @@ end -- -- GroundGroup = GROUP:FindByName( "Vehicle" ) -- --- --- @param Wrapper.Group#GROUP GroundGroup +-- -- @param Wrapper.Group#GROUP GroundGroup -- function RouteToZone( Vehicle, ZoneRoute ) -- -- local Route = {} diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index fdc6c8f1e..bb8ed9408 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -38,7 +38,7 @@ -- @image Wrapper_Group.JPG ---- @type GROUP +-- @type GROUP -- @extends Wrapper.Controllable#CONTROLLABLE -- @field #string GroupName The name of the group. @@ -91,7 +91,7 @@ -- -- Tasks[#Tasks+1] = HeliGroup:TaskFunction( "_Resume", { "''" } ) -- --- --- @param Wrapper.Group#GROUP HeliGroup +-- -- @param Wrapper.Group#GROUP HeliGroup -- function _Resume( HeliGroup ) -- env.info( '_Resume' ) -- diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 1710289d7..8786807d5 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -11,7 +11,7 @@ -- @module Wrapper.Identifiable -- @image MOOSE.JPG ---- @type IDENTIFIABLE +-- @type IDENTIFIABLE -- @extends Wrapper.Object#OBJECT -- @field #string IdentifiableName The name of the identifiable. diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index 4488141fa..382e13f5e 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -12,7 +12,7 @@ -- @image MOOSE.JPG ---- @type OBJECT +-- @type OBJECT -- @extends Core.Base#BASE -- @field #string ObjectName The name of the Object. diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 571e0d48c..8a951fb79 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -11,10 +11,10 @@ -- @module Wrapper.Positionable -- @image Wrapper_Positionable.JPG ---- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used internally by the moose designer :-) +-- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used internally by the moose designer :-) -- @extends Wrapper.Identifiable#IDENTIFIABLE ---- @type POSITIONABLE +-- @type POSITIONABLE -- @field Core.Point#COORDINATE coordinate Coordinate object. -- @field Core.Point#POINT_VEC3 pointvec3 Point Vec3 object. -- @extends Wrapper.Identifiable#IDENTIFIABLE @@ -51,10 +51,10 @@ POSITIONABLE = { pointvec3 = nil, } ---- @field #POSITIONABLE.__ +-- @field #POSITIONABLE.__ POSITIONABLE.__ = {} ---- @field #POSITIONABLE.__.Cargo +-- @field #POSITIONABLE.__.Cargo POSITIONABLE.__.Cargo = {} --- A DCSPositionable diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 933052517..5143c698c 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -12,7 +12,7 @@ -- @image Wrapper_Static.JPG ---- @type STATIC +-- @type STATIC -- @extends Wrapper.Positionable#POSITIONABLE --- Wrapper class to handle Static objects. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index cd6769fc2..98f24d5ad 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -21,7 +21,7 @@ -- @image Wrapper_Unit.JPG ---- @type UNIT +-- @type UNIT -- @field #string ClassName Name of the class. -- @field #string UnitName Name of the unit. -- @field #string GroupName Name of the group the unit belongs to. From e8ab86ab7f0bd2fa470900d76eedd3925bb892dd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 22 Jun 2023 12:57:08 +0200 Subject: [PATCH 289/603] #CTLD * Added categories for Troops --- Moose Development/Moose/Ops/CTLD.lua | 37 ++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 1ced7de11..1d039a6eb 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1390,6 +1390,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- sub categories self.usesubcats = false self.subcats = {} + self.subcatsTroop = {} -- disallow building in loadzones self.nobuildinloadzones = true @@ -3468,6 +3469,12 @@ function CTLD:_RefreshF10Menus() self.subcats[entry.Subcategory] = entry.Subcategory end end + for _id,_cargo in pairs(self.Cargo_Troops) do + local entry = _cargo -- #CTLD_CARGO + if not self.subcatsTroop[entry.Subcategory] then + self.subcatsTroop[entry.Subcategory] = entry.Subcategory + end + end end -- build unit menus @@ -3504,15 +3511,28 @@ function CTLD:_RefreshF10Menus() local beaconself = MENU_GROUP_COMMAND:New(_group,"Drop beacon now",smoketopmenu, self.DropBeaconNow, self, _unit):Refresh() -- sub menus -- sub menu troops management - if cantroops then + if cantroops then local troopsmenu = MENU_GROUP:New(_group,"Load troops",toptroops) - for _,_entry in pairs(self.Cargo_Troops) do - local entry = _entry -- #CTLD_CARGO - menucount = menucount + 1 - menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry) + if self.usesubcats then + local subcatmenus = {} + for _name,_entry in pairs(self.subcatsTroop) do + subcatmenus[_name] = MENU_GROUP:New(_group,_name,troopsmenu) + end + for _,_entry in pairs(self.Cargo_Troops) do + local entry = _entry -- #CTLD_CARGO + local subcat = entry.Subcategory + menucount = menucount + 1 + menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,subcatmenus[subcat],self._LoadTroops, self, _group, _unit, entry) + end + else + for _,_entry in pairs(self.Cargo_Troops) do + local entry = _entry -- #CTLD_CARGO + menucount = menucount + 1 + menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry) + end end local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh() - local extractMenu1 = MENU_GROUP_COMMAND:New(_group, "Extract troops", toptroops, self._ExtractTroops, self, _group, _unit):Refresh() + local extractMenu1 = MENU_GROUP_COMMAND:New(_group, "Extract troops", toptroops, self._ExtractTroops, self, _group, _unit):Refresh() end -- sub menu crates management if cancrates then @@ -3603,7 +3623,8 @@ end -- @param #number NoTroops Size of the group in number of Units across combined templates (for loading). -- @param #number PerTroopMass Mass in kg of each soldier -- @param #number Stock Number of groups in stock. Nil for unlimited. -function CTLD:AddTroopsCargo(Name,Templates,Type,NoTroops,PerTroopMass,Stock) +-- @param #string SubCategory Name of sub-category (optional). +function CTLD:AddTroopsCargo(Name,Templates,Type,NoTroops,PerTroopMass,Stock,SubCategory) self:T(self.lid .. " AddTroopsCargo") self:T({Name,Templates,Type,NoTroops,PerTroopMass,Stock}) if not self:_CheckTemplates(Templates) then @@ -3612,7 +3633,7 @@ function CTLD:AddTroopsCargo(Name,Templates,Type,NoTroops,PerTroopMass,Stock) end self.CargoCounter = self.CargoCounter + 1 -- Troops are directly loadable - local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,true,NoTroops,nil,nil,PerTroopMass,Stock) + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,true,NoTroops,nil,nil,PerTroopMass,Stock, SubCategory) table.insert(self.Cargo_Troops,cargo) return self end From a2d320570188b2d1a8b93a474294c41dedba78f3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 22 Jun 2023 13:41:58 +0200 Subject: [PATCH 290/603] #CTLD * Added option for troops subcategories --- Moose Development/Moose/Ops/Airboss.lua | 5 +++-- Moose Development/Moose/Ops/CTLD.lua | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 005f5b614..e9655c499 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -5459,6 +5459,7 @@ function AIRBOSS:_GetAircraftParameters( playerData, step ) local skyhawk = playerData.actype == AIRBOSS.AircraftCarrier.A4EC local tomcat = playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B local harrier = playerData.actype == AIRBOSS.AircraftCarrier.AV8B + local goshawk = playerData.actype == AIRBOSS.AircraftCarrier.T45C -- Return values. local alt @@ -14841,7 +14842,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p local text = call.subtitle self:I(self.lid..text) local srstext = self:_GetNiceSRSText(text) - self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, tstart, 0.1, subgroups, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) + self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, nil, 0.1, nil, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) end end @@ -15259,7 +15260,7 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio self:I(self.lid..text) self:I({sender,frequency,modulation,voice}) local srstext = self:_GetNiceSRSText(text) - self.SRSQ:NewTransmission(srstext,duration,self.SRS,tstart,0.1,subgroups,subtitle,subduration,frequency,modulation,gender,culture,voice,volume,sender) + self.SRSQ:NewTransmission(srstext,duration,self.SRS,nil,0.1,nil,nil,nil,frequency,modulation,gender,culture,voice,1.0,sender) end -- Text message to player client. if playerData.client then diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 1d039a6eb..7e79bdbc8 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1221,7 +1221,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.39" +CTLD.version="1.0.40" --- Instantiate a new CTLD. -- @param #CTLD self @@ -2280,6 +2280,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) if not drop then inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) if not inzone then +---@diagnostic disable-next-line: cast-local-type inzone, ship, zone, distance, width = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) end else From c7a66e0f9964e5d0d1dc60a521ef821fea21c2e0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 26 Jun 2023 17:29:02 +0200 Subject: [PATCH 291/603] #Fixes --- Moose Development/Moose/Ops/ATIS.lua | 26 +++++++++++++-------- Moose Development/Moose/Wrapper/Airbase.lua | 4 +++- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 25b8dc81c..719ad8da7 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -609,15 +609,16 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.9.14" +ATIS.version = "0.9.15" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Add new Normandy airfields. --- TODO: Zulu time --> Zulu in output. -- TODO: Correct fog for elevation. +-- DONE: Zulu time --> Zulu in output. +-- DONE: Fix for AB not having a runway - Helopost like Naqoura +-- DONE: Add new Normandy airfields. -- DONE: Use new AIRBASE system to set start/landing runway -- DONE: SetILS doesn't work -- DONE: Visibility reported twice over SRS @@ -2145,11 +2146,13 @@ function ATIS:onafterBroadcast( From, Event, To ) if not self.ATISforFARPs then -- Active runway. - local subtitle=string.format("Active runway %s", runwayLanding) - if rwyLandingLeft==true then - subtitle=subtitle.." Left" - elseif rwyLandingLeft==false then - subtitle=subtitle.." Right" + if runwayLanding then + local subtitle=string.format("Active runway %s", runwayLanding) + if rwyLandingLeft==true then + subtitle=subtitle.." Left" + elseif rwyLandingLeft==false then + subtitle=subtitle.." Right" + end end _RUNACT = subtitle if not self.useSRS then @@ -2512,8 +2515,11 @@ function ATIS:GetActiveRunway(Takeoff) else runway=self.airbase:GetActiveRunwayLanding() end - - return runway.name, runway.isLeft + if runway then -- some ABs have NO runways, e.g. Syria Naqoura + return runway.name, runway.isLeft + else + return nil, nil + end end --- Get runway from user supplied magnetic heading. diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 60c47d7e8..78d3d40d8 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1504,7 +1504,9 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, local _aircraftsize, ax,ay,az if group and group.ClassName == "GROUP" then aircraft=group:GetUnit(1) - _aircraftsize, ax,ay,az=aircraft:GetObjectSize() + if aircraft then + _aircraftsize, ax,ay,az=aircraft:GetObjectSize() + end else -- SU27 dimensions _aircraftsize = 23 From b3e9a2b64d74aa1f80fdf8e47e4745874ddd5472 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 1 Jul 2023 13:08:00 +0200 Subject: [PATCH 292/603] #fixes --- Moose Development/Moose/Functional/Detection.lua | 3 ++- Moose Development/Moose/Functional/Mantis.lua | 5 +++-- Moose Development/Moose/Functional/Scoring.lua | 6 +++--- Moose Development/Moose/Ops/Awacs.lua | 10 +++++----- Moose Development/Moose/Utilities/Utils.lua | 2 +- Moose Development/Moose/Wrapper/Airbase.lua | 12 +++++------- Moose Development/Moose/Wrapper/Unit.lua | 2 +- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 24e3577dc..31acfda48 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1975,7 +1975,8 @@ do -- DETECTION_BASE end do -- DETECTION_UNITS - + + --- -- @type DETECTION_UNITS -- @field DCS#Distance DetectionRange The range till which targets are detected. -- @extends Functional.Detection#DETECTION_BASE diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 3f6f7b008..256f30f37 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -369,6 +369,7 @@ MANTIS.SamData = { ["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"}, ["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"}, ["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" }, + ["SHORAD"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="Igla" } } --- SAM data HDS @@ -578,7 +579,7 @@ do -- TODO Version -- @field #string version - self.version="0.8.9" + self.version="0.8.10" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) --- FSM Functions --- @@ -1319,7 +1320,7 @@ do elseif sma then SAMData = self.SamDataSMA end - --self:I("Looking to auto-match for "..grpname) + --self:T("Looking to auto-match for "..grpname) for _,_unit in pairs(units) do local unit = _unit -- Wrapper.Unit#UNIT local type = string.lower(unit:GetTypeName()) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 33d50f923..245842223 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -1767,9 +1767,9 @@ function SCORING:SecondsToClock( sSeconds ) -- return nil; return "00:00:00"; else - nHours = string.format( "%02.f", math.floor( nSeconds / 3600 ) ); - nMins = string.format( "%02.f", math.floor( nSeconds / 60 - (nHours * 60) ) ); - nSecs = string.format( "%02.f", math.floor( nSeconds - nHours * 3600 - nMins * 60 ) ); + local nHours = string.format( "%02.f", math.floor( nSeconds / 3600 ) ); + local nMins = string.format( "%02.f", math.floor( nSeconds / 60 - (nHours * 60) ) ); + local nSecs = string.format( "%02.f", math.floor( nSeconds - nHours * 3600 - nMins * 60 ) ); return nHours .. ":" .. nMins .. ":" .. nSecs end end diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index b99e16a5c..5861d097b 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -499,7 +499,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.54", -- #string + version = "0.2.55", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -783,8 +783,8 @@ AWACS.Messages = { -- @field #string AwacsStateMission -- @field #string AwacsStateFG -- @field #boolean AwacsShiftChange --- @field #string EscortsStateMission --- @field #string EscortsStateFG +-- @field #table EscortsStateMission +-- @field #table EscortsStateFG -- @field #boolean EscortsShiftChange -- @field #number AICAPMax -- @field #number AICAPCurrent @@ -1162,8 +1162,8 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station MonitoringData.AwacsStateFG = "unknown" MonitoringData.AwacsStateMission = "unknown" MonitoringData.EscortsShiftChange = false - MonitoringData.EscortsStateFG= "unknown" - MonitoringData.EscortsStateMission = "unknown" + MonitoringData.EscortsStateFG = {} + MonitoringData.EscortsStateMission = {} self.MonitoringOn = false -- #boolean self.MonitoringData = MonitoringData diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 74307d9eb..d4e08f26c 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -299,7 +299,7 @@ end -- @param #table tbl Input table. UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - lookup_table = {} + local lookup_table = {} local function _Serialize( tbl ) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 78d3d40d8..276ec5b8d 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1501,18 +1501,16 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, -- Get the aircraft size, i.e. it's longest side of x,z. local aircraft = nil -- fix local problem below - local _aircraftsize, ax,ay,az + -- SU27 dimensions as default + local _aircraftsize = 23 + local ax = 23 -- l + local ay = 7 -- h + local az = 17 -- w if group and group.ClassName == "GROUP" then aircraft=group:GetUnit(1) if aircraft then _aircraftsize, ax,ay,az=aircraft:GetObjectSize() end - else - -- SU27 dimensions - _aircraftsize = 23 - ax = 23 -- length - ay = 7 -- height - az = 17 -- width end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 98f24d5ad..447d1d2f3 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -20,7 +20,7 @@ -- @module Wrapper.Unit -- @image Wrapper_Unit.JPG - +--- -- @type UNIT -- @field #string ClassName Name of the class. -- @field #string UnitName Name of the unit. From 303a15eb504d11958564186498d897a329a9d5fd Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 3 Jul 2023 12:21:05 +0200 Subject: [PATCH 293/603] Ammotruck * Added feature for # of re-munitions a truck can do --- .../Moose/Functional/AmmoTruck.lua | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index 68e40beef..218921e6c 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -2,9 +2,7 @@ -- -- === -- --- ## Features: --- --- * Send a truck to supply artillery groups. +-- **AMMOTRUCK** - Send a truck to supply artillery groups. -- -- === -- @@ -19,7 +17,7 @@ -- @module Functional.AmmoTruck -- @image Artillery.JPG -- --- Date: Nov 2022 +-- Last update: July 2023 ------------------------------------------------------------------------- --- **AMMOTRUCK** class, extends Core.FSM#FSM @@ -42,6 +40,7 @@ -- @field #number unloadtime Unload time in seconds -- @field #number waitingtime Max waiting time in seconds -- @field #boolean routeonroad Route truck on road if true (default) +-- @field #number reloads Number of reloads a single truck can do before he must return home -- @extends Core.FSM#FSM --- *Amateurs talk about tactics, but professionals study logistics.* - General Robert H Barrow, USMC @@ -75,9 +74,10 @@ -- ammotruck.remunidist = 20000 -- 20km - send trucks max this far from home -- ammotruck.unloadtime = 600 -- 10 minutes - min time to unload ammunition -- ammotruck.waitingtime = 1800 -- 30 mintes - wait max this long until remunition is done --- ammotruck.monitor = -60 - 1 minute - AMMOTRUCK checks on things every 1 minute --- ammotruck.routeonroad = true - Trucks will **try** to drive on roads --- ammotruck.usearmygroup = false - if true, will make use of ARMYGROUP in the background (if used in DEV branch) +-- ammotruck.monitor = -60 -- 1 minute - AMMOTRUCK checks run every one minute +-- ammotruck.routeonroad = true -- Trucks will **try** to drive on roads +-- ammotruck.usearmygroup = false -- If true, will make use of ARMYGROUP in the background (if used in DEV branch) +-- ammotruck.reloads = 5 -- Maxn re-arms a truck can do before he needs to go home and restock. Set to -1 for unlimited -- -- ## 3 FSM Events to shape mission -- @@ -115,7 +115,7 @@ AMMOTRUCK = { ClassName = "AMMOTRUCK", lid = "", - version = "0.0.10", + version = "0.0.11", alias = "", debug = false, trucklist = {}, @@ -130,7 +130,8 @@ AMMOTRUCK = { monitor = -60, unloadtime = 600, waitingtime = 1800, - routeonroad = true + routeonroad = true, + reloads = 5, } --- @@ -158,6 +159,7 @@ AMMOTRUCK.State = { --@field #string targetname --@field Wrapper.Group#GROUP targetgroup --@field Core.Point#COORDINATE targetcoordinate +--@field #number reloads --- -- @param #AMMOTRUCK self @@ -371,6 +373,7 @@ function AMMOTRUCK:CheckReturningTrucks(dataset) truck.statusquo = AMMOTRUCK.State.IDLE truck.timestamp = timer.getAbsTime() truck.coordinate = coord + truck.reloads = self.reloads or 5 self:__TruckHome(1,truck) end end @@ -542,6 +545,7 @@ function AMMOTRUCK:CheckTrucksAlive() newtruck.statusquo = AMMOTRUCK.State.IDLE newtruck.timestamp = timer.getAbsTime() newtruck.coordinate = truck:GetCoordinate() + newtruck.reloads = self.reloads or 5 self.trucklist[name] = newtruck end end @@ -628,8 +632,10 @@ function AMMOTRUCK:onafterMonitor(From, Event, To) unloadingtrucks[#unloadingtrucks+1] = data elseif data.statusquo == AMMOTRUCK.State.RETURNING then returningtrucks[#returningtrucks+1] = data - idletrucks[#idletrucks+1] = data - found = true + if data.reloads > 0 or data.reloads == -1 then + idletrucks[#idletrucks+1] = data + found = true + end end else self.truckset[data.name] = nil @@ -639,7 +645,7 @@ function AMMOTRUCK:onafterMonitor(From, Event, To) local n=0 if found and remunition then -- match - local match = false + --local match = false for _,_truckdata in pairs(idletrucks) do local truckdata = _truckdata -- #AMMOTRUCK.data local truckcoord = truckdata.group:GetCoordinate() -- Core.Point#COORDINATE @@ -752,6 +758,12 @@ end end local scheduler = SCHEDULER:New(nil,destroyammo,{ammo},self.waitingtime) + + -- one reload less + if truck.reloads ~= -1 then + truck.reloads = truck.reloads - 1 + end + return self end --- @@ -792,4 +804,3 @@ function AMMOTRUCK:onafterStop(From, Event, To) self:T({From, Event, To}) return self end - From ffcfc448bec2f29d219a1dfb126afe80fd2fdb51 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 3 Jul 2023 13:53:59 +0200 Subject: [PATCH 294/603] Update Controllable.lua --- .../Moose/Wrapper/Controllable.lua | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index f62cb0ed4..45fad0c42 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -11,7 +11,7 @@ -- @module Wrapper.Controllable -- @image Wrapper_Controllable.JPG --- @type CONTROLLABLE +--- @type CONTROLLABLE -- @field DCS#Controllable DCSControllable The DCS controllable class. -- @field #string ControllableName The name of the controllable. -- @extends Wrapper.Positionable#POSITIONABLE @@ -1270,7 +1270,7 @@ end --- (AIR) Orbit at a position with at a given altitude and speed. Optionally, a race track pattern can be specified. -- @param #CONTROLLABLE self --- @param Core.Point#COORDINATE Coord Coordinate at which the CONTROLLABLE orbits. +-- @param Core.Point#COORDINATE Coord Coordinate at which the CONTROLLABLE orbits. Can also be given as a `DCS#Vec3` or `DCS#Vec2` object. -- @param #number Altitude Altitude in meters of the orbit pattern. Default y component of Coord. -- @param #number Speed Speed [m/s] flying the orbit pattern. Default 128 m/s = 250 knots. -- @param Core.Point#COORDINATE CoordRaceTrack (Optional) If this coordinate is specified, the CONTROLLABLE will fly a race-track pattern using this and the initial coordinate. @@ -1279,11 +1279,11 @@ function CONTROLLABLE:TaskOrbit( Coord, Altitude, Speed, CoordRaceTrack ) local Pattern = AI.Task.OrbitPattern.CIRCLE - local P1 = Coord:GetVec2() + local P1 = {x=Coord.x, y=Coord.z or Coord.y} local P2 = nil if CoordRaceTrack then Pattern = AI.Task.OrbitPattern.RACE_TRACK - P2 = CoordRaceTrack:GetVec2() + P2 = {x=CoordRaceTrack.x, y=CoordRaceTrack.z or CoordRaceTrack.y} end local Task = { @@ -1480,15 +1480,53 @@ function CONTROLLABLE:TaskFollow( FollowControllable, Vec3, LastWaypointIndex ) return DCSTask end +--- (AIR/HELO) Escort a ground controllable. +-- The unit / controllable will follow lead unit of the other controllable, additional units of both controllables will continue following their leaders. +-- The unit / controllable will also protect that controllable from threats of specified types. +-- @param #CONTROLLABLE self +-- @param #CONTROLLABLE FollowControllable The controllable to be escorted. +-- @param #number LastWaypointIndex (optional) Detach waypoint of another controllable. Once reached the unit / controllable Escort task is finished. +-- @param #number OrbitDistance (optional) Maximum distance helo will orbit around the ground unit in meters. Defaults to 2000 meters. +-- @param DCS#AttributeNameArray TargetTypes (optional) Array of AttributeName that is contains threat categories allowed to engage. Default {"Ground vehicles"}. See https://wiki.hoggit.us/view/DCS_enum_attributes +-- @return DCS#Task The DCS task structure. +function CONTROLLABLE:TaskGroundEscort( FollowControllable, LastWaypointIndex, OrbitDistance, TargetTypes ) + + -- Escort = { + -- id = 'GroundEscort', + -- params = { + -- groupId = Group.ID, -- must + -- engagementDistMax = Distance, -- Must. With his task it does not appear to actually define the range AI are allowed to attack at, rather it defines the size length of the orbit. The helicopters will fly up to this set distance before returning to the escorted group. + -- lastWptIndexFlag = boolean, -- optional + -- lastWptIndex = number, -- optional + -- targetTypes = array of AttributeName, -- must + -- lastWptIndexFlagChangedManually = boolean, -- must be true + -- } + -- } + + local DCSTask = { + id = 'GroundEscort', + params = { + groupId = FollowControllable and FollowControllable:GetID() or nil + engagementDistMax = OrbitDistance or 2000, + lastWptIndexFlag = LastWaypointIndex and true or false, + lastWptIndex = LastWaypointIndex, + targetTypes = TargetTypes or {"Ground vehicles"}, + lastWptIndexFlagChangedManually = true, + }, + } + + return DCSTask +end + --- (AIR) Escort another airborne controllable. -- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders. -- The unit / controllable will also protect that controllable from threats of specified types. -- @param #CONTROLLABLE self -- @param #CONTROLLABLE FollowControllable The controllable to be escorted. -- @param DCS#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. --- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Follow task is finished. --- @param #number EngagementDistance Maximal distance from escorted controllable to threat. If the threat is already engaged by escort escort will disengage if the distance becomes greater than 1.5 * engagementDistMax. --- @param DCS#AttributeNameArray TargetTypes Array of AttributeName that is contains threat categories allowed to engage. Default {"Air"}. +-- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Escort task is finished. +-- @param #number EngagementDistance Maximal distance from escorted controllable to threat in meters. If the threat is already engaged by escort escort will disengage if the distance becomes greater than 1.5 * engagementDistMax. +-- @param DCS#AttributeNameArray TargetTypes Array of AttributeName that is contains threat categories allowed to engage. Default {"Air"}. See https://wiki.hoggit.us/view/DCS_enum_attributes -- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes ) @@ -1504,8 +1542,7 @@ function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, E -- } -- } - local DCSTask - DCSTask = { + local DCSTask = { id = 'Escort', params = { groupId = FollowControllable and FollowControllable:GetID() or nil, @@ -1906,7 +1943,7 @@ end -- -- GroundGroup = GROUP:FindByName( "Vehicle" ) -- --- -- @param Wrapper.Group#GROUP GroundGroup +-- --- @param Wrapper.Group#GROUP GroundGroup -- function RouteToZone( Vehicle, ZoneRoute ) -- -- local Route = {} From 0d6fe49a4160c2416f67867fcc6f805847d0375b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 3 Jul 2023 16:45:02 +0200 Subject: [PATCH 295/603] changes --- Moose Development/Moose/Functional/AmmoTruck.lua | 14 +++++++------- Moose Development/Moose/Wrapper/Controllable.lua | 15 ++++++++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index 218921e6c..7a87ce107 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -115,7 +115,7 @@ AMMOTRUCK = { ClassName = "AMMOTRUCK", lid = "", - version = "0.0.11", + version = "0.0.12", alias = "", debug = false, trucklist = {}, @@ -373,7 +373,7 @@ function AMMOTRUCK:CheckReturningTrucks(dataset) truck.statusquo = AMMOTRUCK.State.IDLE truck.timestamp = timer.getAbsTime() truck.coordinate = coord - truck.reloads = self.reloads or 5 + truck.reloads = self.reloads or 5 self:__TruckHome(1,truck) end end @@ -545,7 +545,7 @@ function AMMOTRUCK:CheckTrucksAlive() newtruck.statusquo = AMMOTRUCK.State.IDLE newtruck.timestamp = timer.getAbsTime() newtruck.coordinate = truck:GetCoordinate() - newtruck.reloads = self.reloads or 5 + newtruck.reloads = self.reloads or 5 self.trucklist[name] = newtruck end end @@ -632,10 +632,10 @@ function AMMOTRUCK:onafterMonitor(From, Event, To) unloadingtrucks[#unloadingtrucks+1] = data elseif data.statusquo == AMMOTRUCK.State.RETURNING then returningtrucks[#returningtrucks+1] = data - if data.reloads > 0 or data.reloads == -1 then - idletrucks[#idletrucks+1] = data - found = true - end + if data.reloads > 0 or data.reloads == -1 then + idletrucks[#idletrucks+1] = data + found = true + end end else self.truckset[data.name] = nil diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 45fad0c42..37067d492 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -52,6 +52,7 @@ -- * @{#CONTROLLABLE.TaskEmbarking}: (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. -- * @{#CONTROLLABLE.TaskEmbarkToTransport}: (GROUND) Embark to a Transport landed at a location. -- * @{#CONTROLLABLE.TaskEscort}: (AIR) Escort another airborne controllable. +-- * @{#CONTROLLABLE.TaskGroundEscort}: (AIR/HELO) Escort a ground controllable. -- * @{#CONTROLLABLE.TaskFAC_AttackGroup}: (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction. -- * @{#CONTROLLABLE.TaskFireAtPoint}: (GROUND) Fire some or all ammunition at a VEC2 point. -- * @{#CONTROLLABLE.TaskFollow}: (AIR) Following another airborne controllable. @@ -1487,7 +1488,7 @@ end -- @param #CONTROLLABLE FollowControllable The controllable to be escorted. -- @param #number LastWaypointIndex (optional) Detach waypoint of another controllable. Once reached the unit / controllable Escort task is finished. -- @param #number OrbitDistance (optional) Maximum distance helo will orbit around the ground unit in meters. Defaults to 2000 meters. --- @param DCS#AttributeNameArray TargetTypes (optional) Array of AttributeName that is contains threat categories allowed to engage. Default {"Ground vehicles"}. See https://wiki.hoggit.us/view/DCS_enum_attributes +-- @param DCS#AttributeNameArray TargetTypes (optional) Array of AttributeName that is contains threat categories allowed to engage. Default {"Ground vehicles"}. See [https://wiki.hoggit.us/view/DCS_enum_attributes](https://wiki.hoggit.us/view/DCS_enum_attributes) -- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskGroundEscort( FollowControllable, LastWaypointIndex, OrbitDistance, TargetTypes ) @@ -1506,12 +1507,12 @@ function CONTROLLABLE:TaskGroundEscort( FollowControllable, LastWaypointIndex, O local DCSTask = { id = 'GroundEscort', params = { - groupId = FollowControllable and FollowControllable:GetID() or nil - engagementDistMax = OrbitDistance or 2000, - lastWptIndexFlag = LastWaypointIndex and true or false, - lastWptIndex = LastWaypointIndex, - targetTypes = TargetTypes or {"Ground vehicles"}, - lastWptIndexFlagChangedManually = true, + groupId = FollowControllable and FollowControllable:GetID() or nil, + engagementDistMax = OrbitDistance or 2000, + lastWptIndexFlag = LastWaypointIndex and true or false, + lastWptIndex = LastWaypointIndex, + targetTypes = TargetTypes or {"Ground vehicles"}, + lastWptIndexFlagChangedManually = true, }, } From be3fc2c964e575fe78f1717e20c9bc651af4c5d3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 3 Jul 2023 17:05:54 +0200 Subject: [PATCH 296/603] #GROUNDESCORT --- Moose Development/Moose/Ops/Auftrag.lua | 57 ++++++++++++++++++++- Moose Development/Moose/Utilities/Enums.lua | 2 + 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 7f3ff6337..0abda645a 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -260,6 +260,10 @@ -- -- Not implemented yet. -- +-- ## Ground Escort +-- +-- An escort mission can be created with the @{#AUFTRAG.NewGROUNDESCORT}() function. +-- -- ## Intercept -- -- An intercept mission can be created with the @{#AUFTRAG.NewINTERCEPT}() function. @@ -407,6 +411,7 @@ _AUFTRAGSNR=0 -- @field #string FAC Forward AirController mission. -- @field #string FACA Forward AirController airborne mission. -- @field #string FERRY Ferry mission. +-- @field #string GROUNDESCORT Ground escort mission. -- @field #string INTERCEPT Intercept mission. -- @field #string ORBIT Orbit mission. -- @field #string GCICAP Similar to CAP but no auto engage targets. @@ -451,6 +456,7 @@ AUFTRAG.Type={ FAC="FAC", FACA="FAC-A", FERRY="Ferry Flight", + GROUNDESCORT="Ground Escort", INTERCEPT="Intercept", ORBIT="Orbit", GCICAP="Ground Controlled CAP", @@ -1741,6 +1747,43 @@ function AUFTRAG:NewBOMBCARPET(Target, Altitude, CarpetLength) return mission end +--- **[AIR/HELO]** Create a GROUNDESCORT (or FOLLOW) mission. Helo will escort a **ground** group and automatically engage certain target types. +-- @param #AUFTRAG self +-- @param Wrapper.Group#GROUP EscortGroup The ground group to escort. +-- @param #number OrbitDistance Orbit to/from the lead unit this many NM. Defaults to 3 NM. +-- @param #table TargetTypes Types of targets to engage automatically. Default is {"Ground vehicles"}, i.e. all enemy ground units. Use an empty set {} for a simple "FOLLOW" mission. +-- @return #AUFTRAG self +function AUFTRAG:NewESCORT(EscortGroup, OrbitDistance, TargetTypes) + + local mission=AUFTRAG:New(AUFTRAG.Type.GROUNDESCORT) + + -- If only a string is passed we set a variable and check later if the group exists. + if type(EscortGroup)=="string" then + mission.escortGroupName=EscortGroup + mission:_TargetFromObject() + else + mission:_TargetFromObject(EscortGroup) + end + + -- DCS task parameters: + mission.orbitDistance=OrbitDistance and UTILS.NMToMeters(OrbitDistance) or UTILS.NMToMeters(3) + --mission.engageMaxDistance=EngageMaxDistance and UTILS.NMToMeters(EngageMaxDistance) or UTILS.NMToMeters(5) + mission.engageTargetTypes=TargetTypes or {"Ground vehicles"} + + -- Mission options: + mission.missionTask=ENUMS.MissionTask.GROUNDESCORT + mission.missionFraction=0.1 + mission.missionAltitude=1000 + mission.optionROE=ENUMS.ROE.OpenFire -- TODO: what's the best ROE here? Make dependent on ESCORT or FOLLOW! + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.HELICOPTER} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + --- **[AIR]** Create an ESCORT (or FOLLOW) mission. Flight will escort another group and automatically engage certain target types. -- @param #AUFTRAG self @@ -5844,10 +5887,20 @@ function AUFTRAG:GetDCSMissionTask() -- ESCORT Mission -- -------------------- - local DCStask=CONTROLLABLE.TaskEscort(nil, self.engageTarget:GetObject(), self.escortVec3, LastWaypointIndex, self.engageMaxDistance, self.engageTargetTypes) + local DCStask=CONTROLLABLE.TaskEscort(nil, self.engageTarget:GetObject(), self.escortVec3, nil, self.engageMaxDistance, self.engageTargetTypes) table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.GROUNDESCORT then + -------------------- + -- GROUNDESCORT Mission -- + -------------------- + + local DCSTask=CONTROLLABLE.TaskGroundEscort(nil,self.engageTarget:GetObject(),nil,self.orbitDistance,self.engageTargetTypes) + + table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.FACA then ------------------ @@ -6529,6 +6582,8 @@ function AUFTRAG:GetMissionTaskforMissionType(MissionType) mtask=ENUMS.MissionTask.AFAC elseif MissionType==AUFTRAG.Type.FERRY then mtask=ENUMS.MissionTask.NOTHING + elseif MissionType==AUFTRAG.Type.GROUNDESCORT then + mtask=ENUMS.MissionTask.GROUNDESCORT elseif MissionType==AUFTRAG.Type.INTERCEPT then mtask=ENUMS.MissionTask.INTERCEPT elseif MissionType==AUFTRAG.Type.RECON then diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index 6c47c5216..b00552dcf 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -236,6 +236,7 @@ ENUMS.WeaponType.Any={ -- @field #string ESCORT Escort another group. -- @field #string FIGHTERSWEEP Fighter sweep. -- @field #string GROUNDATTACK Ground attack. +-- @field #string GROUNDESCORT Ground escort another group. -- @field #string INTERCEPT Intercept. -- @field #string PINPOINTSTRIKE Pinpoint strike. -- @field #string RECONNAISSANCE Reconnaissance mission. @@ -251,6 +252,7 @@ ENUMS.MissionTask={ CAP="CAP", CAS="CAS", ESCORT="Escort", + GROUNDESCORT="Ground escort", FIGHTERSWEEP="Fighter Sweep", GROUNDATTACK="Ground Attack", INTERCEPT="Intercept", From 7dd270dc8bb9f972bf6b5282c62a93354e121449 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 3 Jul 2023 17:26:47 +0200 Subject: [PATCH 297/603] #GROUND ESCORT --- Moose Development/Moose/Ops/Auftrag.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 0abda645a..7d4deac86 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1750,10 +1750,10 @@ end --- **[AIR/HELO]** Create a GROUNDESCORT (or FOLLOW) mission. Helo will escort a **ground** group and automatically engage certain target types. -- @param #AUFTRAG self -- @param Wrapper.Group#GROUP EscortGroup The ground group to escort. --- @param #number OrbitDistance Orbit to/from the lead unit this many NM. Defaults to 3 NM. +-- @param #number OrbitDistance Orbit to/from the lead unit this many NM. Defaults to 1.5 NM. -- @param #table TargetTypes Types of targets to engage automatically. Default is {"Ground vehicles"}, i.e. all enemy ground units. Use an empty set {} for a simple "FOLLOW" mission. -- @return #AUFTRAG self -function AUFTRAG:NewESCORT(EscortGroup, OrbitDistance, TargetTypes) +function AUFTRAG:NewGROUNDESCORT(EscortGroup, OrbitDistance, TargetTypes) local mission=AUFTRAG:New(AUFTRAG.Type.GROUNDESCORT) @@ -1766,16 +1766,16 @@ function AUFTRAG:NewESCORT(EscortGroup, OrbitDistance, TargetTypes) end -- DCS task parameters: - mission.orbitDistance=OrbitDistance and UTILS.NMToMeters(OrbitDistance) or UTILS.NMToMeters(3) + mission.orbitDistance=OrbitDistance and UTILS.NMToMeters(OrbitDistance) or UTILS.NMToMeters(1.5) --mission.engageMaxDistance=EngageMaxDistance and UTILS.NMToMeters(EngageMaxDistance) or UTILS.NMToMeters(5) mission.engageTargetTypes=TargetTypes or {"Ground vehicles"} -- Mission options: mission.missionTask=ENUMS.MissionTask.GROUNDESCORT mission.missionFraction=0.1 - mission.missionAltitude=1000 + mission.missionAltitude=100 mission.optionROE=ENUMS.ROE.OpenFire -- TODO: what's the best ROE here? Make dependent on ESCORT or FOLLOW! - mission.optionROT=ENUMS.ROT.PassiveDefense + mission.optionROT=ENUMS.ROT.EvadeFire mission.categories={AUFTRAG.Category.HELICOPTER} @@ -5899,7 +5899,7 @@ function AUFTRAG:GetDCSMissionTask() local DCSTask=CONTROLLABLE.TaskGroundEscort(nil,self.engageTarget:GetObject(),nil,self.orbitDistance,self.engageTargetTypes) - table.insert(DCStasks, DCStask) + table.insert(DCStasks, DCSTask) elseif self.type==AUFTRAG.Type.FACA then From fd1257052bce91fcbf856da32a39a529d7e7a2ad Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 7 Jul 2023 15:15:28 +0200 Subject: [PATCH 298/603] # --- Moose Development/Moose/Functional/AmmoTruck.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index 83b177bef..648d40d06 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -77,7 +77,7 @@ -- ammotruck.monitor = -60 -- 1 minute - AMMOTRUCK checks run every one minute -- ammotruck.routeonroad = true -- Trucks will **try** to drive on roads -- ammotruck.usearmygroup = false -- If true, will make use of ARMYGROUP in the background (if used in DEV branch) --- ammotruck.reloads = 5 -- Maxn re-arms a truck can do before he needs to go home and restock. Set to -1 for unlimited +-- ammotruck.reloads = 5 -- Maxn re-arms a truck can do before he needs to go home and restock. Set to -1 for unlimited -- -- ## 3 FSM Events to shape mission -- From 0326bee7a3410bf08c4f1e7634bb232389808f43 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 11 Jul 2023 15:42:14 +0200 Subject: [PATCH 299/603] # --- Moose Development/Moose/Core/ClientMenu.lua | 892 ++++++++++++++++++ Moose Development/Moose/Core/Menu.lua | 1 + Moose Development/Moose/Core/Spot.lua | 26 +- Moose Development/Moose/Modules.lua | 1 + Moose Development/Moose/Modules_local.lua | 1 + .../Moose/Utilities/Routines.lua | 2 +- Moose Development/Moose/Utilities/Utils.lua | 2 +- Moose Development/Moose/Wrapper/Airbase.lua | 10 +- Moose Setup/Moose.files | 1 + 9 files changed, 926 insertions(+), 10 deletions(-) create mode 100644 Moose Development/Moose/Core/ClientMenu.lua diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua new file mode 100644 index 000000000..3f43642e4 --- /dev/null +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -0,0 +1,892 @@ +--- **Core** - Client Menu Management. +-- +-- **Main Features:** +-- +-- * For complex, non-static menu structures +-- * Separation of menu tree creation from pushing it to clients +-- * Works with a SET_CLIENT set of clients +-- * Allow manipulation of the shadow tree in various ways +-- * Push to all or only one client +-- * Change entries' menu text, even if they have a sub-structure +-- * Option to make an entry usable once +-- +-- === +-- +-- ### Author: **applevangelist** +-- +-- === +-- @module Core.ClientMenu +-- @image Core_Menu.JPG +-- last change: July 2023 + +-- TODO +---------------------------------------------------------------------------------------------------------------- +-- +-- CLIENTMENU +-- +---------------------------------------------------------------------------------------------------------------- + +--- +-- @type CLIENTMENU +-- @field #string ClassName Class Name +-- @field #string lid Lid for log entries +-- @field #string version Version string +-- @field #string name Name +-- @field #table path +-- @field #table parentpath +-- @field #CLIENTMENU Parent +-- @field Wrapper.Client#CLIENT client +-- @field #number GID +-- @field #number ID +-- @field Wrapper.Group#GROUP group +-- @field #string Function +-- @field #table Functionargs +-- @field #table Children +-- @field #boolean Once +-- @field #boolean Generic +-- @field #boolean debug +-- @field #CLIENTMENUMANAGER Controller +-- @extends Core.Base#BASE + +--- +-- @field #CLIENTMENU +CLIENTMENU = { + ClassName = "CLIENTMENUE", + lid = "", + version = "0.0.1", + name = nil, + path = nil, + group = nil, + client = nil, + GID = nil, + Children = {}, + Once = false, + Generic = false, + debug = false, + Controller = nil, +} + +--- +-- @field #CLIENTMENU_ID +CLIENTMENU_ID = 0 + +--- Create an new CLIENTMENU object. +-- @param #CLIENTMENU self +-- @param Wrapper.Client#CLIENT Client The client for whom this entry is. +-- @param #string Text Text of the F10 menu entry. +-- @param #CLIENTMENU Parent The parent menu entry. +-- @param #string Function (optional) Function to call when the entry is used. +-- @param ... (optional) Arguments for the Function, comma separated +-- @return #CLIENTMENU self +function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) + -- Inherit everything from BASE class. + local self=BASE:Inherit(self, BASE:New()) -- #CLIENTMENU + CLIENTMENU_ID = CLIENTMENU_ID + 1 + self.ID = CLIENTMENU_ID + if Client then + self.group = Client:GetGroup() + self.client = Client + self.GID = self.group:GetID() + else + self.Generic = true + end + self.name = Text or "unknown entry" + if Parent then + self.parentpath = Parent:GetPath() + Parent:AddChild(self) + end + self.Parent = Parent + self.Function = Function + self.Functionargs = arg + if self.Functionargs and self.debug then + self:I({"Functionargs",self.Functionargs}) + end + if not self.Generic then + if Function ~= nil then + local ErrorHandler = function( errmsg ) + env.info( "MOOSE Error in CLIENTMENU COMMAND function: " .. errmsg ) + if BASE.Debug ~= nil then + env.info( BASE.Debug.traceback() ) + end + return errmsg + end + self.CallHandler = function() + local function MenuFunction() + return self.Function( unpack( self.Functionargs ) ) + end + local Status, Result = xpcall( MenuFunction, ErrorHandler) + if self.Once == true then + self:Clear() + end + end + self.path = missionCommands.addCommandForGroup(self.GID,Text,self.parentpath, self.CallHandler) + else + self.path = missionCommands.addSubMenuForGroup(self.GID,Text,self.parentpath) + end + else + if self.parentpath then + self.path = UTILS.DeepCopy(self.parentpath) + else + self.path = {} + end + self.path[#self.path+1] = Text + self:T({self.path}) + end + self.Once = false + -- Log id. + self.lid=string.format("CLIENTMENU %s | %s | ", self.ID, self.name) + self:T(self.lid.."Created") + return self +end + +--- Set the CLIENTMENUMANAGER for this entry. +-- @param #CLIENTMENU self +-- @param #CLIENTMENUMANAGER Controller The controlling object. +-- @return #CLIENTMENU self +function CLIENTMENU:SetController(Controller) + self.Controller = Controller + return self +end + +--- The entry will be deleted after being used used - for menu entries with functions only. +-- @param #CLIENTMENU self +-- @return #CLIENTMENU self +function CLIENTMENU:SetOnce() + self:T(self.lid.."SetOnce") + self.Once = true + return self +end + +--- Remove the entry from the F10 menu. +-- @param #CLIENTMENU self +-- @return #CLIENTMENU self +function CLIENTMENU:RemoveF10() + self:T(self.lid.."RemoveF10") + if not self.Generic then + missionCommands.removeItemForGroup(self.GID , self.path ) + end + return self +end + +--- Get the menu path table. +-- @param #CLIENTMENU self +-- @return #table Path +function CLIENTMENU:GetPath() + self:T(self.lid.."GetPath") + return self.path +end + +--- Link a child entry. +-- @param #CLIENTMENU self +-- @param #CLIENTMENU Child The entry to link as a child. +-- @return #CLIENTMENU self +function CLIENTMENU:AddChild(Child) + self:T(self.lid.."AddChild "..Child.ID) + table.insert(self.Children,Child.ID,Child) + return self +end + +--- Remove a child entry. +-- @param #CLIENTMENU self +-- @param #CLIENTMENU Child The entry to remove from the children. +-- @return #CLIENTMENU self +function CLIENTMENU:RemoveChild(Child) + self:T(self.lid.."RemoveChild "..Child.ID) + table.remove(self.Children,Child.ID) + return self +end + +--- Remove all subentries (children) from this entry. +-- @param #CLIENTMENU self +-- @return #CLIENTMENU self +function CLIENTMENU:RemoveSubEntries() + self:T(self.lid.."RemoveSubEntries") + --self:T({self.Children}) + for _id,_entry in pairs(self.Children) do + self:T("Removing ".._id) + if _entry then + _entry:RemoveSubEntries() + _entry:RemoveF10() + if _entry.Parent then + _entry.Parent:RemoveChild(self) + end + if self.Controller then + self.Controller:_RemoveByID(_entry.ID) + end + _entry = nil + end + end + return self +end + +--- Remove this entry and all subentries (children) from this entry. +-- @param #CLIENTMENU self +-- @return #CLIENTMENU self +function CLIENTMENU:Clear() + self:T(self.lid.."Clear") + for _id,_entry in pairs(self.Children) do + if _entry then + _entry:RemoveSubEntries() + _entry = nil + end + end + self:RemoveF10() + if self.Parent then + self.Parent:RemoveChild(self) + end + if self.Controller then + self.Controller:_RemoveByID(self.ID) + end + return self +end + +-- TODO +---------------------------------------------------------------------------------------------------------------- +-- +-- CLIENTMENUMANAGER +-- +---------------------------------------------------------------------------------------------------------------- + +---CLIENTMENUMANAGER class +-- @type CLIENTMENUMANAGER +-- @field #string ClassName Class Name +-- @field #string lid Lid for log entries +-- @field #string version Version string +-- @field #string name Name +-- @field Core.Set#SET_CLIENT clientset The set of clients this menu manager is for +-- @field #table structure +-- @field #table replacementstructure +-- @field #table rootentries +-- @field #number entrycount +-- @field #boolean debug +-- @extends Core.Base#BASE + +--- *As a child my family's menu consisted of two choices: take it, or leave it.* +-- +-- === +-- +-- # CLIENTMENU and CLIENTMENUMANAGER +-- +-- Manage menu structures for a SET_CLIENT of clients. +-- +-- ## Concept +-- +-- Separate creation of a menu tree structure from pushing it to each client. Create a shadow "reference" menu structure tree for your client pilot's in a mission. +-- This can then be propagated to all clients. Manipulate the entries in the structure with removing, clearing or changing single entries, create replacement sub-structures +-- for entries etc, push to one or all clients. +-- +-- Many functions can either change the tree for one client or for all clients. +-- +-- ## Create a base reference tree and send to all clients +-- +-- local clientset = SET_CLIENT:New():FilterStart() +-- +-- local menumgr = CLIENTMENUMANAGER:New(clientset,"Dayshift") +-- local mymenu = menumgr:NewEntry("Top") +-- local mymenu_lv1a = menumgr:NewEntry("Level 1 a",mymenu) +-- local mymenu_lv1b = menumgr:NewEntry("Level 1 b",mymenu) +-- -- next one is a command menu entry, which can only be used once +-- local mymenu_lv1c = menumgr:NewEntry("Action Level 1 c",mymenu, testfunction, "testtext"):SetOnce() +-- +-- local mymenu_lv2a = menumgr:NewEntry("Go here",mymenu_lv1a) +-- local mymenu_lv2b = menumgr:NewEntry("Level 2 ab",mymenu_lv1a) +-- local mymenu_lv2c = menumgr:NewEntry("Level 2 ac",mymenu_lv1a) +-- +-- local mymenu_lv2ba = menumgr:NewEntry("Level 2 ba",mymenu_lv1b) +-- local mymenu_lv2bb = menumgr:NewEntry("Level 2 bb",mymenu_lv1b) +-- local mymenu_lv2bc = menumgr:NewEntry("Level 2 bc",mymenu_lv1b) +-- +-- local mymenu_lv3a = menumgr:NewEntry("Level 3 aaa",mymenu_lv2a) +-- local mymenu_lv3b = menumgr:NewEntry("Level 3 aab",mymenu_lv2a) +-- local mymenu_lv3c = menumgr:NewEntry("Level 3 aac",mymenu_lv2a) +-- +-- menumgr:Propagate() +-- +-- ## Remove a single entry's subtree +-- +-- menumgr:RemoveSubEntries(mymenu_lv3a) +-- + -- ## Remove a single entry and also it's subtree +-- +-- menumgr:Clear(mymenu_lv3a) +-- +-- ## Add a single entry +-- +-- local baimenu = menumgr:NewEntry("BAI",mymenu_lv1b) +-- menumgr:AddEntry(baimenu) +-- +-- ## Prepare and push a partial replacement in the tree +-- +-- menumgr:PrepareNewReplacementStructure() +-- local submenu = menumgr:NewReplacementEntry("New Level 2 ba",mymenu_lv2a) +-- menumgr:NewReplacementEntry("New Level 2 bb",mymenu_lv2a) +-- menumgr:NewReplacementEntry("Deleted",mymenu_lv2a) +-- menumgr:NewReplacementEntry("New Level 2 bd",mymenu_lv2a) +-- menumgr:NewReplacementEntry("SubLevel 3 baa",submenu) +-- menumgr:NewReplacementEntry("SubLevel 3 bab",submenu) +-- menumgr:NewReplacementEntry("SubLevel 3 bac",submenu) +-- menumgr:NewReplacementEntry("SubLevel 3 bad",submenu) +-- menumgr:ReplaceEntries(mymenu_lv2a) +-- +-- ## Change the text of an entry in the menu tree +-- +-- menumgr:ChangeEntryTextForAll(mymenu_lv1b,"Attack") +-- +-- ## Reset a single clients menu tree +-- +-- menumgr:ResetMenu(client) +-- +-- ## Reset all and clear the reference tree +-- +-- menumgr:ResetMenuComplete() +--- +-- @type CLIENTMENUMANAGER.Structure +-- @field #table generic +-- @field #table IDs + +--- +-- @field #CLIENTMENUMANAGER +CLIENTMENUMANAGER = { + ClassName = "CLIENTMENUMANAGER", + lid = "", + version = "0.0.1", + name = nil, + clientset = nil, + --- + -- @field #CLIENTMENUMANAGER.Structure + structure = { + generic = {}, + IDs = {}, + }, + --- + -- #CLIENTMENUMANAGER.ReplacementStructure + replacementstructure = { + generic = {}, + IDs = {}, + }, + entrycount = 0, + rootentries = {}, + debug = true, +} + + +--- Create a new ClientManager instance. +-- @param #CLIENTMENUMANAGER self +-- @param Core.Set#SET_CLIENT ClientSet The set of clients to manage. +-- @param #string Alias The name of this manager. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:New(ClientSet, Alias) + -- Inherit everything from FSM class. + local self=BASE:Inherit(self, BASE:New()) -- #CLIENTMENUMANAGER + self.clientset = ClientSet + self.name = Alias or "Nightshift" + -- Log id. + self.lid=string.format("CLIENTMENUMANAGER %s | %s | ", self.version, self.name) + if self.debug then + self:I(self.lid.."Created") + end + return self +end + +--- Create a new entry in the generic structure. +-- @param #CLIENTMENUMANAGER self +-- @param #string Text Text of the F10 menu entry. +-- @param #CLIENTMENU Parent The parent menu entry. +-- @param #string Function (optional) Function to call when the entry is used. +-- @param ... (optional) Arguments for the Function, comma separated. +-- @return #CLIENTMENU Entry +function CLIENTMENUMANAGER:NewEntry(Text,Parent,Function,...) + self:T(self.lid.."NewEntry "..Text or "None") + self.entrycount = self.entrycount + 1 + local entry = CLIENTMENU:NewEntry(nil,Text,Parent,Function,unpack(arg)) + self.structure.generic[self.entrycount] = entry + self.structure.IDs[entry.ID] = self.entrycount + if not Parent then + self.rootentries[self.entrycount] = self.entrycount + end + return entry +end + +--- Find **first** matching entry in the generic structure by the menu text. +-- @param #CLIENTMENUMANAGER self +-- @param #string Text Text of the F10 menu entry. +-- @return #CLIENTMENU Entry The #CLIENTMENU object found or nil. +-- @return #number GID GID The GID found or nil. +function CLIENTMENUMANAGER:FindEntryByText(Text) + self:T(self.lid.."FindEntryByText "..Text or "None") + local entry = nil + local gid = nil + for _gid,_entry in UTILS.spairs(self.structure.generic) do + local Entry = _entry -- #CLIENTMENU + if Entry and Entry.name == Text then + entry = Entry + gid = _gid + end + end + return entry, gid +end + +--- Find first matching entry in the generic structure by the GID. +-- @param #CLIENTMENUMANAGER self +-- @param #number GID The GID of the entry to find. +-- @return #CLIENTMENU Entry The #CLIENTMENU object found or nil. +function CLIENTMENUMANAGER:GetEntryByGID(GID) + self:T(self.lid.."GetEntryByGID "..GID or "None") + if GID and type(GID) == "number" then + return self.structure.generic[GID] + else + return nil + end +end + +--- Alter the text of an entry in the generic structure and push to all clients. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The menu entry. +-- @param #string Text Text of the F10 menu entry. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:ChangeEntryTextForAll(Entry,Text) + self:T(self.lid.."ChangeEntryTextForAll "..Text or "None") + for _,_client in pairs(self.clientset.Set) do + local client = _client -- Wrapper.Client#CLIENT + if client and client:IsAlive() then + self:ChangeEntryText(Entry,Text, client) + end + end + return self +end + +--- Alter the text of an entry in the generic structure and push to one specific client. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The menu entry. +-- @param #string Text Text of the F10 menu entry. +-- @param Wrapper.Client#CLIENT Client The client for whom to alter the entry +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:ChangeEntryText(Entry,Text, Client) + self:T(self.lid.."ChangeEntryText "..Text or "None") + + local text = Text or "none" + local oldtext = Entry.name + Entry.name = text + + local newstructure = {} + local changed = 0 + + local function ChangePath(path,oldtext,newtext) + local newpath = {} + for _id,_text in UTILS.spairs(path) do + local txt = _text + if _text == oldtext then + txt = newtext + end + newpath[_id] = txt + end + return newpath + end + + local function AlterPath(children) + for _,_entry in pairs(children) do + local entry = _entry -- #CLIENTMENU + local newpath = ChangePath(entry.path,oldtext,text) + local newparentpath = ChangePath(entry.parentpath,oldtext,text) + entry.path = nil + entry.parentpath = nil + entry.path = newpath + entry.parentpath = newparentpath + self:T({entry.ID}) + --self:T({entry.parentpath}) + newstructure[entry.ID] = UTILS.DeepCopy(entry) + changed = changed + 1 + if entry.Children and #entry.Children > 0 then + AlterPath(entry.Children) + end + end + end + + -- get the entry + local ID = Entry.ID + local GID = self.structure.IDs[ID] + local playername = Client:GetPlayerName() + local children = self.structure[playername][GID].Children + AlterPath(children) + + self:T("Changed entries: "..changed) + + local NewParent = self:NewEntry(Entry.name,Entry.Parent,Entry.Function,unpack(Entry.Functionargs)) + + for _,_entry in pairs(children) do + self:T("Changed parent for ".._entry.ID.." | GID ".._entry.GID) + local entry = _entry -- #CLIENTMENU + entry.Parent = NewParent + end + + self:PrepareNewReplacementStructure() + + for _,_entry in pairs(newstructure) do + self:T("Changed entry: ".._entry.ID.." | GID ".._entry.GID) + local entry = _entry -- #CLIENTMENU + self:NewReplacementEntry(entry.name,entry.Parent,entry.Function,unpack(entry.Functionargs)) + end + + + self:AddEntry(NewParent) + self:ReplaceEntries(NewParent) + + self:Clear(Entry) + + return self +end + +--- Create a new entry in the replacement structure. +-- @param #CLIENTMENUMANAGER self +-- @param #string Text Text of the F10 menu entry. +-- @param #CLIENTMENU Parent The parent menu entry. +-- @param #string Function (optional) Function to call when the entry is used. +-- @param ... (optional) Arguments for the Function, comma separated +-- @return #CLIENTMENU Entry +function CLIENTMENUMANAGER:NewReplacementEntry(Text,Parent,Function,...) + self:T(self.lid.."NewReplacementEntry "..Text or "None") + self.entrycount = self.entrycount + 1 + local entry = CLIENTMENU:NewEntry(nil,Text,Parent,Function,unpack(arg)) + self.replacementstructure.generic[self.entrycount] = entry + self.replacementstructure.IDs[entry.ID] = self.entrycount + local pID = Parent and Parent.ID or "none" + if self.debug then + self:I("Entry ID = "..self.entrycount.." | Parent ID = "..tostring(pID)) + end + if not Parent then + self.rootentries[self.entrycount] = self.entrycount + end + return entry +end + +--- Prepare a new replacement structure. Deletes the previous one. +-- @param #CLIENTMENUMANAGER self +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:PrepareNewReplacementStructure() + self:T(self.lid.."PrepareNewReplacementStructure") + self.replacementstructure = nil -- #CLIENTMENUMANAGER.Structure + self.replacementstructure = { + generic = {}, + IDs = {}, + } + return self +end + +--- [Internal] Merge the replacement structure into the generic structure. +-- @param #CLIENTMENUMANAGER self +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:_MergeReplacementData() + self:T(self.lid.."_MergeReplacementData") + for _id,_entry in pairs(self.replacementstructure.generic) do + self.structure.generic[_id] = _entry + end + for _id,_entry in pairs(self.replacementstructure.IDs) do + self.structure.IDs[_id] = _entry + end + self:_CleanUpPlayerStructure() + return self +end + +--- Replace entries under the Parent entry with the Replacement structure created prior for all clients. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Parent The parent entry under which to replace with the new structure. +-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:ReplaceEntries(Parent,Client) + self:T(self.lid.."ReplaceEntries") + -- clear Parent substructure + local Set = self.clientset.Set + if Client then + Set = {Client} + else + self:RemoveSubEntries(Parent) + end + for _,_client in pairs(Set) do + local client = _client -- Wrapper.Client#CLIENT + if client and client:IsAlive() then + local playername = client:GetPlayerName() + --self.structure[playername] = {} + for _id,_entry in UTILS.spairs(self.replacementstructure.generic) do + local entry = _entry -- #CLIENTMENU + local parent = Parent + self:T("Posted Parent = "..Parent.ID) + if entry.Parent and entry.Parent.name then + parent = self:_GetParentEntry(self.replacementstructure.generic,entry.Parent.name) or Parent + self:T("Found Parent = "..parent.ID) + end + self.structure[playername][_id] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) + self.structure[playername][_id].Once = entry.Once + end + end + end + self:_MergeReplacementData() + return self +end + +--- [Internal] Find a parent entry in a given structure by name. +-- @param #CLIENTMENUMANAGER self +-- @param #table Structure Table of entries. +-- @param #string Name Name to find. +-- @return #CLIENTMENU Entry +function CLIENTMENUMANAGER:_GetParentEntry(Structure,Name) + self:T(self.lid.."_GetParentEntry") + local found = nil + for _,_entry in pairs(Structure) do + local entry = _entry -- #CLIENTMENU + if entry.name == Name then + found = entry + break + end + end + return found +end + +--- Push the complete menu structure to each of the clients in the set. +-- @param #CLIENTMENUMANAGER self +-- @param Wrapper.Client#CLIENT Client (optional) If given, propagate only for this client. +-- @return #CLIENTMENU Entry +function CLIENTMENUMANAGER:Propagate(Client) + self:T(self.lid.."Propagate") + local Set = self.clientset.Set + if Client then + Set = {Set} + end + for _,_client in pairs(Set) do + local client = _client -- Wrapper.Client#CLIENT + if client and client:IsAlive() then + local playername = client:GetPlayerName() + self.structure[playername] = {} + for _id,_entry in pairs(self.structure.generic) do + local entry = _entry -- #CLIENTMENU + local parent = nil + if entry.Parent and entry.Parent.name then + parent = self:_GetParentEntry(self.structure[playername],entry.Parent.name) + end + self.structure[playername][_id] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) + self.structure[playername][_id].Once = entry.Once + end + end + end + return self +end + +--- Push a single previously created entry into the menu structure of all clients. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The entry to add. +-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:AddEntry(Entry,Client) + self:T(self.lid.."AddEntry") + local Set = self.clientset.Set + if Client then + Set = {Client} + end + for _,_client in pairs(Set) do + local client = _client -- Wrapper.Client#CLIENT + if client and client:IsAlive() then + local playername = client:GetPlayerName() + local entry = Entry -- #CLIENTMENU + local parent = nil + if entry.Parent and entry.Parent.name then + parent = self:_GetParentEntry(self.structure[playername],entry.Parent.name) + end + self.structure[playername][Entry.ID] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) + self.structure[playername][Entry.ID].Once = entry.Once + end + end + return self +end + +--- Blank out the menu - remove **all root entries** and all entries below from the client's menus, leaving the generic structure untouched. +-- @param #CLIENTMENUMANAGER self +-- @param Wrapper.Client#CLIENT Client +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:ResetMenu(Client) + self:T(self.lid.."ResetMenu") + for _,_entry in pairs(self.rootentries) do + local RootEntry = self.structure.generic[_entry] + if RootEntry then + self:Clear(RootEntry,Client) + end + end + return self +end + +--- Blank out the menu - remove **all root entries** and all entries below from all clients' menus, and **delete** the generic structure. +-- @param #CLIENTMENUMANAGER self +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:ResetMenuComplete() + self:T(self.lid.."ResetMenuComplete") + for _,_entry in pairs(self.rootentries) do + local RootEntry = self.structure.generic[_entry] + if RootEntry then + self:Clear(RootEntry) + end + end + self.structure = nil + self.structure = { + generic = {}, + IDs = {}, + } + self.rootentries = nil + self.rootentries = {} + return self +end + +--- Remove the entry and all entries below the given entry from the client's menus and the generic structure. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The entry to remove +-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:Clear(Entry,Client) + self:T(self.lid.."Clear") + local rid = self.structure.IDs[Entry.ID] + if rid then + local generic = self.structure.generic[rid] + local Set = self.clientset.Set + if Client then + Set = {Client} + end + for _,_client in pairs(Set) do + local client = _client -- Wrapper.Client#CLIENT + if client and client:IsAlive() then + local playername = client:GetPlayerName() + local entry = self.structure[playername][rid] -- #CLIENTMENU + if entry then + entry:Clear() + self.structure[playername][rid] = nil + end + end + end + if not Client then + for _id,_entry in pairs(self.structure.generic) do + local entry = _entry -- #CLIENTMENU + if entry and entry.Parent and entry.Parent.ID and entry.Parent.ID == rid then + self.structure.IDs[entry.ID] = nil + entry = nil + end + end + end + end + return self +end + +--- [Internal] Clean up player shadow structure +-- @param #CLIENTMENUMANAGER self +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:_CleanUpPlayerStructure() + self:T(self.lid.."_CleanUpPlayerStructure") + for _,_client in pairs(self.clientset.Set) do + local client = _client -- Wrapper.Client#CLIENT + if client and client:IsAlive() then + local playername = client:GetPlayerName() + local newstructure = {} + for _id, _entry in UTILS.spairs(self.structure[playername]) do + if self.structure.generic[_id] then + newstructure[_id] = _entry + end + end + self.structure[playername] = nil + self.structure[playername] = newstructure + end + end + return self +end + +--- Remove all entries below the given entry from the clients' menus and the generic structure. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The menu entry +-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:RemoveSubEntries(Entry,Client) + self:T(self.lid.."RemoveSubEntries") + local rid = self.structure.IDs[Entry.ID] + if rid then + local Set = self.clientset.Set + if Client then + Set = {Client} + end + for _,_client in pairs(Set) do + local client = _client -- Wrapper.Client#CLIENT + if client and client:IsAlive() then + local playername = client:GetPlayerName() + local entry = self.structure[playername][rid] -- #CLIENTMENU + if entry then + entry:RemoveSubEntries() + end + end + end + if not Client then + for _id,_entry in pairs(self.structure.generic) do + local entry = _entry -- #CLIENTMENU + if entry and entry.Parent and entry.Parent.ID and entry.Parent.ID == rid then + self.structure.IDs[entry.ID] = nil + self.structure.generic[_id] = nil + end + end + end + end + self:_CleanUpPlayerStructure() + return self +end + +--- Remove a specific entry by ID from the generic structure +-- @param #CLIENTMENUMANAGER self +-- @param #number ID +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:_RemoveByID(ID) + self:T(self.lid.."_RemoveByID "..ID or "none") + if ID then + local gid = self.structure.IDs[ID] + if gid then + self.structure.generic[gid] = nil + self.structure.IDs[ID] = nil + end + end + return self +end + +--- [Internal] Dump structures to log for debug +-- @param #CLIENTMENUMANAGER self +-- @param #string Playername +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:_CheckStructures(Playername) + self:T(self.lid.."CheckStructures") + self:I("Generic Structure") + self:I("-----------------") + for _id,_entry in UTILS.spairs(self.structure.generic) do + local ID = "none" + if _entry and _entry.ID then + ID = _entry.ID + end + self:I("ID= ".._id.." | EntryID = "..ID) + if _id > 10 and _id < 14 then + self:I(_entry.name) + end + end + self:I("Reverse Structure") + self:I("-----------------") + for _id,_entry in UTILS.spairs(self.structure.IDs) do + self:I("EntryID= ".._id.." | ID = ".._entry) + end + if Playername then + self:I("Player Structure") + self:I("-----------------") + for _id,_entry in UTILS.spairs(self.structure[Playername]) do + local ID = "none" + if _entry and _entry.ID then + ID = _entry.ID + end + local _lid = _id or "none" + self:I("ID= ".._lid.." | EntryID = "..ID) + end + end + return self +end + +---------------------------------------------------------------------------------------------------------------- +-- +-- End ClientMenu +-- +---------------------------------------------------------------------------------------------------------------- + diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 1d0f1b1d9..39acc42f1 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -166,6 +166,7 @@ function MENU_INDEX:Refresh( Group ) end do -- MENU_BASE + --- -- @type MENU_BASE -- @extends Core.Base#BASE --- Defines the main MENU class where other MENU classes are derived from. diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index 48bb2cb84..0b0ad7f77 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -33,6 +33,7 @@ do + --- -- @type SPOT -- @extends Core.Fsm#FSM @@ -256,6 +257,8 @@ do self:HandleEvent( EVENTS.Dead ) self:__Lasing( -1 ) + + return self end @@ -290,8 +293,10 @@ do end self:__Lasing(-1) + return self end - + + --- -- @param #SPOT self -- @param Core.Event#EVENTDATA EventData function SPOT:OnEventDead(EventData) @@ -309,8 +314,10 @@ do self:LaseOff() end end + return self end + --- -- @param #SPOT self -- @param From -- @param Event @@ -318,9 +325,11 @@ do function SPOT:onafterLasing( From, Event, To ) if self.Target and self.Target:IsAlive() then + self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() ) self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() ) - self:__Lasing( -0.2 ) + + self:__Lasing( -0.3 ) elseif self.TargetCoord then -- Wiggle the IR spot a bit. @@ -330,13 +339,14 @@ do self.SpotIR:setPoint(irvec3) self.SpotLaser:setPoint(lsvec3) - self:__Lasing(-0.25) + self:__Lasing(-0.3) else self:F( { "Target is not alive", self.Target:IsAlive() } ) end - + return self end - + + --- -- @param #SPOT self -- @param From -- @param Event @@ -359,7 +369,11 @@ do end self.ScheduleID = nil - self.Target = nil + if self.Target and self.Target:IsAlive() then + self:I("Target still alive.") + else + self.Target = nil + end return self end diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index ad37bf77e..ee7d82b8b 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -34,6 +34,7 @@ __Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' ) __Moose.Include( 'Scripts/Moose/Core/Velocity.lua' ) __Moose.Include( 'Scripts/Moose/Core/Zone_Detection.lua' ) __Moose.Include( 'Scripts/Moose/Core/Zone.lua' ) +__Moose.Include( 'Scripts/Moose/Core/ClientMenu.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Client.lua' ) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index a16bfc378..a52a4a74a 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -34,6 +34,7 @@ __Moose.Include( 'Core\\Astar.lua' ) __Moose.Include( 'Core\\MarkerOps_Base.lua' ) __Moose.Include( 'Core\\TextAndSound.lua' ) __Moose.Include( 'Core\\Condition.lua' ) +__Moose.Include( 'Core\\ClientMenu.lua' ) __Moose.Include( 'Wrapper\\Object.lua' ) __Moose.Include( 'Wrapper\\Identifiable.lua' ) diff --git a/Moose Development/Moose/Utilities/Routines.lua b/Moose Development/Moose/Utilities/Routines.lua index e774a290f..8545a1dd7 100644 --- a/Moose Development/Moose/Utilities/Routines.lua +++ b/Moose Development/Moose/Utilities/Routines.lua @@ -54,7 +54,7 @@ routines.utils.oneLineSerialize = function( tbl ) -- serialization of a table al if type( tbl ) == 'table' then -- function only works for tables! if lookup_table[tbl] then - return lookup_table[tbl] + return lookup_table[object] end local tbl_str = {} diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index d4e08f26c..74307d9eb 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -299,7 +299,7 @@ end -- @param #table tbl Input table. UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - local lookup_table = {} + lookup_table = {} local function _Serialize( tbl ) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 276ec5b8d..b820e28ec 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -11,7 +11,7 @@ -- @module Wrapper.Airbase -- @image Wrapper_Airbase.JPG - +--- -- @type AIRBASE -- @field #string ClassName Name of the class, i.e. "AIRBASE". -- @field #table CategoryName Names of airbase categories. @@ -787,7 +787,13 @@ function AIRBASE:Register(AirbaseName) -- Category. self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME - + + -- H2 is bugged + --if self.AirbaseName == "H4" and self.descriptors == nil then + --self:E("***** H4 on Syria map is currently bugged!") + --return nil + --end + -- Set category. if self.category==Airbase.Category.AIRDROME then self.isAirdrome=true diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 6caa7f179..9691e1e4e 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -31,6 +31,7 @@ Core/Spot.lua Core/MarkerOps_Base.lua Core/Astar.lua Core/Condition.lua +Core/ClientMenu Wrapper/Object.lua Wrapper/Identifiable.lua From 45adc6160d8eff7aeb4f78010706111d8e179ed7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 11 Jul 2023 16:10:08 +0200 Subject: [PATCH 300/603] #CLIENTMENUMANAGER Docu --- Moose Development/Moose/Core/ClientMenu.lua | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 3f43642e4..61e37bb73 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -15,6 +15,7 @@ -- ### Author: **applevangelist** -- -- === +-- -- @module Core.ClientMenu -- @image Core_Menu.JPG -- last change: July 2023 @@ -247,7 +248,8 @@ end -- ---------------------------------------------------------------------------------------------------------------- ----CLIENTMENUMANAGER class + +--- Class CLIENTMENUMANAGER -- @type CLIENTMENUMANAGER -- @field #string ClassName Class Name -- @field #string lid Lid for log entries @@ -265,7 +267,7 @@ end -- -- === -- --- # CLIENTMENU and CLIENTMENUMANAGER +-- ## CLIENTMENU and CLIENTMENUMANAGER -- -- Manage menu structures for a SET_CLIENT of clients. -- @@ -338,13 +340,8 @@ end -- -- ## Reset all and clear the reference tree -- --- menumgr:ResetMenuComplete() ---- --- @type CLIENTMENUMANAGER.Structure --- @field #table generic --- @field #table IDs - ---- +-- menumgr:ResetMenuComplete() +-- -- @field #CLIENTMENUMANAGER CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", @@ -369,6 +366,10 @@ CLIENTMENUMANAGER = { debug = true, } +--- +-- @type CLIENTMENUMANAGER.Structure +-- @field #table generic +-- @field #table IDs --- Create a new ClientManager instance. -- @param #CLIENTMENUMANAGER self From 6c5cf814b4d119e88703addcbf9387595e3694a6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 12 Jul 2023 17:54:35 +0200 Subject: [PATCH 301/603] #Spot --- Moose Development/Moose/Core/Spot.lua | 55 ++++++++++++++------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index 0b0ad7f77..136146b41 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -229,7 +229,8 @@ do -- @param #number LaserCode Laser code. -- @param #number Duration Duration of lasing in seconds. function SPOT:onafterLaseOn( From, Event, To, Target, LaserCode, Duration ) - self:F( { "LaseOn", Target, LaserCode, Duration } ) + self:T({From, Event, To}) + self:T2( { "LaseOn", Target, LaserCode, Duration } ) local function StopLase( self ) self:LaseOff() @@ -271,7 +272,7 @@ do -- @param #number LaserCode Laser code. -- @param #number Duration Duration of lasing in seconds. function SPOT:onafterLaseOnCoordinate(From, Event, To, Coordinate, LaserCode, Duration) - self:F( { "LaseOnCoordinate", Coordinate, LaserCode, Duration } ) + self:T2( { "LaseOnCoordinate", Coordinate, LaserCode, Duration } ) local function StopLase( self ) self:LaseOff() @@ -300,7 +301,7 @@ do -- @param #SPOT self -- @param Core.Event#EVENTDATA EventData function SPOT:OnEventDead(EventData) - self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } ) + self:T2( { Dead = EventData.IniDCSUnitName, Target = self.Target } ) if self.Target then if EventData.IniDCSUnitName == self.TargetName then self:F( {"Target dead ", self.TargetName } ) @@ -323,25 +324,28 @@ do -- @param Event -- @param To function SPOT:onafterLasing( From, Event, To ) - - if self.Target and self.Target:IsAlive() then - - self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() ) - self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() ) - - self:__Lasing( -0.3 ) - elseif self.TargetCoord then + self:T({From, Event, To}) - -- Wiggle the IR spot a bit. - local irvec3={x=self.TargetCoord.x+math.random(-100,100)/100, y=self.TargetCoord.y+math.random(-100,100)/100, z=self.TargetCoord.z} --#DCS.Vec3 - local lsvec3={x=self.TargetCoord.x, y=self.TargetCoord.y, z=self.TargetCoord.z} --#DCS.Vec3 + if self.Lasing then + if self.Target and self.Target:IsAlive() then + + self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() ) + self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() ) + + self:__Lasing(0.2) + elseif self.TargetCoord then - self.SpotIR:setPoint(irvec3) - self.SpotLaser:setPoint(lsvec3) - - self:__Lasing(-0.3) - else - self:F( { "Target is not alive", self.Target:IsAlive() } ) + -- Wiggle the IR spot a bit. + local irvec3={x=self.TargetCoord.x+math.random(-100,100)/100, y=self.TargetCoord.y+math.random(-100,100)/100, z=self.TargetCoord.z} --#DCS.Vec3 + local lsvec3={x=self.TargetCoord.x, y=self.TargetCoord.y, z=self.TargetCoord.z} --#DCS.Vec3 + + self.SpotIR:setPoint(irvec3) + self.SpotLaser:setPoint(lsvec3) + + self:__Lasing(0.2) + else + self:F( { "Target is not alive", self.Target:IsAlive() } ) + end end return self end @@ -353,8 +357,9 @@ do -- @param To -- @return #SPOT function SPOT:onafterLaseOff( From, Event, To ) - - self:F( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } ) + self:t({From, Event, To}) + + self:T2( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } ) self.Lasing = false @@ -369,11 +374,7 @@ do end self.ScheduleID = nil - if self.Target and self.Target:IsAlive() then - self:I("Target still alive.") - else - self.Target = nil - end + self.Target = nil return self end From eba9c697e395e0872886833caf953288f1d1e24f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 13 Jul 2023 16:06:23 +0200 Subject: [PATCH 302/603] #SPAWN --- Moose Development/Moose/Core/Spawn.lua | 85 +++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 0c3958929..ee6f34ebe 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1047,7 +1047,7 @@ end --- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types. -- @param #SPAWN self -- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Core.Zone}s objects. --- @return #SPAWN +-- @return #SPAWN self -- @usage -- -- -- Create a zone table of the 2 zones. @@ -1077,6 +1077,31 @@ function SPAWN:InitRandomizeZones( SpawnZoneTable ) return self end +--- This method sets a spawn position for the group that is different from the location of the template. +-- @param #SPAWN self +-- @param Core.Point#COORDINATE Coordinate The position to spawn from +-- @return #SPAWN self +function SPAWN:InitPositionCoordinate(Coordinate) + self:T( { self.SpawnTemplatePrefix, Coordinate:GetVec2()} ) + self:InitPositionVec2(Coordinate:GetVec2()) + return self +end + +--- This method sets a spawn position for the group that is different from the location of the template. +-- @param #SPAWN self +-- @param DCS#Vec2 Vec2 The position to spawn from +-- @return #SPAWN self +function SPAWN:InitPositionVec2(Vec2) + self:T( { self.SpawnTemplatePrefix, Vec2} ) + self.SpawnInitPosition = Vec2 + self.SpawnFromNewPosition = true + self:I("MaxGroups:"..self.SpawnMaxGroups) + for SpawnGroupID = 1, self.SpawnMaxGroups do + self:_SetInitialPosition( SpawnGroupID ) + end + return self +end + --- For planes and helicopters, when these groups go home and land on their home airbases and FARPs, they normally would taxi to the parking spot, shut-down their engines and wait forever until the Group is removed by the runtime environment. -- This method is used to re-spawn automatically (so no extra call is needed anymore) the same group after it has landed. -- This will enable a spawned group to be re-spawned after it lands, until it is destroyed... @@ -1376,7 +1401,12 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth ) self:F2( { SpawnTemplatePrefix = self.SpawnTemplatePrefix, SpawnIndex = SpawnIndex, AliveUnits = self.AliveUnits, SpawnMaxGroups = self.SpawnMaxGroups } ) if self:_GetSpawnIndex( SpawnIndex ) then - + + if self.SpawnFromNewPosition then + self:_SetInitialPosition( SpawnIndex ) + end + + if self.SpawnGroups[self.SpawnIndex].Visible then self.SpawnGroups[self.SpawnIndex].Group:Activate() else @@ -3300,6 +3330,57 @@ function SPAWN:_RandomizeTemplate( SpawnIndex ) return self end +--- Private method that sets the DCS#Vec2 where the Group will be spawned. +-- @param #SPAWN self +-- @param #number SpawnIndex +-- @return #SPAWN self +function SPAWN:_SetInitialPosition( SpawnIndex ) + self:T( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeZones } ) + + if self.SpawnFromNewPosition then + + self:T( "Preparing Spawn at Vec2 ", self.SpawnInitPosition ) + + local SpawnVec2 = self.SpawnInitPosition + + self:T( { SpawnVec2 = SpawnVec2 } ) + + local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate + + SpawnTemplate.route = SpawnTemplate.route or {} + SpawnTemplate.route.points = SpawnTemplate.route.points or {} + SpawnTemplate.route.points[1] = SpawnTemplate.route.points[1] or {} + SpawnTemplate.route.points[1].x = SpawnTemplate.route.points[1].x or 0 + SpawnTemplate.route.points[1].y = SpawnTemplate.route.points[1].y or 0 + + self:T( { Route = SpawnTemplate.route } ) + + for UnitID = 1, #SpawnTemplate.units do + local UnitTemplate = SpawnTemplate.units[UnitID] + self:T( 'Before Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. UnitTemplate.y ) + local SX = UnitTemplate.x + local SY = UnitTemplate.y + local BX = SpawnTemplate.route.points[1].x + local BY = SpawnTemplate.route.points[1].y + local TX = SpawnVec2.x + (SX - BX) + local TY = SpawnVec2.y + (SY - BY) + UnitTemplate.x = TX + UnitTemplate.y = TY + -- TODO: Manage altitude based on landheight... + -- SpawnTemplate.units[UnitID].alt = SpawnVec2: + self:T( 'After Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. UnitTemplate.y ) + end + + SpawnTemplate.route.points[1].x = SpawnVec2.x + SpawnTemplate.route.points[1].y = SpawnVec2.y + SpawnTemplate.x = SpawnVec2.x + SpawnTemplate.y = SpawnVec2.y + + end + + return self +end + --- Private method that randomizes the @{Core.Zone}s where the Group will be spawned. -- @param #SPAWN self -- @param #number SpawnIndex From 77955bc03cac135caa0da021a687a6ab83505b90 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 15 Jul 2023 18:02:12 +0200 Subject: [PATCH 303/603] #changes --- Moose Development/Moose/Core/ClientMenu.lua | 662 +++++++------------- Moose Development/Moose/Ops/PlayerTask.lua | 221 ++++++- 2 files changed, 437 insertions(+), 446 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 61e37bb73..c8d9c3833 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -37,9 +37,10 @@ -- @field #table parentpath -- @field #CLIENTMENU Parent -- @field Wrapper.Client#CLIENT client --- @field #number GID --- @field #number ID +-- @field #number GroupID Group ID +-- @field #number ID Entry ID -- @field Wrapper.Group#GROUP group +-- @field #string UUID Unique ID based on path+name -- @field #string Function -- @field #table Functionargs -- @field #table Children @@ -54,12 +55,12 @@ CLIENTMENU = { ClassName = "CLIENTMENUE", lid = "", - version = "0.0.1", + version = "0.0.2", name = nil, path = nil, group = nil, client = nil, - GID = nil, + GroupID = nil, Children = {}, Once = false, Generic = false, @@ -87,7 +88,7 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) if Client then self.group = Client:GetGroup() self.client = Client - self.GID = self.group:GetID() + self.GroupID = self.group:GetID() else self.Generic = true end @@ -98,7 +99,9 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) end self.Parent = Parent self.Function = Function - self.Functionargs = arg + self.Functionargs = arg or {} + table.insert(self.Functionargs,self.group) + table.insert(self.Functionargs,self.client) if self.Functionargs and self.debug then self:I({"Functionargs",self.Functionargs}) end @@ -120,9 +123,9 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) self:Clear() end end - self.path = missionCommands.addCommandForGroup(self.GID,Text,self.parentpath, self.CallHandler) + self.path = missionCommands.addCommandForGroup(self.GroupID,Text,self.parentpath, self.CallHandler) else - self.path = missionCommands.addSubMenuForGroup(self.GID,Text,self.parentpath) + self.path = missionCommands.addSubMenuForGroup(self.GroupID,Text,self.parentpath) end else if self.parentpath then @@ -131,8 +134,9 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) self.path = {} end self.path[#self.path+1] = Text - self:T({self.path}) end + self.UUID = table.concat(self.path,";") + self:I({self.UUID}) self.Once = false -- Log id. self.lid=string.format("CLIENTMENU %s | %s | ", self.ID, self.name) @@ -162,9 +166,10 @@ end -- @param #CLIENTMENU self -- @return #CLIENTMENU self function CLIENTMENU:RemoveF10() - self:T(self.lid.."RemoveF10") + self:I(self.lid.."RemoveF10") if not self.Generic then - missionCommands.removeItemForGroup(self.GID , self.path ) + self:I(self.lid.."Removing") + missionCommands.removeItemForGroup(self.GroupID , self.path ) end return self end @@ -177,6 +182,14 @@ function CLIENTMENU:GetPath() return self.path end +--- Get the UUID. +-- @param #CLIENTMENU self +-- @return #string UUID +function CLIENTMENU:GetUUID() + self:T(self.lid.."GetUUID") + return self.UUID +end + --- Link a child entry. -- @param #CLIENTMENU self -- @param #CLIENTMENU Child The entry to link as a child. @@ -201,20 +214,20 @@ end -- @param #CLIENTMENU self -- @return #CLIENTMENU self function CLIENTMENU:RemoveSubEntries() - self:T(self.lid.."RemoveSubEntries") - --self:T({self.Children}) + self:I(self.lid.."RemoveSubEntries") + self:I({self.Children}) for _id,_entry in pairs(self.Children) do - self:T("Removing ".._id) + self:I("Removing ".._id) if _entry then _entry:RemoveSubEntries() _entry:RemoveF10() if _entry.Parent then _entry.Parent:RemoveChild(self) end - if self.Controller then - self.Controller:_RemoveByID(_entry.ID) - end - _entry = nil + --if self.Controller then + --self.Controller:_RemoveByID(_entry.ID) + --end + --_entry = nil end end return self @@ -235,9 +248,9 @@ function CLIENTMENU:Clear() if self.Parent then self.Parent:RemoveChild(self) end - if self.Controller then - self.Controller:_RemoveByID(self.ID) - end + --if self.Controller then + --self.Controller:_RemoveByID(self.ID) + --end return self end @@ -256,9 +269,9 @@ end -- @field #string version Version string -- @field #string name Name -- @field Core.Set#SET_CLIENT clientset The set of clients this menu manager is for --- @field #table structure --- @field #table replacementstructure +-- @field #table flattree -- @field #table rootentries +-- @field #table menutree -- @field #number entrycount -- @field #boolean debug -- @extends Core.Base#BASE @@ -310,27 +323,14 @@ end -- -- ## Remove a single entry and also it's subtree -- --- menumgr:Clear(mymenu_lv3a) +-- menumgr:DeleteEntry(mymenu_lv3a) -- -- ## Add a single entry -- -- local baimenu = menumgr:NewEntry("BAI",mymenu_lv1b) -- menumgr:AddEntry(baimenu) -- --- ## Prepare and push a partial replacement in the tree --- --- menumgr:PrepareNewReplacementStructure() --- local submenu = menumgr:NewReplacementEntry("New Level 2 ba",mymenu_lv2a) --- menumgr:NewReplacementEntry("New Level 2 bb",mymenu_lv2a) --- menumgr:NewReplacementEntry("Deleted",mymenu_lv2a) --- menumgr:NewReplacementEntry("New Level 2 bd",mymenu_lv2a) --- menumgr:NewReplacementEntry("SubLevel 3 baa",submenu) --- menumgr:NewReplacementEntry("SubLevel 3 bab",submenu) --- menumgr:NewReplacementEntry("SubLevel 3 bac",submenu) --- menumgr:NewReplacementEntry("SubLevel 3 bad",submenu) --- menumgr:ReplaceEntries(mymenu_lv2a) --- --- ## Change the text of an entry in the menu tree +-- ## Change the text of a leaf entry in the menu tree -- -- menumgr:ChangeEntryTextForAll(mymenu_lv1b,"Attack") -- @@ -346,31 +346,17 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.0.1", + version = "0.0.4", name = nil, clientset = nil, - --- - -- @field #CLIENTMENUMANAGER.Structure - structure = { - generic = {}, - IDs = {}, - }, - --- - -- #CLIENTMENUMANAGER.ReplacementStructure - replacementstructure = { - generic = {}, - IDs = {}, - }, + menutree = {}, + flattree = {}, + playertree = {}, entrycount = 0, rootentries = {}, debug = true, } ---- --- @type CLIENTMENUMANAGER.Structure --- @field #table generic --- @field #table IDs - --- Create a new ClientManager instance. -- @param #CLIENTMENUMANAGER self -- @param Core.Set#SET_CLIENT ClientSet The set of clients to manage. @@ -400,271 +386,119 @@ function CLIENTMENUMANAGER:NewEntry(Text,Parent,Function,...) self:T(self.lid.."NewEntry "..Text or "None") self.entrycount = self.entrycount + 1 local entry = CLIENTMENU:NewEntry(nil,Text,Parent,Function,unpack(arg)) - self.structure.generic[self.entrycount] = entry - self.structure.IDs[entry.ID] = self.entrycount if not Parent then - self.rootentries[self.entrycount] = self.entrycount + self.rootentries[self.entrycount] = entry end + local depth = #entry.path + if not self.menutree[depth] then self.menutree[depth] = {} end + table.insert(self.menutree[depth],entry.UUID) + self.flattree[entry.UUID] = entry return entry end ---- Find **first** matching entry in the generic structure by the menu text. +--- Find matching entry in the generic structure by UUID. -- @param #CLIENTMENUMANAGER self --- @param #string Text Text of the F10 menu entry. +-- @param #string UUID UUID of the menu entry. -- @return #CLIENTMENU Entry The #CLIENTMENU object found or nil. --- @return #number GID GID The GID found or nil. -function CLIENTMENUMANAGER:FindEntryByText(Text) - self:T(self.lid.."FindEntryByText "..Text or "None") +function CLIENTMENUMANAGER:FindEntryByUUID(UUID) + self:I(self.lid.."FindEntryByUUID "..UUID or "None") local entry = nil - local gid = nil - for _gid,_entry in UTILS.spairs(self.structure.generic) do + for _gid,_entry in pairs(self.flattree) do local Entry = _entry -- #CLIENTMENU - if Entry and Entry.name == Text then + if Entry and Entry.UUID == UUID then entry = Entry - gid = _gid end end - return entry, gid -end - ---- Find first matching entry in the generic structure by the GID. --- @param #CLIENTMENUMANAGER self --- @param #number GID The GID of the entry to find. --- @return #CLIENTMENU Entry The #CLIENTMENU object found or nil. -function CLIENTMENUMANAGER:GetEntryByGID(GID) - self:T(self.lid.."GetEntryByGID "..GID or "None") - if GID and type(GID) == "number" then - return self.structure.generic[GID] - else - return nil - end -end - ---- Alter the text of an entry in the generic structure and push to all clients. --- @param #CLIENTMENUMANAGER self --- @param #CLIENTMENU Entry The menu entry. --- @param #string Text Text of the F10 menu entry. --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:ChangeEntryTextForAll(Entry,Text) - self:T(self.lid.."ChangeEntryTextForAll "..Text or "None") - for _,_client in pairs(self.clientset.Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - self:ChangeEntryText(Entry,Text, client) - end - end - return self -end - ---- Alter the text of an entry in the generic structure and push to one specific client. --- @param #CLIENTMENUMANAGER self --- @param #CLIENTMENU Entry The menu entry. --- @param #string Text Text of the F10 menu entry. --- @param Wrapper.Client#CLIENT Client The client for whom to alter the entry --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:ChangeEntryText(Entry,Text, Client) - self:T(self.lid.."ChangeEntryText "..Text or "None") - - local text = Text or "none" - local oldtext = Entry.name - Entry.name = text - - local newstructure = {} - local changed = 0 - - local function ChangePath(path,oldtext,newtext) - local newpath = {} - for _id,_text in UTILS.spairs(path) do - local txt = _text - if _text == oldtext then - txt = newtext - end - newpath[_id] = txt - end - return newpath - end - - local function AlterPath(children) - for _,_entry in pairs(children) do - local entry = _entry -- #CLIENTMENU - local newpath = ChangePath(entry.path,oldtext,text) - local newparentpath = ChangePath(entry.parentpath,oldtext,text) - entry.path = nil - entry.parentpath = nil - entry.path = newpath - entry.parentpath = newparentpath - self:T({entry.ID}) - --self:T({entry.parentpath}) - newstructure[entry.ID] = UTILS.DeepCopy(entry) - changed = changed + 1 - if entry.Children and #entry.Children > 0 then - AlterPath(entry.Children) - end - end - end - - -- get the entry - local ID = Entry.ID - local GID = self.structure.IDs[ID] - local playername = Client:GetPlayerName() - local children = self.structure[playername][GID].Children - AlterPath(children) - - self:T("Changed entries: "..changed) - - local NewParent = self:NewEntry(Entry.name,Entry.Parent,Entry.Function,unpack(Entry.Functionargs)) - - for _,_entry in pairs(children) do - self:T("Changed parent for ".._entry.ID.." | GID ".._entry.GID) - local entry = _entry -- #CLIENTMENU - entry.Parent = NewParent - end - - self:PrepareNewReplacementStructure() - - for _,_entry in pairs(newstructure) do - self:T("Changed entry: ".._entry.ID.." | GID ".._entry.GID) - local entry = _entry -- #CLIENTMENU - self:NewReplacementEntry(entry.name,entry.Parent,entry.Function,unpack(entry.Functionargs)) - end - - - self:AddEntry(NewParent) - self:ReplaceEntries(NewParent) - - self:Clear(Entry) - - return self -end - ---- Create a new entry in the replacement structure. --- @param #CLIENTMENUMANAGER self --- @param #string Text Text of the F10 menu entry. --- @param #CLIENTMENU Parent The parent menu entry. --- @param #string Function (optional) Function to call when the entry is used. --- @param ... (optional) Arguments for the Function, comma separated --- @return #CLIENTMENU Entry -function CLIENTMENUMANAGER:NewReplacementEntry(Text,Parent,Function,...) - self:T(self.lid.."NewReplacementEntry "..Text or "None") - self.entrycount = self.entrycount + 1 - local entry = CLIENTMENU:NewEntry(nil,Text,Parent,Function,unpack(arg)) - self.replacementstructure.generic[self.entrycount] = entry - self.replacementstructure.IDs[entry.ID] = self.entrycount - local pID = Parent and Parent.ID or "none" - if self.debug then - self:I("Entry ID = "..self.entrycount.." | Parent ID = "..tostring(pID)) - end - if not Parent then - self.rootentries[self.entrycount] = self.entrycount - end return entry end ---- Prepare a new replacement structure. Deletes the previous one. +--- Find matching entry by text in the generic structure by UUID. -- @param #CLIENTMENUMANAGER self --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:PrepareNewReplacementStructure() - self:T(self.lid.."PrepareNewReplacementStructure") - self.replacementstructure = nil -- #CLIENTMENUMANAGER.Structure - self.replacementstructure = { - generic = {}, - IDs = {}, - } - return self -end - ---- [Internal] Merge the replacement structure into the generic structure. --- @param #CLIENTMENUMANAGER self --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:_MergeReplacementData() - self:T(self.lid.."_MergeReplacementData") - for _id,_entry in pairs(self.replacementstructure.generic) do - self.structure.generic[_id] = _entry - end - for _id,_entry in pairs(self.replacementstructure.IDs) do - self.structure.IDs[_id] = _entry - end - self:_CleanUpPlayerStructure() - return self -end - ---- Replace entries under the Parent entry with the Replacement structure created prior for all clients. --- @param #CLIENTMENUMANAGER self --- @param #CLIENTMENU Parent The parent entry under which to replace with the new structure. --- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:ReplaceEntries(Parent,Client) - self:T(self.lid.."ReplaceEntries") - -- clear Parent substructure - local Set = self.clientset.Set - if Client then - Set = {Client} - else - self:RemoveSubEntries(Parent) - end - for _,_client in pairs(Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - local playername = client:GetPlayerName() - --self.structure[playername] = {} - for _id,_entry in UTILS.spairs(self.replacementstructure.generic) do - local entry = _entry -- #CLIENTMENU - local parent = Parent - self:T("Posted Parent = "..Parent.ID) - if entry.Parent and entry.Parent.name then - parent = self:_GetParentEntry(self.replacementstructure.generic,entry.Parent.name) or Parent - self:T("Found Parent = "..parent.ID) - end - self.structure[playername][_id] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) - self.structure[playername][_id].Once = entry.Once - end +-- @param #string Text Text or partial text of the menu entry to find +-- @return #table Table of matching UUIDs of #CLIENTMENU objects +-- @return #table Table of matching #CLIENTMENU objects +function CLIENTMENUMANAGER:FindUUIDsByText(Text) + self:I(self.lid.."FindUUIDsByText "..Text or "None") + local matches = {} + local entries = {} + for _uuid,_entry in pairs(self.flattree) do + local Entry = _entry -- #CLIENTMENU + if Entry and string.find(Entry.name,Text) then + table.insert(matches,_uuid) + table.insert(entries,Entry ) end end - self:_MergeReplacementData() + return matches, entries +end + +--- Find matching entries in the generic structure by the menu text. +-- @param #CLIENTMENUMANAGER self +-- @param #string Text Text or partial text of the F10 menu entry. +-- @return #table Table of matching #CLIENTMENU objects. +function CLIENTMENUMANAGER:FindEntriesByText(Text) + self:I(self.lid.."FindEntriesByText "..Text or "None") + local matches, objects = self:FindUUIDsByText(Text) + return objects +end + +--- Alter the text of a leaf entry in the generic structure and push to one specific client's F10 menu. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The menu entry. +-- @param #string Text New Text of the F10 menu entry. +-- @param Wrapper.Client#CLIENT Client (optional) The client for whom to alter the entry, if nil done for all clients. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:ChangeEntryText(Entry, Text, Client) + self:T(self.lid.."ChangeEntryText "..Text or "None") + local newentry = CLIENTMENU:NewEntry(nil,Text,Entry.Parent,Entry.Function,unpack(Entry.Functionargs)) + self:DeleteF10Entry(Entry,Client) + self:DeleteGenericEntry(Entry) + if not Entry.Parent then + self.rootentries[self.entrycount] = newentry + end + local depth = #newentry.path + if not self.menutree[depth] then self.menutree[depth] = {} end + table.insert(self.menutree[depth],newentry.UUID) + self.flattree[newentry.UUID] = newentry + self:AddEntry(newentry,Client) return self end ---- [Internal] Find a parent entry in a given structure by name. --- @param #CLIENTMENUMANAGER self --- @param #table Structure Table of entries. --- @param #string Name Name to find. --- @return #CLIENTMENU Entry -function CLIENTMENUMANAGER:_GetParentEntry(Structure,Name) - self:T(self.lid.."_GetParentEntry") - local found = nil - for _,_entry in pairs(Structure) do - local entry = _entry -- #CLIENTMENU - if entry.name == Name then - found = entry - break - end - end - return found -end - ---- Push the complete menu structure to each of the clients in the set. +--- Push the complete menu structure to each of the clients in the set - refresh the menu tree of the clients. -- @param #CLIENTMENUMANAGER self -- @param Wrapper.Client#CLIENT Client (optional) If given, propagate only for this client. -- @return #CLIENTMENU Entry function CLIENTMENUMANAGER:Propagate(Client) - self:T(self.lid.."Propagate") + self:I(self.lid.."Propagate") + self:I(Client) local Set = self.clientset.Set if Client then - Set = {Set} + Set = {Client} end + self:ResetMenu(Client) for _,_client in pairs(Set) do local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then local playername = client:GetPlayerName() - self.structure[playername] = {} - for _id,_entry in pairs(self.structure.generic) do - local entry = _entry -- #CLIENTMENU - local parent = nil - if entry.Parent and entry.Parent.name then - parent = self:_GetParentEntry(self.structure[playername],entry.Parent.name) + if not self.playertree[playername] then + self.playertree[playername] = {} + end + for level,branch in pairs (self.menutree) do + self:I("Building branch:" .. level) + for _,leaf in pairs(branch) do + self:I("Building leaf:" .. leaf) + local entry = self:FindEntryByUUID(leaf) + if entry then + self:I("Found generic entry:" .. entry.UUID) + local parent = nil + if entry.Parent and entry.Parent.UUID then + parent = self.playertree[playername][entry.Parent.UUID] or self:FindEntryByUUID(entry.Parent.UUID) + end + self.playertree[playername][entry.UUID] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) + self.playertree[playername][entry.UUID].Once = entry.Once + else + self:I("NO generic entry for:" .. leaf) + end end - self.structure[playername][_id] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) - self.structure[playername][_id].Once = entry.Once end end end @@ -686,85 +520,75 @@ function CLIENTMENUMANAGER:AddEntry(Entry,Client) local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then local playername = client:GetPlayerName() - local entry = Entry -- #CLIENTMENU - local parent = nil - if entry.Parent and entry.Parent.name then - parent = self:_GetParentEntry(self.structure[playername],entry.Parent.name) - end - self.structure[playername][Entry.ID] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) - self.structure[playername][Entry.ID].Once = entry.Once + if Entry then + self:I("Adding generic entry:" .. Entry.UUID) + local parent = nil + if Entry.Parent and Entry.Parent.UUID then + parent = self.playertree[playername][Entry.Parent.UUID] or self:FindEntryByUUID(Entry.Parent.UUID) + end + self.playertree[playername][Entry.UUID] = CLIENTMENU:NewEntry(client,Entry.name,parent,Entry.Function,unpack(Entry.Functionargs)) + self.playertree[playername][Entry.UUID].Once = Entry.Once + else + self:I("NO generic entry given") + end end end return self end ---- Blank out the menu - remove **all root entries** and all entries below from the client's menus, leaving the generic structure untouched. +--- Blank out the menu - remove **all root entries** and all entries below from the client's F10 menus, leaving the generic structure untouched. -- @param #CLIENTMENUMANAGER self --- @param Wrapper.Client#CLIENT Client +-- @param Wrapper.Client#CLIENT Client (optional) If given, remove only for this client. -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:ResetMenu(Client) self:T(self.lid.."ResetMenu") for _,_entry in pairs(self.rootentries) do - local RootEntry = self.structure.generic[_entry] - if RootEntry then - self:Clear(RootEntry,Client) + --local RootEntry = self.structure.generic[_entry] + if _entry then + self:DeleteF10Entry(_entry,Client) end end return self end ---- Blank out the menu - remove **all root entries** and all entries below from all clients' menus, and **delete** the generic structure. +--- Blank out the menu - remove **all root entries** and all entries below from all clients' F10 menus, and **delete** the generic structure. -- @param #CLIENTMENUMANAGER self -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:ResetMenuComplete() self:T(self.lid.."ResetMenuComplete") for _,_entry in pairs(self.rootentries) do - local RootEntry = self.structure.generic[_entry] - if RootEntry then - self:Clear(RootEntry) + --local RootEntry = self.structure.generic[_entry] + if _entry then + self:DeleteF10Entry(_entry) end end - self.structure = nil - self.structure = { - generic = {}, - IDs = {}, - } + self.playertree = nil + self.playertree = {} self.rootentries = nil self.rootentries = {} + self.menutree = nil + self.menutree = {} return self end ---- Remove the entry and all entries below the given entry from the client's menus and the generic structure. +--- Remove the entry and all entries below the given entry from the client's F10 menus. -- @param #CLIENTMENUMANAGER self -- @param #CLIENTMENU Entry The entry to remove -- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. -- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:Clear(Entry,Client) - self:T(self.lid.."Clear") - local rid = self.structure.IDs[Entry.ID] - if rid then - local generic = self.structure.generic[rid] - local Set = self.clientset.Set - if Client then - Set = {Client} - end - for _,_client in pairs(Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - local playername = client:GetPlayerName() - local entry = self.structure[playername][rid] -- #CLIENTMENU - if entry then - entry:Clear() - self.structure[playername][rid] = nil - end - end - end - if not Client then - for _id,_entry in pairs(self.structure.generic) do - local entry = _entry -- #CLIENTMENU - if entry and entry.Parent and entry.Parent.ID and entry.Parent.ID == rid then - self.structure.IDs[entry.ID] = nil - entry = nil +function CLIENTMENUMANAGER:DeleteF10Entry(Entry,Client) + self:I(self.lid.."DeleteF10Entry") + local Set = self.clientset.Set + if Client then + Set = {Client} + end + for _,_client in pairs(Set) do + if _client and _client:IsAlive() then + local playername = _client:GetPlayerName() + if self.playertree[playername] then + local centry = self.playertree[playername][Entry.UUID] -- #CLIENTMENU + if centry then + centry:Clear() end end end @@ -772,115 +596,87 @@ function CLIENTMENUMANAGER:Clear(Entry,Client) return self end ---- [Internal] Clean up player shadow structure +--- Remove the entry and all entries below the given entry from the generic tree. -- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The entry to remove -- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:_CleanUpPlayerStructure() - self:T(self.lid.."_CleanUpPlayerStructure") - for _,_client in pairs(self.clientset.Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - local playername = client:GetPlayerName() - local newstructure = {} - for _id, _entry in UTILS.spairs(self.structure[playername]) do - if self.structure.generic[_id] then - newstructure[_id] = _entry - end - end - self.structure[playername] = nil - self.structure[playername] = newstructure +function CLIENTMENUMANAGER:DeleteGenericEntry(Entry) + self:I(self.lid.."DeleteGenericEntry") + + if Entry.Children and #Entry.Children > 0 then + self:RemoveGenericSubEntries(Entry) + end + + local depth = #Entry.path + local uuid = Entry.UUID + + local tbl = UTILS.DeepCopy(self.menutree) + + if tbl[depth] then + for i=depth,#tbl do + self:I("Level = "..i) + for _id,_uuid in pairs(tbl[i]) do + self:I(_uuid) + if string.find(_uuid,uuid) then + self:I("Match for ".._uuid) + self.menutree[i][_id] = nil + self.flattree[_uuid] = nil + end + end end end + return self end ---- Remove all entries below the given entry from the clients' menus and the generic structure. +--- Remove all entries below the given entry from the generic tree. -- @param #CLIENTMENUMANAGER self --- @param #CLIENTMENU Entry The menu entry +-- @param #CLIENTMENU Entry The entry where to start. This entry stays. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:RemoveGenericSubEntries(Entry) + self:I(self.lid.."RemoveGenericSubEntries") + + local depth = #Entry.path + 1 + local uuid = Entry.UUID + + local tbl = UTILS.DeepCopy(self.menutree) + + if tbl[depth] then + for i=depth,#tbl do + self:I("Level = "..i) + for _id,_uuid in pairs(tbl[i]) do + self:I(_uuid) + if string.find(_uuid,uuid) then + self:I("Match for ".._uuid) + self.menutree[i][_id] = nil + self.flattree[_uuid] = nil + end + end + end + end + return self +end + + +--- Remove all entries below the given entry from the client's F10 menus. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The entry where to start. This entry stays. -- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. -- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:RemoveSubEntries(Entry,Client) - self:T(self.lid.."RemoveSubEntries") - local rid = self.structure.IDs[Entry.ID] - if rid then - local Set = self.clientset.Set - if Client then - Set = {Client} - end - for _,_client in pairs(Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - local playername = client:GetPlayerName() - local entry = self.structure[playername][rid] -- #CLIENTMENU - if entry then - entry:RemoveSubEntries() - end +function CLIENTMENUMANAGER:RemoveF10SubEntries(Entry,Client) + self:I(self.lid.."RemoveSubEntries") + local Set = self.clientset.Set + if Client then + Set = {Client} + end + for _,_client in pairs(Set) do + if _client and _client:IsAlive() then + local playername = _client:GetPlayerName() + if self.playertree[playername] then + local centry = self.playertree[playername][Entry.UUID] -- #CLIENTMENU + centry:RemoveSubEntries() end end - if not Client then - for _id,_entry in pairs(self.structure.generic) do - local entry = _entry -- #CLIENTMENU - if entry and entry.Parent and entry.Parent.ID and entry.Parent.ID == rid then - self.structure.IDs[entry.ID] = nil - self.structure.generic[_id] = nil - end - end - end - end - self:_CleanUpPlayerStructure() - return self -end - ---- Remove a specific entry by ID from the generic structure --- @param #CLIENTMENUMANAGER self --- @param #number ID --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:_RemoveByID(ID) - self:T(self.lid.."_RemoveByID "..ID or "none") - if ID then - local gid = self.structure.IDs[ID] - if gid then - self.structure.generic[gid] = nil - self.structure.IDs[ID] = nil - end - end - return self -end - ---- [Internal] Dump structures to log for debug --- @param #CLIENTMENUMANAGER self --- @param #string Playername --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:_CheckStructures(Playername) - self:T(self.lid.."CheckStructures") - self:I("Generic Structure") - self:I("-----------------") - for _id,_entry in UTILS.spairs(self.structure.generic) do - local ID = "none" - if _entry and _entry.ID then - ID = _entry.ID - end - self:I("ID= ".._id.." | EntryID = "..ID) - if _id > 10 and _id < 14 then - self:I(_entry.name) - end - end - self:I("Reverse Structure") - self:I("-----------------") - for _id,_entry in UTILS.spairs(self.structure.IDs) do - self:I("EntryID= ".._id.." | ID = ".._entry) - end - if Playername then - self:I("Player Structure") - self:I("-----------------") - for _id,_entry in UTILS.spairs(self.structure[Playername]) do - local ID = "none" - if _entry and _entry.ID then - ID = _entry.ID - end - local _lid = _id or "none" - self:I("ID= ".._lid.." | EntryID = "..ID) - end end return self end diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 94f1b35c8..c7669ce73 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -988,6 +988,7 @@ do -- @field #table PlayerJoinMenu -- @field #table PlayerInfoMenu -- @field #boolean noflaresmokemenu +-- @field #boolean illumenu -- @field #boolean TransmitOnlyWithPlayers -- @field #boolean buddylasing -- @field Ops.PlayerRecce#PLAYERRECCE PlayerRecce @@ -999,6 +1000,13 @@ do -- @field #table PlayerMenuTag -- @field #boolean UseTypeNames -- @field Functional.Scoring#SCORING Scoring +-- @field Core.ClientMenu#CLIENTMENUMANAGER JoinTaskMenuTemplate +-- @field Core.ClientMenu#CLIENTMENU JoinMenu +-- @field Core.ClientMenu#CLIENTMENU JoinTopMenu +-- @field Core.ClientMenu#CLIENTMENU JoinInfoMenu +-- @field Core.ClientMenu#CLIENTMENUMANAGER ActiveTaskMenuTemplate +-- @field Core.ClientMenu#CLIENTMENU ActiveTopMenu +-- @field Core.ClientMenu#CLIENTMENU ActiveInfoMenu -- @extends Core.Fsm#FSM --- @@ -1155,6 +1163,7 @@ do -- MENUMARK = "Mark on map", -- MENUSMOKE = "Smoke", -- MENUFLARE = "Flare", +-- MENUILLU = "Illuminate", -- MENUABORT = "Abort", -- MENUJOIN = "Join Task", -- MENUTASKINFO = Task Info", @@ -1315,6 +1324,7 @@ PLAYERTASKCONTROLLER = { PlayerInfoMenu = {}, PlayerMenuTag = {}, noflaresmokemenu = false, + illumenu = false, TransmitOnlyWithPlayers = true, buddylasing = false, PlayerRecce = nil, @@ -1408,6 +1418,7 @@ PLAYERTASKCONTROLLER.Messages = { MENUMARK = "Mark on map", MENUSMOKE = "Smoke", MENUFLARE = "Flare", + MENUILLU = "Illuminate", MENUABORT = "Abort", MENUJOIN = "Join Task", MENUTASKINFO = "Task Info", @@ -1487,6 +1498,7 @@ PLAYERTASKCONTROLLER.Messages = { MENUMARK = "Kartenmarkierung", MENUSMOKE = "Rauchgranate", MENUFLARE = "Leuchtgranate", + MENUILLU = "Feldbeleuchtung", MENUABORT = "Abbrechen", MENUJOIN = "Auftrag annehmen", MENUTASKINFO = "Auftrag Briefing", @@ -1592,6 +1604,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.CallsignTranslations = nil self.noflaresmokemenu = false + self.illumenu = false self.ShowMagnetic = true @@ -1849,6 +1862,24 @@ function PLAYERTASKCONTROLLER:SetEnableSmokeFlareTask() return self end +--- [User] Show menu entries to illuminate targets. Needs smoke/flare enabled. +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetEnableIlluminateTask() + self:T(self.lid.."SetEnableSmokeFlareTask") + self.illumenu = true + return self +end + +--- [User] Do not show menu entries to illuminate targets. +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetDisableIlluminateTask() + self:T(self.lid.."SetDisableIlluminateTask") + self.illumenu = false + return self +end + --- [User] Show info text on screen with a coordinate info in any case (OFF by default) -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff Switch on = true or off = false @@ -2140,7 +2171,7 @@ end -- @param Core.Event#EVENTDATA EventData -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_EventHandler(EventData) - self:T(self.lid.."_EventHandler: "..EventData.id) + self:I(self.lid.."_EventHandler: "..EventData.id) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then if EventData.IniPlayerName then self:T(self.lid.."Event for player: "..EventData.IniPlayerName) @@ -2168,7 +2199,7 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) if self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then return self end - self:T(self.lid.."Event for player: "..EventData.IniPlayerName) + self:I(self.lid.."Event for player: "..EventData.IniPlayerName) local frequency = self.Frequency local freqtext = "" if type(frequency) == "table" then @@ -2198,7 +2229,11 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) if EventData.IniPlayerName then self.PlayerMenu[EventData.IniPlayerName] = nil --self:_BuildMenus(CLIENT:FindByName(EventData.IniUnitName)) - self:_BuildMenus(CLIENT:FindByPlayerName(EventData.IniPlayerName)) + --self:_BuildMenus(CLIENT:FindByPlayerName(EventData.IniPlayerName)) + local player = CLIENT:FindByName(EventData.IniUnitName) + self:I({player}) + local controller = self.JoinTaskMenuTemplate + controller:Propagate(player) end end end @@ -2308,7 +2343,7 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType() self:T(self.lid.."_GetTasksPerType") local tasktypes = self:_GetAvailableTaskTypes() - --self:T({tasktypes}) + -- self:I({tasktypes}) -- Sort tasks per threat level first local datatable = self.TaskQueue:GetDataTable() @@ -2965,12 +3000,12 @@ end --- [Internal] Join a player to a task -- @param #PLAYERTASKCONTROLLER self --- @param Wrapper.Group#GROUP Group --- @param Wrapper.Client#CLIENT Client -- @param Ops.PlayerTask#PLAYERTASK Task -- @param #boolean Force Assign task even if client already has one +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force) +function PLAYERTASKCONTROLLER:_JoinTask(Task, Force, Group, Client) self:T(self.lid.."_JoinTask") local playername, ttsplayername = self:_GetPlayerName(Client) if self.TasksPerPlayer:HasUniqueID(playername) and not Force then @@ -3004,7 +3039,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force) self.TasksPerPlayer:Push(Task,playername) self:__PlayerJoinedTask(1, Group, Client, Task) -- clear menu - self:_BuildMenus(Client,true) + --self:_BuildMenus(Client,true) end if Task.Type == AUFTRAG.Type.PRECISIONBOMBING then if not self.PrecisionTasks:HasUniqueID(Task.PlayerTaskNr) then @@ -3065,11 +3100,11 @@ end --- [Internal] Show active task info -- @param #PLAYERTASKCONTROLLER self +-- @param Ops.PlayerTask#PLAYERTASK Task -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Client#CLIENT Client --- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) +function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) self:T(self.lid.."_ActiveTaskInfo") local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" @@ -3389,7 +3424,7 @@ function PLAYERTASKCONTROLLER:_AbortTask(Group, Client) if not self.NoScreenOutput then local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client) end - self:_BuildMenus(Client,true) + --self:_BuildMenus(Client,true) return self end @@ -3454,6 +3489,150 @@ function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu return taskinfomenu end +-- TODO - New Menu Manager + +function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate() + self:I("_UpdateJoinMenuTemplate") + if self.TaskQueue:Count() > 0 then + local taskpertype = self:_GetTasksPerType() + local JoinMenu = self.JoinMenu -- Core.ClientMenu#CLIENTMENU + self:I(JoinMenu.path) + local controller = self.JoinTaskMenuTemplate -- Core.ClientMenu#CLIENTMENUMANAGER + local actcontroller = self.ActiveTaskMenuTemplate -- Core.ClientMenu#CLIENTMENUMANAGER + local entrynumbers = {} + local existingentries = {} + local maxn = self.menuitemlimit + -- Generate task type menu items + for _type,_ in pairs(taskpertype) do + --local found = controller:FindEntryByText(_type) + local found, text = controller:FindEntryUnderParentByText(JoinMenu,_type) + self:I(text) + if not found then + local newentry = controller:NewEntry(_type,JoinMenu) + controller:AddEntry(newentry) + if self.JoinInfoMenu then + local newentry = controller:NewEntry(_type,self.JoinInfoMenu) + controller:AddEntry(newentry) + end + entrynumbers[_type] = 0 + existingentries[_type] = {} + else + entrynumbers[_type] = #found.Children + if #taskpertype[_type] > 0 and entrynumbers[_type] < maxn then + existingentries[_type] = {} + for _,_entry in pairs(found.Children) do + local entry = _entry -- Core.ClientMenu#CLIENTMENU + table.insert(existingentries[_type],_entry.name) + end + if self.verbose then + UTILS.PrintTableToLog(existingentries) + end + end + end + end + + local function FindInList(List,Text) + local found = false + for _,_name in pairs(List) do + if string.match(_name,Text) then + found = true + break + end + end + return found + end + + end + return self +end + +function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate() + self:I("_CreateActiveTaskMenuTemplate") + + local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale) + local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) + local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) + + local JoinTaskMenuTemplate = CLIENTMENUMANAGER:New(self.ClientSet,"JoinTask") + + if not self.JoinTopMenu then + local taskings = self.gettext:GetEntry("MENUTASKING",self.locale) + local longname = self.Name..taskings..self.Type + local menuname = self.MenuName or longname + self.JoinTopMenu = JoinTaskMenuTemplate:NewEntry(menuname,self.MenuParent) + end + + if self.AllowFlash then + JoinTaskMenuTemplate:NewEntry(flashtext,self.JoinTopMenu,self._SwitchFlashing,self,group,client) + end + + self.JoinMenu = JoinTaskMenuTemplate:NewEntry(menujoin,self.JoinTopMenu) + + if self.taskinfomenu then + local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) + self.JoinInfoMenu = JoinTaskMenuTemplate:NewEntry(menutaskinfo,self.JoinTopMenu) + end + + if self.TaskQueue:Count() == 0 then + JoinTaskMenuTemplate:NewEntry(menunotasks,self.JoinMenu) + end + + self.JoinTaskMenuTemplate = JoinTaskMenuTemplate + + return self +end + + +function PLAYERTASKCONTROLLER:_CreateActiveTaskMenuTemplate() + self:I("_CreateActiveTaskMenuTemplate") + + local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale) + local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale) + local menumark = self.gettext:GetEntry("MENUMARK",self.locale) + local menusmoke = self.gettext:GetEntry("MENUSMOKE",self.locale) + local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale) + local menuillu = self.gettext:GetEntry("MENUILLU",self.locale) + local menuabort = self.gettext:GetEntry("MENUABORT",self.locale) + + local ActiveTaskMenuTemplate = CLIENTMENUMANAGER:New(self.ClientSet,"ActiveTask") + + if not self.ActiveTopMenu then + local taskings = self.gettext:GetEntry("MENUTASKING",self.locale) + local longname = self.Name..taskings..self.Type + local menuname = self.MenuName or longname + self.ActiveTopMenu = ActiveTaskMenuTemplate:NewEntry(menuname,self.MenuParent) + end + + if self.AllowFlash then + local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) + ActiveTaskMenuTemplate:NewEntry(flashtext,self.ActiveTopMenu,self._SwitchFlashing,self,group,client) + end + + local active = ActiveTaskMenuTemplate:NewEntry(menuactive,self.ActiveTopMenu) + ActiveTaskMenuTemplate:NewEntry(menuinfo,active,self._ActiveTaskInfo,self,nil) + ActiveTaskMenuTemplate:NewEntry(menumark,active,self._MarkTask,self,nil) + + if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A and self.noflaresmokemenu ~= true then + ActiveTaskMenuTemplate:NewEntry(menusmoke,active,self._SmokeTask,self,group,client) + ActiveTaskMenuTemplate:NewEntry(menuflare,active,self._FlareTask,self,group,client) + + if self.illumenu then + ActiveTaskMenuTemplate:NewEntry(menuillu,active,self._IlluminateTask,self,group,client) + end + + end + + ActiveTaskMenuTemplate:NewEntry(menuabort,active,self._AbortTask,self,group,client) + self.ActiveTaskMenuTemplate = ActiveTaskMenuTemplate + + if self.taskinfomenu and self.activehasinfomenu then + local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) + self.ActiveInfoMenu = ActiveTaskMenuTemplate:NewEntry(menutaskinfo,self.ActiveTopMenu) + end + + return self +end + --- [Internal] Build client menus -- @param #PLAYERTASKCONTROLLER self -- @param Wrapper.Client#CLIENT Client (optional) build for this client name only @@ -3965,6 +4144,20 @@ end -- TODO: FSM Functions PLAYERTASKCONTROLLER ------------------------------------------------------------------------------------------------------------------- +--- [Internal] On after start call +-- @param #PLAYERTASKCONTROLLER self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:onafterStart(From, Event, To) + self:I({From, Event, To}) + self:I(self.lid.."onafterStart") + self:_CreateJoinMenuTemplate() + self:_CreateActiveTaskMenuTemplate() + return self +end + --- [Internal] On after Status call -- @param #PLAYERTASKCONTROLLER self -- @param #string From @@ -3994,7 +4187,9 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To) end end - self:_BuildMenus(nil,enforcedmenu) + --self:_BuildMenus(nil,enforcedmenu) + + self:_UpdateJoinMenuTemplate() if self.verbose then local text = string.format("%s | New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",self.MenuName, targetcount,taskcount,playercount,assignedtasks) @@ -4067,7 +4262,7 @@ function PLAYERTASKCONTROLLER:onafterTaskSuccess(From, Event, To, Task) end local clients=Task:GetClientObjects() for _,client in pairs(clients) do - self:_BuildMenus(client,true,true) + --self:_BuildMenus(client,true,true) end return self end From e8ad649770a6659037ce898c729bf5ff769774e4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 16 Jul 2023 11:26:47 +0200 Subject: [PATCH 304/603] #PT --- Moose Development/Moose/Ops/PlayerTask.lua | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index c7669ce73..8968d186e 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -3517,17 +3517,6 @@ function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate() entrynumbers[_type] = 0 existingentries[_type] = {} else - entrynumbers[_type] = #found.Children - if #taskpertype[_type] > 0 and entrynumbers[_type] < maxn then - existingentries[_type] = {} - for _,_entry in pairs(found.Children) do - local entry = _entry -- Core.ClientMenu#CLIENTMENU - table.insert(existingentries[_type],_entry.name) - end - if self.verbose then - UTILS.PrintTableToLog(existingentries) - end - end end end From e8432db26eb591810c2e7ff41baba3dbf6d6e7e2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 17 Jul 2023 16:26:06 +0200 Subject: [PATCH 305/603] #ClientMenu --- Moose Development/Moose/Core/ClientMenu.lua | 195 +++++-- Moose Development/Moose/Core/Menu.lua | 29 +- Moose Development/Moose/Core/Set.lua | 5 +- Moose Development/Moose/Ops/PlayerTask.lua | 611 +++++++++----------- 4 files changed, 431 insertions(+), 409 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index c8d9c3833..74745c217 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -3,12 +3,14 @@ -- **Main Features:** -- -- * For complex, non-static menu structures --- * Separation of menu tree creation from pushing it to clients +-- * Lightweigt implementation as alternative to MENU +-- * Separation of menu tree creation from menu on the clients's side -- * Works with a SET_CLIENT set of clients -- * Allow manipulation of the shadow tree in various ways -- * Push to all or only one client --- * Change entries' menu text, even if they have a sub-structure --- * Option to make an entry usable once +-- * Change entries' menu text +-- * Option to make an entry usable once only across all clients +-- * Auto appends GROUP and CLIENT objects to menu calls -- -- === -- @@ -55,7 +57,7 @@ CLIENTMENU = { ClassName = "CLIENTMENUE", lid = "", - version = "0.0.2", + version = "0.1.0", name = nil, path = nil, group = nil, @@ -94,8 +96,12 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) end self.name = Text or "unknown entry" if Parent then - self.parentpath = Parent:GetPath() - Parent:AddChild(self) + if Parent:IsInstanceOf("MENU_BASE") then + self.parentpath = Parent.MenuPath + else + self.parentpath = Parent:GetPath() + Parent:AddChild(self) + end end self.Parent = Parent self.Function = Function @@ -103,7 +109,7 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) table.insert(self.Functionargs,self.group) table.insert(self.Functionargs,self.client) if self.Functionargs and self.debug then - self:I({"Functionargs",self.Functionargs}) + self:T({"Functionargs",self.Functionargs}) end if not self.Generic then if Function ~= nil then @@ -136,7 +142,7 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) self.path[#self.path+1] = Text end self.UUID = table.concat(self.path,";") - self:I({self.UUID}) + self:T({self.UUID}) self.Once = false -- Log id. self.lid=string.format("CLIENTMENU %s | %s | ", self.ID, self.name) @@ -144,6 +150,21 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) return self end +--- Create a UUID +-- @param #CLIENTMENU self +-- @param #CLIENTMENU Parent The parent object if any +-- @param #string Text The menu entry text +-- @return #string UUID +function CLIENTMENU:CreateUUID(Parent,Text) + local path = {} + if Parent and Parent.path then + path = Parent.path + end + path[#path+1] = Text + local UUID = table.concat(path,";") + return UUID +end + --- Set the CLIENTMENUMANAGER for this entry. -- @param #CLIENTMENU self -- @param #CLIENTMENUMANAGER Controller The controlling object. @@ -166,9 +187,9 @@ end -- @param #CLIENTMENU self -- @return #CLIENTMENU self function CLIENTMENU:RemoveF10() - self:I(self.lid.."RemoveF10") - if not self.Generic then - self:I(self.lid.."Removing") + self:T(self.lid.."RemoveF10") + if self.GroupID then + --self:I(self.lid.."Removing "..table.concat(self.path,";")) missionCommands.removeItemForGroup(self.GroupID , self.path ) end return self @@ -214,10 +235,10 @@ end -- @param #CLIENTMENU self -- @return #CLIENTMENU self function CLIENTMENU:RemoveSubEntries() - self:I(self.lid.."RemoveSubEntries") - self:I({self.Children}) + self:T(self.lid.."RemoveSubEntries") + self:T({self.Children}) for _id,_entry in pairs(self.Children) do - self:I("Removing ".._id) + self:T("Removing ".._id) if _entry then _entry:RemoveSubEntries() _entry:RemoveF10() @@ -328,7 +349,21 @@ end -- ## Add a single entry -- -- local baimenu = menumgr:NewEntry("BAI",mymenu_lv1b) --- menumgr:AddEntry(baimenu) +-- +-- menumgr:AddEntry(baimenu) +-- +-- ## Add an entry with a function +-- +-- local baimenu = menumgr:NewEntry("Task Action", mymenu_lv1b, TestFunction, Argument1, Argument1) +-- +-- Now, the class will **automatically append the call with GROUP and CLIENT objects**, as this is can only be done when pushing the entry to the clients. So, the actual function implementation needs to look like this: +-- +-- function TestFunction( Argument1, Argument2, Group, Client) +-- +-- **Caveat is**, that you need to ensure your arguments are not **nil** or **false**, as LUA will optimize those away. You would end up having Group and Client in wrong places in the function call. Hence, +-- if you need/ want to send **nil** or **false**, send a place holder instead and ensure your function can handle this, e.g. +-- +-- local baimenu = menumgr:NewEntry("Task Action", mymenu_lv1b, TestFunction, "nil", Argument1) -- -- ## Change the text of a leaf entry in the menu tree -- @@ -346,7 +381,7 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.0.4", + version = "0.1.0", name = nil, clientset = nil, menutree = {}, @@ -370,7 +405,7 @@ function CLIENTMENUMANAGER:New(ClientSet, Alias) -- Log id. self.lid=string.format("CLIENTMENUMANAGER %s | %s | ", self.version, self.name) if self.debug then - self:I(self.lid.."Created") + self:T(self.lid.."Created") end return self end @@ -396,12 +431,21 @@ function CLIENTMENUMANAGER:NewEntry(Text,Parent,Function,...) return entry end +--- Check matching entry in the generic structure by UUID. +-- @param #CLIENTMENUMANAGER self +-- @param #string UUID UUID of the menu entry. +-- @return #boolean Exists +function CLIENTMENUMANAGER:EntryUUIDExists(UUID) + local exists = self.flattree[UUID] and true or false + return exists +end + --- Find matching entry in the generic structure by UUID. -- @param #CLIENTMENUMANAGER self -- @param #string UUID UUID of the menu entry. -- @return #CLIENTMENU Entry The #CLIENTMENU object found or nil. function CLIENTMENUMANAGER:FindEntryByUUID(UUID) - self:I(self.lid.."FindEntryByUUID "..UUID or "None") + self:T(self.lid.."FindEntryByUUID "..UUID or "None") local entry = nil for _gid,_entry in pairs(self.flattree) do local Entry = _entry -- #CLIENTMENU @@ -412,33 +456,82 @@ function CLIENTMENUMANAGER:FindEntryByUUID(UUID) return entry end ---- Find matching entry by text in the generic structure by UUID. +--- Find matching entries by text in the generic structure by UUID. -- @param #CLIENTMENUMANAGER self --- @param #string Text Text or partial text of the menu entry to find +-- @param #string Text Text or partial text of the menu entry to find. +-- @param #CLIENTMENU Parent (Optional) Only find entries under this parent entry. -- @return #table Table of matching UUIDs of #CLIENTMENU objects -- @return #table Table of matching #CLIENTMENU objects -function CLIENTMENUMANAGER:FindUUIDsByText(Text) - self:I(self.lid.."FindUUIDsByText "..Text or "None") +-- @return #number Number of matches +function CLIENTMENUMANAGER:FindUUIDsByText(Text,Parent) + self:T(self.lid.."FindUUIDsByText "..Text or "None") local matches = {} local entries = {} + local n = 0 for _uuid,_entry in pairs(self.flattree) do local Entry = _entry -- #CLIENTMENU - if Entry and string.find(Entry.name,Text) then - table.insert(matches,_uuid) - table.insert(entries,Entry ) + if Parent then + if Entry and string.find(Entry.name,Text) and string.find(Entry.UUID,Parent.UUID) then + table.insert(matches,_uuid) + table.insert(entries,Entry ) + n=n+1 + end + else + if Entry and string.find(Entry.name,Text) then + table.insert(matches,_uuid) + table.insert(entries,Entry ) + n=n+1 + end end end - return matches, entries + return matches, entries, n end --- Find matching entries in the generic structure by the menu text. -- @param #CLIENTMENUMANAGER self -- @param #string Text Text or partial text of the F10 menu entry. +-- @param #CLIENTMENU Parent (Optional) Only find entries under this parent entry. -- @return #table Table of matching #CLIENTMENU objects. -function CLIENTMENUMANAGER:FindEntriesByText(Text) - self:I(self.lid.."FindEntriesByText "..Text or "None") - local matches, objects = self:FindUUIDsByText(Text) - return objects +-- @return #number Number of matches +function CLIENTMENUMANAGER:FindEntriesByText(Text,Parent) + self:T(self.lid.."FindEntriesByText "..Text or "None") + local matches, objects, number = self:FindUUIDsByText(Text, Parent) + return objects, number +end + +--- Find matching entries under a parent in the generic structure by UUID. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Parent Find entries under this parent entry. +-- @return #table Table of matching UUIDs of #CLIENTMENU objects +-- @return #table Table of matching #CLIENTMENU objects +-- @return #number Number of matches +function CLIENTMENUMANAGER:FindUUIDsByParent(Parent) + self:T(self.lid.."FindUUIDsByParent") + local matches = {} + local entries = {} + local n = 0 + for _uuid,_entry in pairs(self.flattree) do + local Entry = _entry -- #CLIENTMENU + if Parent then + if Entry and string.find(Entry.UUID,Parent.UUID) then + table.insert(matches,_uuid) + table.insert(entries,Entry ) + n=n+1 + end + end + end + return matches, entries, n +end + +--- Find matching entries in the generic structure under a parent. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Parent Find entries under this parent entry. +-- @return #table Table of matching #CLIENTMENU objects. +-- @return #number Number of matches +function CLIENTMENUMANAGER:FindEntriesByParent(Parent) + self:T(self.lid.."FindEntriesByParent") + local matches, objects, number = self:FindUUIDsByParent(Parent) + return objects, number end --- Alter the text of a leaf entry in the generic structure and push to one specific client's F10 menu. @@ -468,8 +561,8 @@ end -- @param Wrapper.Client#CLIENT Client (optional) If given, propagate only for this client. -- @return #CLIENTMENU Entry function CLIENTMENUMANAGER:Propagate(Client) - self:I(self.lid.."Propagate") - self:I(Client) + self:T(self.lid.."Propagate") + self:T(Client) local Set = self.clientset.Set if Client then Set = {Client} @@ -483,12 +576,12 @@ function CLIENTMENUMANAGER:Propagate(Client) self.playertree[playername] = {} end for level,branch in pairs (self.menutree) do - self:I("Building branch:" .. level) + self:T("Building branch:" .. level) for _,leaf in pairs(branch) do - self:I("Building leaf:" .. leaf) + self:T("Building leaf:" .. leaf) local entry = self:FindEntryByUUID(leaf) if entry then - self:I("Found generic entry:" .. entry.UUID) + self:T("Found generic entry:" .. entry.UUID) local parent = nil if entry.Parent and entry.Parent.UUID then parent = self.playertree[playername][entry.Parent.UUID] or self:FindEntryByUUID(entry.Parent.UUID) @@ -496,7 +589,7 @@ function CLIENTMENUMANAGER:Propagate(Client) self.playertree[playername][entry.UUID] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) self.playertree[playername][entry.UUID].Once = entry.Once else - self:I("NO generic entry for:" .. leaf) + self:T("NO generic entry for:" .. leaf) end end end @@ -521,15 +614,18 @@ function CLIENTMENUMANAGER:AddEntry(Entry,Client) if client and client:IsAlive() then local playername = client:GetPlayerName() if Entry then - self:I("Adding generic entry:" .. Entry.UUID) + self:T("Adding generic entry:" .. Entry.UUID) local parent = nil + if not self.playertree[playername] then + self.playertree[playername] = {} + end if Entry.Parent and Entry.Parent.UUID then parent = self.playertree[playername][Entry.Parent.UUID] or self:FindEntryByUUID(Entry.Parent.UUID) end self.playertree[playername][Entry.UUID] = CLIENTMENU:NewEntry(client,Entry.name,parent,Entry.Function,unpack(Entry.Functionargs)) self.playertree[playername][Entry.UUID].Once = Entry.Once else - self:I("NO generic entry given") + self:T("NO generic entry given") end end end @@ -577,7 +673,7 @@ end -- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:DeleteF10Entry(Entry,Client) - self:I(self.lid.."DeleteF10Entry") + self:T(self.lid.."DeleteF10Entry") local Set = self.clientset.Set if Client then Set = {Client} @@ -588,6 +684,7 @@ function CLIENTMENUMANAGER:DeleteF10Entry(Entry,Client) if self.playertree[playername] then local centry = self.playertree[playername][Entry.UUID] -- #CLIENTMENU if centry then + --self:I("Match for "..Entry.UUID) centry:Clear() end end @@ -601,7 +698,7 @@ end -- @param #CLIENTMENU Entry The entry to remove -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:DeleteGenericEntry(Entry) - self:I(self.lid.."DeleteGenericEntry") + self:T(self.lid.."DeleteGenericEntry") if Entry.Children and #Entry.Children > 0 then self:RemoveGenericSubEntries(Entry) @@ -614,11 +711,11 @@ function CLIENTMENUMANAGER:DeleteGenericEntry(Entry) if tbl[depth] then for i=depth,#tbl do - self:I("Level = "..i) + --self:I("Level = "..i) for _id,_uuid in pairs(tbl[i]) do - self:I(_uuid) - if string.find(_uuid,uuid) then - self:I("Match for ".._uuid) + self:T(_uuid) + if string.find(_uuid,uuid) or _uuid == uuid then + --self:I("Match for ".._uuid) self.menutree[i][_id] = nil self.flattree[_uuid] = nil end @@ -634,7 +731,7 @@ end -- @param #CLIENTMENU Entry The entry where to start. This entry stays. -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:RemoveGenericSubEntries(Entry) - self:I(self.lid.."RemoveGenericSubEntries") + self:T(self.lid.."RemoveGenericSubEntries") local depth = #Entry.path + 1 local uuid = Entry.UUID @@ -643,11 +740,11 @@ function CLIENTMENUMANAGER:RemoveGenericSubEntries(Entry) if tbl[depth] then for i=depth,#tbl do - self:I("Level = "..i) + self:T("Level = "..i) for _id,_uuid in pairs(tbl[i]) do - self:I(_uuid) + self:T(_uuid) if string.find(_uuid,uuid) then - self:I("Match for ".._uuid) + self:T("Match for ".._uuid) self.menutree[i][_id] = nil self.flattree[_uuid] = nil end @@ -664,7 +761,7 @@ end -- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:RemoveF10SubEntries(Entry,Client) - self:I(self.lid.."RemoveSubEntries") + self:T(self.lid.."RemoveSubEntries") local Set = self.clientset.Set if Client then Set = {Client} diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 39acc42f1..7209bdac5 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -278,7 +278,9 @@ do -- MENU_BASE end end -do -- MENU_COMMAND_BASE +do + --- + -- MENU_COMMAND_BASE -- @type MENU_COMMAND_BASE -- @field #function MenuCallHandler -- @extends Core.Menu#MENU_BASE @@ -344,7 +346,9 @@ do -- MENU_COMMAND_BASE end end -do -- MENU_MISSION +do + --- + -- MENU_MISSION -- @type MENU_MISSION -- @extends Core.Menu#MENU_BASE --- Manages the main menus for a complete mission. @@ -432,8 +436,9 @@ do -- MENU_MISSION end end -do -- MENU_MISSION_COMMAND - +do + + --- MENU_MISSION_COMMAND -- @type MENU_MISSION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE @@ -510,7 +515,8 @@ do -- MENU_MISSION_COMMAND return self end end -do -- MENU_COALITION +do + --- MENU_COALITION -- @type MENU_COALITION -- @extends Core.Menu#MENU_BASE @@ -636,8 +642,9 @@ do -- MENU_COALITION return self end end -do -- MENU_COALITION_COMMAND - +do + + --- MENU_COALITION_COMMAND -- @type MENU_COALITION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE @@ -726,7 +733,10 @@ do -- So every menu for a client created must be tracked so that program logic accidentally does not create. -- the same menus twice during initialization logic. -- These menu classes are handling this logic with this variable. + local _MENUGROUPS = {} + + --- -- @type MENU_GROUP -- @extends Core.Menu#MENU_BASE @@ -900,7 +910,7 @@ do return self end - + --- -- @type MENU_GROUP_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE @@ -984,6 +994,7 @@ do end --- MENU_GROUP_DELAYED do + --- -- @type MENU_GROUP_DELAYED -- @extends Core.Menu#MENU_BASE @@ -1107,7 +1118,7 @@ do return self end - + --- -- @type MENU_GROUP_COMMAND_DELAYED -- @extends Core.Menu#MENU_COMMAND_BASE diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index d7e7b6f97..df5a14320 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -3945,7 +3945,8 @@ do -- SET_STATIC end do -- SET_CLIENT - + + --- -- @type SET_CLIENT -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval @@ -4059,7 +4060,7 @@ do -- SET_CLIENT --- Remove CLIENT(s) from SET_CLIENT. -- @param Core.Set#SET_CLIENT self - -- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names. + -- @param Wrapper.Client#CLIENT RemoveClientNames A single object or an array of CLIENT objects. -- @return self function SET_CLIENT:RemoveClientsByName( RemoveClientNames ) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 8968d186e..196e422d0 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -954,6 +954,7 @@ do -- @field Utilities.FiFo#FIFO TasksPerPlayer -- @field Utilities.FiFo#FIFO PrecisionTasks -- @field Core.Set#SET_CLIENT ClientSet +-- @field Core.Set#SET_CLIENT ActiveClientSet -- @field #string ClientFilter -- @field #string Name -- @field #string Type @@ -1610,20 +1611,22 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.UseTypeNames = false - local IsClientSet = false + self.IsClientSet = false if ClientFilter and type(ClientFilter) == "table" and ClientFilter.ClassName and ClientFilter.ClassName == "SET_CLIENT" then -- we have a predefined SET_CLIENT self.ClientSet = ClientFilter - IsClientSet = true + self.IsClientSet = true end - if ClientFilter and not IsClientSet then + if ClientFilter and not self.IsClientSet then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart() - elseif not IsClientSet then + elseif not self.IsClientSet then self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterStart() end + self.ActiveClientSet = SET_CLIENT:New() + self.lid=string.format("PlayerTaskController %s %s | ", self.Name, tostring(self.Type)) self:_InitLocalization() @@ -2171,7 +2174,8 @@ end -- @param Core.Event#EVENTDATA EventData -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_EventHandler(EventData) - self:I(self.lid.."_EventHandler: "..EventData.id) + self:T(self.lid.."_EventHandler: "..EventData.id) + self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then if EventData.IniPlayerName then self:T(self.lid.."Event for player: "..EventData.IniPlayerName) @@ -2195,45 +2199,45 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) self:T(self.lid..text) end elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then - if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then - if self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then + if EventData.IniPlayerName and EventData.IniGroup then + if self.IsClientSet and self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then + self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName) return self end - self:I(self.lid.."Event for player: "..EventData.IniPlayerName) - local frequency = self.Frequency - local freqtext = "" - if type(frequency) == "table" then - freqtext = self.gettext:GetEntry("FREQUENCIES",self.locale) - freqtext = freqtext..table.concat(frequency,", ") - else - local freqt = self.gettext:GetEntry("FREQUENCY",self.locale) - freqtext = string.format(freqt,frequency) - end - local modulation = self.Modulation - if type(modulation) == "table" then modulation = modulation[1] end - modulation = UTILS.GetModulationName(modulation) - local switchtext = self.gettext:GetEntry("BROADCAST",self.locale) + self:T(self.lid.."Event for player: "..EventData.IniPlayerName) - local playername = EventData.IniPlayerName - if EventData.IniGroup then - -- personalized flight name in player naming - if self.customcallsigns[playername] then - self.customcallsigns[playername] = nil + if self.UseSRS then + local frequency = self.Frequency + local freqtext = "" + if type(frequency) == "table" then + freqtext = self.gettext:GetEntry("FREQUENCIES",self.locale) + freqtext = freqtext..table.concat(frequency,", ") + else + local freqt = self.gettext:GetEntry("FREQUENCY",self.locale) + freqtext = string.format(freqt,frequency) end - playername = EventData.IniGroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber) + local modulation = self.Modulation + if type(modulation) == "table" then modulation = modulation[1] end + modulation = UTILS.GetModulationName(modulation) + local switchtext = self.gettext:GetEntry("BROADCAST",self.locale) + + local playername = EventData.IniPlayerName + if EventData.IniGroup then + -- personalized flight name in player naming + if self.customcallsigns[playername] then + self.customcallsigns[playername] = nil + end + playername = EventData.IniGroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber) + end + playername = self:_GetTextForSpeech(playername) + --local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext) + local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext) + self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation) end - playername = self:_GetTextForSpeech(playername) - --local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext) - local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext) - self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation) if EventData.IniPlayerName then self.PlayerMenu[EventData.IniPlayerName] = nil - --self:_BuildMenus(CLIENT:FindByName(EventData.IniUnitName)) - --self:_BuildMenus(CLIENT:FindByPlayerName(EventData.IniPlayerName)) local player = CLIENT:FindByName(EventData.IniUnitName) - self:I({player}) - local controller = self.JoinTaskMenuTemplate - controller:Propagate(player) + self:_SwitchMenuForClient(player,"Info") end end end @@ -2343,7 +2347,7 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType() self:T(self.lid.."_GetTasksPerType") local tasktypes = self:_GetAvailableTaskTypes() - -- self:I({tasktypes}) + --self:I({tasktypes}) -- Sort tasks per threat level first local datatable = self.TaskQueue:GetDataTable() @@ -2362,11 +2366,23 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType() local threat=_data.threat local task = _data.task -- Ops.PlayerTask#PLAYERTASK local type = task.Type + local name = task.Target:GetName() + --self:I(name) if not task:IsDone() then + --self:I(name) table.insert(tasktypes[type],task) end end + --[[ + for _type,_data in pairs(tasktypes) do + self:I("Task Type: ".._type) + for _id,_task in pairs(_data) do + self:I("Task Name: ".._task.Target:GetName()) + end + end + --]] + return tasktypes end @@ -2478,7 +2494,7 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue() local client = _client --Wrapper.Client#CLIENT local group = client:GetGroup() for _,task in pairs(nexttasks) do - self:_JoinTask(group,client,task,true) + self:_JoinTask(task,true,group,client) end end end @@ -2497,7 +2513,7 @@ end -- @param #PLAYERTASKCONTROLLER self -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_CheckPrecisionTasks() - self:T(self.lid.."_CheckTaskQueue") + self:T(self.lid.."_CheckPrecisionTasks") if self.PrecisionTasks:Count() > 0 and self.precisionbombing then if not self.LasingDrone or self.LasingDrone:IsDead() then -- we need a new drone @@ -3006,9 +3022,14 @@ end -- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_JoinTask(Task, Force, Group, Client) + self:T({Force, Group, Client}) self:T(self.lid.."_JoinTask") + local force = false + if type(Force) == "boolean" then + force = Force + end local playername, ttsplayername = self:_GetPlayerName(Client) - if self.TasksPerPlayer:HasUniqueID(playername) and not Force then + if self.TasksPerPlayer:HasUniqueID(playername) and not force then -- Player already has a task if not self.NoScreenOutput then local text = self.gettext:GetEntry("HAVEACTIVETASK",self.locale) @@ -3039,7 +3060,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Task, Force, Group, Client) self.TasksPerPlayer:Push(Task,playername) self:__PlayerJoinedTask(1, Group, Client, Task) -- clear menu - --self:_BuildMenus(Client,true) + self:_SwitchMenuForClient(Client,"Active",1) end if Task.Type == AUFTRAG.Type.PRECISIONBOMBING then if not self.PrecisionTasks:HasUniqueID(Task.PlayerTaskNr) then @@ -3109,10 +3130,14 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" local textTTS = "" - if self.TasksPerPlayer:HasUniqueID(playername) or Task then + local task = nil + if type(Task) ~= "string" then + task = Task + end + if self.TasksPerPlayer:HasUniqueID(playername) or task then -- NODO: Show multiple? -- Details - local task = Task or self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK + local task = task or self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK local tname = self.gettext:GetEntry("TASKNAME",self.locale) local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale) local taskname = string.format(tname,task.Type,task.PlayerTaskNr) @@ -3424,119 +3449,173 @@ function PLAYERTASKCONTROLLER:_AbortTask(Group, Client) if not self.NoScreenOutput then local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client) end - --self:_BuildMenus(Client,true) + self:_SwitchMenuForClient(Client,"Info",1) return self end ---- [Internal] Build Task Info Menu --- @param #PLAYERTASKCONTROLLER self --- @param Wrapper.Group#GROUP group --- @param Wrapper.Client#CLIENT client --- @param #string playername --- @param Core.Menu#MENU_BASE topmenu --- @param #table tasktypes --- @param #table taskpertype --- @param #string newtag --- @return #table taskinfomenu -function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag) - self:T(self.lid.."_BuildTaskInfoMenu") - local taskinfomenu = nil - if self.taskinfomenu then - local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) - local taskinfomenu = MENU_GROUP:New(group,menutaskinfo,topmenu):SetTag(newtag) - local ittypes = {} - local itaskmenu = {} - local tnow = timer.getTime() - - for _tasktype,_data in pairs(tasktypes) do - ittypes[_tasktype] = MENU_GROUP:New(group,_tasktype,taskinfomenu):SetTag(newtag) - local tasks = taskpertype[_tasktype] or {} - local n = 0 - for _,_task in pairs(tasks) do - _task = _task -- Ops.PlayerTask#PLAYERTASK - local pilotcount = _task:CountClients() - local newtext = "]" - -- marker for new tasks - if tnow - _task.timestamp < 60 then - newtext = "*]" - end - local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale) - local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext) - if self.UseGroupNames then - local name = _task.Target:GetName() - if name ~= "Unknown" then - text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) - end - end - if self.UseTypeNames then - if _task.TypeName then - --local name = self.gettext:GetEntry(_task.TypeName,self.locale) - text = string.format("%s (%03d) [%d%s",_task.TypeName,_task.PlayerTaskNr,pilotcount,newtext) - --self:T(self.lid.."Menu text = "..text) - end - end - local taskentry = MENU_GROUP_COMMAND:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task):SetTag(newtag) - --taskentry:SetTag(playername) - itaskmenu[#itaskmenu+1] = taskentry - -- keep max items limit - n = n + 1 - if n >= self.menuitemlimit then - break - end - end - end - end - return taskinfomenu -end -- TODO - New Menu Manager - +--- [Internal] _UpdateJoinMenuTemplate +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate() - self:I("_UpdateJoinMenuTemplate") + self:T("_UpdateJoinMenuTemplate") if self.TaskQueue:Count() > 0 then local taskpertype = self:_GetTasksPerType() local JoinMenu = self.JoinMenu -- Core.ClientMenu#CLIENTMENU - self:I(JoinMenu.path) + --self:I(JoinMenu.UUID) local controller = self.JoinTaskMenuTemplate -- Core.ClientMenu#CLIENTMENUMANAGER local actcontroller = self.ActiveTaskMenuTemplate -- Core.ClientMenu#CLIENTMENUMANAGER - local entrynumbers = {} - local existingentries = {} + local actinfomenu = self.ActiveInfoMenu + --local entrynumbers = {} + --local existingentries = {} local maxn = self.menuitemlimit -- Generate task type menu items for _type,_ in pairs(taskpertype) do - --local found = controller:FindEntryByText(_type) - local found, text = controller:FindEntryUnderParentByText(JoinMenu,_type) - self:I(text) - if not found then + local found = controller:FindEntriesByText(_type) + --self:I({found}) + if #found == 0 then local newentry = controller:NewEntry(_type,JoinMenu) controller:AddEntry(newentry) if self.JoinInfoMenu then local newentry = controller:NewEntry(_type,self.JoinInfoMenu) controller:AddEntry(newentry) end - entrynumbers[_type] = 0 - existingentries[_type] = {} - else - end - end - - local function FindInList(List,Text) - local found = false - for _,_name in pairs(List) do - if string.match(_name,Text) then - found = true - break + if actinfomenu then + local newentry = actcontroller:NewEntry(_type,self.ActiveInfoMenu) + actcontroller:AddEntry(newentry) end end - return found end + local typelist = self:_GetAvailableTaskTypes() + -- Slot in Tasks + for _tasktype,_data in pairs(typelist) do + self:T("**** Building for TaskType: ".._tasktype) + --local tasks = taskpertype[_tasktype] or {} + for _,_task in pairs(taskpertype[_tasktype]) do + _task = _task -- Ops.PlayerTask#PLAYERTASK + self:T("**** Building for Task: ".._task.Target:GetName()) + if _task.InMenu then + self:T("**** Task already in Menu ".._task.Target:GetName()) + else + --local pilotcount = _task:CountClients() + --local newtext = "]" + --local tnow = timer.getTime() + -- marker for new tasks + --if tnow - _task.timestamp < 60 then + --newtext = "*]" + --end + local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale) + --local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext) + local text = string.format("%s %03d",menutaskno,_task.PlayerTaskNr) + if self.UseGroupNames then + local name = _task.Target:GetName() + if name ~= "Unknown" then + --text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) + text = string.format("%s (%03d)",name,_task.PlayerTaskNr) + end + end + --local taskentry = MENU_GROUP_COMMAND:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task):SetTag(newtag) + local parenttable, number = controller:FindEntriesByText(_tasktype,JoinMenu) + if number > 0 then + local Parent = parenttable[1] + local matches, count = controller:FindEntriesByParent(Parent) + self:T("***** Join Menu ".._tasktype.. " # of entries: "..count) + if count < self.menuitemlimit then + local taskentry = controller:NewEntry(text,Parent,self._JoinTask,self,_task,"false") + controller:AddEntry(taskentry) + _task.InMenu = true + if not _task.UUIDS then _task.UUIDS = {} end + table.insert(_task.UUIDS,taskentry.UUID) + end + end + if self.JoinInfoMenu then + local parenttable, number = controller:FindEntriesByText(_tasktype,self.JoinInfoMenu) + if number > 0 then + local Parent = parenttable[1] + local matches, count = controller:FindEntriesByParent(Parent) + self:T("***** Join Info Menu ".._tasktype.. " # of entries: "..count) + if count < self.menuitemlimit then + local taskentry = controller:NewEntry(text,Parent,self._ActiveTaskInfo,self,_task) + controller:AddEntry(taskentry) + _task.InMenu = true + if not _task.UUIDS then _task.UUIDS = {} end + table.insert(_task.UUIDS,taskentry.UUID) + end + end + end + if actinfomenu then + local parenttable, number = actcontroller:FindEntriesByText(_tasktype,self.ActiveInfoMenu) + if number > 0 then + local Parent = parenttable[1] + local matches, count = actcontroller:FindEntriesByParent(Parent) + self:T("***** Active Info Menu ".._tasktype.. " # of entries: "..count) + if count < self.menuitemlimit then + local taskentry = actcontroller:NewEntry(text,Parent,self._ActiveTaskInfo,self,_task) + actcontroller:AddEntry(taskentry) + _task.InMenu = true + if not _task.AUUIDS then _task.AUUIDS = {} end + table.insert(_task.AUUIDS,taskentry.UUID) + end + end + end + end + end + end end return self end +--- [Internal] _RemoveMenuEntriesForTask +-- @param #PLAYERTASKCONTROLLER self +-- @param #PLAYERTASK Task +-- @param Wrapper.Client#CLIENT Client +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:_RemoveMenuEntriesForTask(Task,Client) + self:T("_RemoveMenuEntriesForTask") + --self:I("Task name: "..Task.Target:GetName()) + --self:I("Client: "..Client:GetPlayerName()) + if Task then + if Task.UUIDS and self.JoinTaskMenuTemplate then + --self:I("***** JoinTaskMenuTemplate") + UTILS.PrintTableToLog(Task.UUIDS) + local controller = self.JoinTaskMenuTemplate + for _,_uuid in pairs(Task.UUIDS) do + local Entry = controller:FindEntryByUUID(_uuid) + if Entry then + controller:DeleteF10Entry(Entry,Client) + controller:DeleteGenericEntry(Entry) + UTILS.PrintTableToLog(controller.menutree) + end + end + end + + if Task.AUUIDS and self.ActiveTaskMenuTemplate then + --self:I("***** ActiveTaskMenuTemplate") + UTILS.PrintTableToLog(Task.AUUIDS) + for _,_uuid in pairs(Task.AUUIDS) do + local controller = self.ActiveTaskMenuTemplate + local Entry = controller:FindEntryByUUID(_uuid) + if Entry then + controller:DeleteF10Entry(Entry,Client) + controller:DeleteGenericEntry(Entry) + UTILS.PrintTableToLog(controller.menutree) + end + end + end + + Task.UUIDS = nil + Task.AUUIDS = nil + end + return self +end + +--- [Internal] _CreateJoinMenuTemplate +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate() - self:I("_CreateActiveTaskMenuTemplate") + self:T("_CreateActiveTaskMenuTemplate") local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale) local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) @@ -3552,7 +3631,7 @@ function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate() end if self.AllowFlash then - JoinTaskMenuTemplate:NewEntry(flashtext,self.JoinTopMenu,self._SwitchFlashing,self,group,client) + JoinTaskMenuTemplate:NewEntry(flashtext,self.JoinTopMenu,self._SwitchFlashing,self) end self.JoinMenu = JoinTaskMenuTemplate:NewEntry(menujoin,self.JoinTopMenu) @@ -3571,9 +3650,11 @@ function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate() return self end - +--- [Internal] _CreateActiveTaskMenuTemplate +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_CreateActiveTaskMenuTemplate() - self:I("_CreateActiveTaskMenuTemplate") + self:T("_CreateActiveTaskMenuTemplate") local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale) local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale) @@ -3583,7 +3664,7 @@ function PLAYERTASKCONTROLLER:_CreateActiveTaskMenuTemplate() local menuillu = self.gettext:GetEntry("MENUILLU",self.locale) local menuabort = self.gettext:GetEntry("MENUABORT",self.locale) - local ActiveTaskMenuTemplate = CLIENTMENUMANAGER:New(self.ClientSet,"ActiveTask") + local ActiveTaskMenuTemplate = CLIENTMENUMANAGER:New(self.ActiveClientSet,"ActiveTask") if not self.ActiveTopMenu then local taskings = self.gettext:GetEntry("MENUTASKING",self.locale) @@ -3594,24 +3675,24 @@ function PLAYERTASKCONTROLLER:_CreateActiveTaskMenuTemplate() if self.AllowFlash then local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) - ActiveTaskMenuTemplate:NewEntry(flashtext,self.ActiveTopMenu,self._SwitchFlashing,self,group,client) + ActiveTaskMenuTemplate:NewEntry(flashtext,self.ActiveTopMenu,self._SwitchFlashing,self) end local active = ActiveTaskMenuTemplate:NewEntry(menuactive,self.ActiveTopMenu) - ActiveTaskMenuTemplate:NewEntry(menuinfo,active,self._ActiveTaskInfo,self,nil) - ActiveTaskMenuTemplate:NewEntry(menumark,active,self._MarkTask,self,nil) + ActiveTaskMenuTemplate:NewEntry(menuinfo,active,self._ActiveTaskInfo,self,"NONE") + ActiveTaskMenuTemplate:NewEntry(menumark,active,self._MarkTask,self) if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A and self.noflaresmokemenu ~= true then - ActiveTaskMenuTemplate:NewEntry(menusmoke,active,self._SmokeTask,self,group,client) - ActiveTaskMenuTemplate:NewEntry(menuflare,active,self._FlareTask,self,group,client) + ActiveTaskMenuTemplate:NewEntry(menusmoke,active,self._SmokeTask,self) + ActiveTaskMenuTemplate:NewEntry(menuflare,active,self._FlareTask,self) if self.illumenu then - ActiveTaskMenuTemplate:NewEntry(menuillu,active,self._IlluminateTask,self,group,client) + ActiveTaskMenuTemplate:NewEntry(menuillu,active,self._IlluminateTask,self) end end - ActiveTaskMenuTemplate:NewEntry(menuabort,active,self._AbortTask,self,group,client) + ActiveTaskMenuTemplate:NewEntry(menuabort,active,self._AbortTask,self) self.ActiveTaskMenuTemplate = ActiveTaskMenuTemplate if self.taskinfomenu and self.activehasinfomenu then @@ -3622,215 +3703,33 @@ function PLAYERTASKCONTROLLER:_CreateActiveTaskMenuTemplate() return self end ---- [Internal] Build client menus +--- [Internal] _SwitchMenuForClient -- @param #PLAYERTASKCONTROLLER self --- @param Wrapper.Client#CLIENT Client (optional) build for this client name only --- @param #boolean enforced --- @param #boolean fromsuccess +-- @param Wrapper.Client#CLIENT Client The client +-- @param #string MenuType +-- @param #number Delay -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) - self:T(self.lid.."_BuildMenus") - - if self.MenuBuildLocked and (timer.getAbsTime() - self.MenuBuildLocked < 2) then - self:ScheduleOnce(2,self._BuildMenus,self,Client,enforced,fromsuccess) +function PLAYERTASKCONTROLLER:_SwitchMenuForClient(Client,MenuType,Delay) + self:T(self.lid.."_SwitchMenuForClient") + if Delay then + self:ScheduleOnce(Delay,self._SwitchMenuForClient,self,Client,MenuType) return self + end + if MenuType == "Info" then + self.ClientSet:AddClientsByName(Client:GetName()) + self.ActiveClientSet:Remove(Client:GetName(),true) + self.ActiveTaskMenuTemplate:ResetMenu(Client) + self.JoinTaskMenuTemplate:ResetMenu(Client) + self.JoinTaskMenuTemplate:Propagate(Client) + elseif MenuType == "Active" then + self.ActiveClientSet:AddClientsByName(Client:GetName()) + self.ClientSet:Remove(Client:GetName(),true) + self.ActiveTaskMenuTemplate:ResetMenu(Client) + self.JoinTaskMenuTemplate:ResetMenu(Client) + self.ActiveTaskMenuTemplate:Propagate(Client) else - self.MenuBuildLocked = timer.getAbsTime() + self:E(self.lid .."Unknown menu type in _SwitchMenuForClient:"..tostring(MenuType)) end - - local clients = self.ClientSet:GetAliveSet() - local joinorabort = false - local timedbuild = false - - if Client then - -- client + enforced -- join task or abort - clients = {Client} - enforced = true - joinorabort = true - end - - local tasktypes = self:_GetAvailableTaskTypes() - local taskpertype = self:_GetTasksPerType() - - for _,_client in pairs(clients) do - if _client and _client:IsAlive() then - local client = _client -- Wrapper.Client#CLIENT - local group = client:GetGroup() - local unknown = self.gettext:GetEntry("UNKNOWN",self.locale) - local playername = client:GetPlayerName() or unknown - - local oldtag = self.PlayerMenuTag[playername] - local newtag = playername..timer.getAbsTime() - self.PlayerMenuTag[playername] = newtag - - if group and client then - --- - -- TOPMENU - --- - local taskings = self.gettext:GetEntry("MENUTASKING",self.locale) - local longname = self.Name..taskings..self.Type - local menuname = self.MenuName or longname - local playerhastask = false - - if self:_CheckPlayerHasTask(playername) and not fromsuccess then playerhastask = true end - local topmenu = nil - --local oldmenu = nil - local rebuilddone = false - - self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort)) - - -- Cases to rebuild menu - -- 1) new player - -- 2) player joined a task, joinorabort = true - -- 3) player left a task, joinorabort = true - -- 4) player has no task, but number of tasks changed, and last build > 30 secs ago - if self.PlayerMenu[playername] then - -- NOT a new player - -- 2)+3) Join or abort? - if joinorabort then - self.PlayerMenu[playername]:RemoveSubMenus() - self.PlayerMenu[playername] = MENU_GROUP:New(group,menuname,self.MenuParent) - self.PlayerMenu[playername]:SetTag(newtag) - topmenu = self.PlayerMenu[playername] - elseif (not playerhastask) or enforced then - -- 4) last build > 30 secs? - local T0 = timer.getAbsTime() - local TDiff = T0-self.PlayerMenu[playername].PTTimeStamp - self:T("TDiff = "..string.format("%.2d",TDiff)) - if TDiff >= self.holdmenutime then - --self.PlayerMenu[playername]:RemoveSubMenus() - --oldmenu = self.PlayerMenu[playername] - --self.PlayerMenu[playername] = nil - - --self.PlayerMenu[playername]:RemoveSubMenus() - --self.PlayerMenu[playername] = MENU_GROUP:New(group,menuname,self.MenuParent) - --self.PlayerMenu[playername]:SetTag(newtag) - --self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime() - --timedbuild = true - end - topmenu = self.PlayerMenu[playername] - end - else - -- 1) new player# - topmenu = MENU_GROUP:New(group,menuname,self.MenuParent) - self.PlayerMenu[playername] = topmenu - self.PlayerMenu[playername]:SetTag(newtag) - self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime() - enforced = true - end - - --- - -- ACTIVE TASK MENU - --- - if playerhastask and enforced then - self:T("Building Active Task Menus for "..playername) - rebuilddone = true - local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale) - local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale) - local menumark = self.gettext:GetEntry("MENUMARK",self.locale) - local menusmoke = self.gettext:GetEntry("MENUSMOKE",self.locale) - local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale) - local menuabort = self.gettext:GetEntry("MENUABORT",self.locale) - - local active = MENU_GROUP:New(group,menuactive,topmenu):SetTag(newtag) - local info = MENU_GROUP_COMMAND:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client):SetTag(newtag) - local mark = MENU_GROUP_COMMAND:New(group,menumark,active,self._MarkTask,self,group,client):SetTag(newtag) - if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then - if self.noflaresmokemenu ~= true then - -- no smoking/flaring here if A2A or designer has set noflaresmokemenu to true - local smoke = MENU_GROUP_COMMAND:New(group,menusmoke,active,self._SmokeTask,self,group,client):SetTag(newtag) - local flare = MENU_GROUP_COMMAND:New(group,menuflare,active,self._FlareTask,self,group,client):SetTag(newtag) - local IsNight = client:GetCoordinate():IsNight() - if IsNight then - local light = MENU_GROUP_COMMAND:New(group,menuflare,active,self._IlluminateTask,self,group,client):SetTag(newtag) - end - end - end - local abort = MENU_GROUP_COMMAND:New(group,menuabort,active,self._AbortTask,self,group,client):SetTag(newtag) - if self.activehasinfomenu and self.taskinfomenu then - self:T("Building Active-Info Menus for "..playername) - if self.PlayerInfoMenu[playername] then - self.PlayerInfoMenu[playername]:RemoveSubMenus(nil,oldtag) - end - self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag) - end - elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask and (timedbuild or joinorabort)) then - self:T("Building Join Menus for "..playername) - rebuilddone = true - --- - -- JOIN TASK MENU - --- - local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale) - - if self.PlayerJoinMenu[playername] then - self.PlayerJoinMenu[playername]:RemoveSubMenus(nil,oldtag) - end - - local joinmenu = MENU_GROUP:New(group,menujoin,topmenu):SetTag(newtag) - self.PlayerJoinMenu[playername] = joinmenu - - local ttypes = {} - local taskmenu = {} - for _tasktype,_data in pairs(tasktypes) do - ttypes[_tasktype] = MENU_GROUP:New(group,_tasktype,joinmenu):SetTag(newtag) - local tasks = taskpertype[_tasktype] or {} - local n = 0 - for _,_task in pairs(tasks) do - _task = _task -- Ops.PlayerTask#PLAYERTASK - local pilotcount = _task:CountClients() - local newtext = "]" - local tnow = timer.getTime() - -- marker for new tasks - if tnow - _task.timestamp < 60 then - newtext = "*]" - end - local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale) - local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext) - if self.UseGroupNames then - local name = _task.Target:GetName() - if name ~= "Unknown" then - text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) - end - end - local taskentry = MENU_GROUP_COMMAND:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task):SetTag(newtag) - --taskentry:SetTag(playername) - taskmenu[#taskmenu+1] = taskentry - n = n + 1 - if n >= self.menuitemlimit then - break - end - end - end - if self.taskinfomenu then - self:T("Building Join-Info Menus for "..playername) - if self.PlayerInfoMenu[playername] then - self.PlayerInfoMenu[playername]:RemoveSubMenus(nil,oldtag) - end - self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag) - end - end - if self.AllowFlash and topmenu ~= nil then - local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) - local flashmenu = MENU_GROUP_COMMAND:New(group,flashtext,topmenu,self._SwitchFlashing,self,group,client):SetTag(newtag) - end - if self.TaskQueue:Count() == 0 then - self:T("No open tasks info") - local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) - local joinmenu = MENU_GROUP:New(group,menunotasks,self.PlayerMenu[playername]):SetTag(newtag) - rebuilddone = true - end - --- - -- REFRESH MENU - --- - if rebuilddone then - --self.PlayerMenu[playername]:RemoveSubMenus(nil,oldtag) - --self.PlayerMenu[playername]:Set() - self.PlayerMenu[playername]:Refresh() - end - end - end - end - self.MenuBuildLocked = false return self end @@ -3978,8 +3877,8 @@ end -- @param Core.Menu#MENU_MISSION Menu -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:SetParentMenu(Menu) - self:T(self.lid.."SetParentName") - self.MenuParent = Menu + self:T(self.lid.."SetParentMenu") + --self.MenuParent = Menu return self end @@ -4140,8 +4039,8 @@ end -- @param #string To -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterStart(From, Event, To) - self:I({From, Event, To}) - self:I(self.lid.."onafterStart") + self:T({From, Event, To}) + self:T(self.lid.."onafterStart") self:_CreateJoinMenuTemplate() self:_CreateActiveTaskMenuTemplate() return self @@ -4176,8 +4075,6 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To) end end - --self:_BuildMenus(nil,enforcedmenu) - self:_UpdateJoinMenuTemplate() if self.verbose then @@ -4225,6 +4122,16 @@ function PLAYERTASKCONTROLLER:onafterTaskCancelled(From, Event, To, Task) taskname = string.format(canceltxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) end + + local clients=Task:GetClientObjects() + for _,client in pairs(clients) do + self:_RemoveMenuEntriesForTask(Task,client) + --self:_SwitchMenuForClient(client,"Info") + end + for _,client in pairs(clients) do + --self:_RemoveMenuEntriesForTask(Task,client) + self:_SwitchMenuForClient(client,"Info",5) + end return self end @@ -4249,9 +4156,15 @@ function PLAYERTASKCONTROLLER:onafterTaskSuccess(From, Event, To, Task) taskname = string.format(succtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) end + local clients=Task:GetClientObjects() for _,client in pairs(clients) do - --self:_BuildMenus(client,true,true) + self:_RemoveMenuEntriesForTask(Task,client) + --self:_SwitchMenuForClient(client,"Info") + end + for _,client in pairs(clients) do + -- self:_RemoveMenuEntriesForTask(Task,client) + self:_SwitchMenuForClient(client,"Info",5) end return self end From 7367e15121731a1c8bc5d3b9ac4983f3244d3ddb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 19 Jul 2023 09:56:09 +0200 Subject: [PATCH 306/603] #AICSAR * Added FSM Event "HeloOnDuty" --- Moose Development/Moose/Functional/AICSAR.lua | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index c9ec13298..b71050778 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -22,7 +22,7 @@ -- === -- -- ### Author: **Applevangelist** --- Last Update February 2022 +-- Last Update July 2023 -- -- === -- @module Functional.AICSAR @@ -191,7 +191,7 @@ -- @field #AICSAR AICSAR = { ClassName = "AICSAR", - version = "0.1.14", + version = "0.1.15", lid = "", coalition = coalition.side.BLUE, template = "", @@ -397,6 +397,7 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) self:AddTransition("*", "PilotRescued", "*") -- Pilot Rescued self:AddTransition("*", "PilotKIA", "*") -- Pilot dead self:AddTransition("*", "HeloDown", "*") -- Helo dead + self:AddTransition("*", "HeloOnDuty", "*") -- Helo spawnd self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:HandleEvent(EVENTS.LandingAfterEjection,self._EventHandler) @@ -473,6 +474,14 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) -- @param #string Event Event. -- @param #string To To state. + --- On after "HeloOnDuty" event. + -- @function [parent=#AICSAR] OnAfterHeloOnDuty + -- @param #AICSAR self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Group#GROUP Helo Helo group object + --- On after "HeloDown" event. -- @function [parent=#AICSAR] OnAfterHeloDown -- @param #AICSAR self @@ -853,6 +862,11 @@ function AICSAR:_GetFlight() local newhelo = SPAWN:NewWithAlias(self.helotemplate,self.helotemplate..math.random(1,10000)) :InitDelayOff() :InitUnControlled(true) + :OnSpawnGroup( + function(Group) + self:__HeloOnDuty(1,Group) + end + ) :Spawn() local nhelo=FLIGHTGROUP:New(newhelo) From 02fadaf17eaeafb3b6917d96cd3348a6bdf9dc37 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 23 Jul 2023 12:38:21 +0200 Subject: [PATCH 307/603] #AIRBASE, #ATIS --- Moose Development/Moose/Ops/ATIS.lua | 16 +++++++++++++--- Moose Development/Moose/Wrapper/Airbase.lua | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 719ad8da7..a7222956b 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -419,6 +419,8 @@ ATIS.RunwayM2T = { TheChannel = -10, Syria = 5, MarianaIslands = 2, + Falklands = 12, + Sinai = 5, } --- Whether ICAO phraseology is used for ATIS broadcasts. @@ -430,6 +432,8 @@ ATIS.RunwayM2T = { -- @field #boolean TheChannel true. -- @field #boolean Syria true. -- @field #boolean MarianaIslands true. +-- @field #boolean Falklands true. +-- @field #boolean Sinai true. ATIS.ICAOPhraseology = { Caucasus = true, Nevada = false, @@ -437,7 +441,9 @@ ATIS.ICAOPhraseology = { PersianGulf = true, TheChannel = true, Syria = true, - MarianaIslands = true + MarianaIslands = true, + Falklands = true, + Sinai = true, } --- Nav point data. @@ -1272,7 +1278,8 @@ end -- @param #string Event Event. -- @param #string To To state. function ATIS:onafterStart( From, Event, To ) - + self:I("Airbase category is "..self.airbase:GetAirbaseCategory()) + -- Check that this is an airdrome. if self.airbase:GetAirbaseCategory() == Airbase.Category.SHIP then self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT SHIPS.", self.airbasename ) ) @@ -1823,7 +1830,10 @@ function ATIS:onafterBroadcast( From, Event, To ) -- Airbase name subtitle = string.format( "%s", self.airbasename ) - if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil then + if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil + and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil + and self.airbasename:find( "Field" ) == nil + then subtitle = subtitle .. " Airport" end if not self.useSRS then diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index b820e28ec..dd52825fa 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -548,6 +548,8 @@ AIRBASE.Syria={ -- * AIRBASE.MarianaIslands.Saipan_Intl -- * AIRBASE.MarianaIslands.Tinian_Intl -- * AIRBASE.MarianaIslands.Olf_Orote +-- * AIRBASE.MarianaIslands.Pagan_Airstrip +-- * AIRBASE.MarianaIslands.North_West_Field -- -- @field MarianaIslands AIRBASE.MarianaIslands = { @@ -557,6 +559,8 @@ AIRBASE.MarianaIslands = { ["Saipan_Intl"] = "Saipan Intl", ["Tinian_Intl"] = "Tinian Intl", ["Olf_Orote"] = "Olf Orote", + ["Pagan_Airstrip"] = "Pagan Airstrip", + ["North_West_Field"] = "North West Field", } --- Airbases of the South Atlantic map: From 1fbadd02f97455d834be7337f26d59d3bb7470d0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 23 Jul 2023 12:41:46 +0200 Subject: [PATCH 308/603] #Updates --- Moose Development/Moose/Core/ClientMenu.lua | 4 ++-- Moose Development/Moose/Ops/PlayerTask.lua | 26 ++++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 74745c217..1f11eebaa 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -381,7 +381,7 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.1.0", + version = "0.1.1", name = nil, clientset = nil, menutree = {}, @@ -670,7 +670,7 @@ end --- Remove the entry and all entries below the given entry from the client's F10 menus. -- @param #CLIENTMENUMANAGER self -- @param #CLIENTMENU Entry The entry to remove --- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. +-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:DeleteF10Entry(Entry,Client) self:T(self.lid.."DeleteF10Entry") diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 196e422d0..d44e9c644 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.17" +PLAYERTASK.version="0.1.18" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -1008,6 +1008,7 @@ do -- @field Core.ClientMenu#CLIENTMENUMANAGER ActiveTaskMenuTemplate -- @field Core.ClientMenu#CLIENTMENU ActiveTopMenu -- @field Core.ClientMenu#CLIENTMENU ActiveInfoMenu +-- @field Core.ClientMenu#CLIENTMENU MenuNoTask -- @extends Core.Fsm#FSM --- @@ -1336,6 +1337,7 @@ PLAYERTASKCONTROLLER = { InfoHasCoordinate = false, UseTypeNames = false, Scoring = nil, + MenuNoTask = nil, } --- @@ -3469,6 +3471,19 @@ function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate() local actinfomenu = self.ActiveInfoMenu --local entrynumbers = {} --local existingentries = {} + + if self.TaskQueue:Count() == 0 and self.MenuNoTask == nil then + local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) + self.MenuNoTask = controller:NewEntry(menunotasks,self.JoinMenu) + controller:AddEntry(self.MenuNoTask) + end + + if self.TaskQueue:Count() > 0 and self.MenuNoTask ~= nil then + controller:DeleteGenericEntry(self.MenuNoTask) + controller:DeleteF10Entry(self.MenuNoTask) + self.MenuNoTask = nil + end + local maxn = self.menuitemlimit -- Generate task type menu items for _type,_ in pairs(taskpertype) do @@ -3641,8 +3656,13 @@ function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate() self.JoinInfoMenu = JoinTaskMenuTemplate:NewEntry(menutaskinfo,self.JoinTopMenu) end - if self.TaskQueue:Count() == 0 then - JoinTaskMenuTemplate:NewEntry(menunotasks,self.JoinMenu) + if self.TaskQueue:Count() == 0 and self.MenuNoTask == nil then + self.MenuNoTask = JoinTaskMenuTemplate:NewEntry(menunotasks,self.JoinMenu) + end + + if self.TaskQueue:Count() > 0 and self.MenuNoTask ~= nil then + JoinTaskMenuTemplate:DeleteGenericEntry(self.MenuNoTask) + self.MenuNoTask = nil end self.JoinTaskMenuTemplate = JoinTaskMenuTemplate From 6fcaccdfb12623f1673c47dee50eafd1c53a8dcf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 25 Jul 2023 12:02:09 +0200 Subject: [PATCH 309/603] * fixes --- Moose Development/Moose/AI/AI_Escort.lua | 52 +++++++++++++++--------- Moose Development/Moose/Core/Menu.lua | 1 + Moose Development/Moose/Core/Set.lua | 5 ++- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Escort.lua b/Moose Development/Moose/AI/AI_Escort.lua index ee84d118f..11ea0294f 100644 --- a/Moose Development/Moose/AI/AI_Escort.lua +++ b/Moose Development/Moose/AI/AI_Escort.lua @@ -147,7 +147,7 @@ -- @image Escorting.JPG - +--- -- @type AI_ESCORT -- @extends AI.AI_Formation#AI_FORMATION @@ -168,11 +168,14 @@ -- -- -- First find the GROUP object and the CLIENT object. -- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. --- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. +-- local EscortGroup = SET_GROUP:New():FilterPrefixes("Escort"):FilterOnce() -- The the group name of the escorts contains "Escort". -- -- -- Now use these 2 objects to construct the new EscortPlanes object. -- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) --- +-- EscortPlanes:MenusAirplanes() -- create menus for airplanes +-- EscortPlanes:__Start(2) +-- +-- -- @field #AI_ESCORT AI_ESCORT = { ClassName = "AI_ESCORT", @@ -211,10 +214,14 @@ AI_ESCORT.Detection = nil -- -- -- First find the GROUP object and the CLIENT object. -- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. --- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. +-- local EscortGroup = SET_GROUP:New():FilterPrefixes("Escort"):FilterOnce() -- The the group name of the escorts contains "Escort". -- -- -- Now use these 2 objects to construct the new EscortPlanes object. -- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) +-- EscortPlanes:MenusAirplanes() -- create menus for airplanes +-- EscortPlanes:__Start(2) +-- +-- function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) local self = BASE:Inherit( self, AI_FORMATION:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) ) -- #AI_ESCORT @@ -227,10 +234,17 @@ function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) self.EscortGroupSet = EscortGroupSet self.EscortGroupSet:SetSomeIteratorLimit( 8 ) - + self.EscortBriefing = EscortBriefing self.Menu = {} + self.Menu.HoldAtEscortPosition = self.Menu.HoldAtEscortPosition or {} + self.Menu.HoldAtLeaderPosition = self.Menu.HoldAtLeaderPosition or {} + self.Menu.Flare = self.Menu.Flare or {} + self.Menu.Smoke = self.Menu.Smoke or {} + self.Menu.Targets = self.Menu.Targets or {} + self.Menu.ROE = self.Menu.ROE or {} + self.Menu.ROT = self.Menu.ROT or {} -- if not EscortBriefing then -- EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " .. @@ -764,7 +778,7 @@ end function AI_ESCORT:SetFlightMenuHoldAtEscortPosition() - for _, MenuHoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition ) do + for _, MenuHoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition or {} ) do local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuHoldPosition = MENU_GROUP_COMMAND @@ -785,7 +799,7 @@ end function AI_ESCORT:SetEscortMenuHoldAtEscortPosition( EscortGroup ) - for _, HoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition ) do + for _, HoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortGroup.EscortMenu ) @@ -853,7 +867,7 @@ end function AI_ESCORT:SetFlightMenuHoldAtLeaderPosition() - for _, MenuHoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition ) do + for _, MenuHoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition or {}) do local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuHoldAtLeaderPosition = MENU_GROUP_COMMAND @@ -874,7 +888,7 @@ end function AI_ESCORT:SetEscortMenuHoldAtLeaderPosition( EscortGroup ) - for _, HoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition ) do + for _, HoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() @@ -999,7 +1013,7 @@ end function AI_ESCORT:SetFlightMenuFlare() - for _, MenuFlare in pairs( self.Menu.Flare) do + for _, MenuFlare in pairs( self.Menu.Flare or {}) do local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuFlare = MENU_GROUP:New( self.PlayerGroup, MenuFlare.MenuText, FlightMenuReportNavigation ) @@ -1014,7 +1028,7 @@ end function AI_ESCORT:SetEscortMenuFlare( EscortGroup ) - for _, MenuFlare in pairs( self.Menu.Flare) do + for _, MenuFlare in pairs( self.Menu.Flare or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() @@ -1059,7 +1073,7 @@ end function AI_ESCORT:SetFlightMenuSmoke() - for _, MenuSmoke in pairs( self.Menu.Smoke) do + for _, MenuSmoke in pairs( self.Menu.Smoke or {}) do local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuSmoke = MENU_GROUP:New( self.PlayerGroup, MenuSmoke.MenuText, FlightMenuReportNavigation ) @@ -1076,7 +1090,7 @@ end function AI_ESCORT:SetEscortMenuSmoke( EscortGroup ) - for _, MenuSmoke in pairs( self.Menu.Smoke) do + for _, MenuSmoke in pairs( self.Menu.Smoke or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() @@ -1169,7 +1183,7 @@ function AI_ESCORT:SetFlightMenuTargets() local FlightMenuAttackNearbyAir = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest airborne targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Air ):SetTag( "Attack" ) local FlightMenuAttackNearbyGround = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest ground targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Ground ):SetTag( "Attack" ) - for _, MenuTargets in pairs( self.Menu.Targets) do + for _, MenuTargets in pairs( self.Menu.Targets or {}) do MenuTargets.FlightReportTargetsScheduler = SCHEDULER:New( self, self._FlightReportTargetsScheduler, {}, MenuTargets.Interval, MenuTargets.Interval ) end @@ -1179,7 +1193,7 @@ end function AI_ESCORT:SetEscortMenuTargets( EscortGroup ) - for _, MenuTargets in pairs( self.Menu.Targets) do + for _, MenuTargets in pairs( self.Menu.Targets or {} or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() --local EscortMenuReportTargets = MENU_GROUP:New( self.PlayerGroup, "Report targets", EscortGroup.EscortMenu ) @@ -1246,7 +1260,7 @@ end function AI_ESCORT:SetFlightMenuROE() - for _, MenuROE in pairs( self.Menu.ROE) do + for _, MenuROE in pairs( self.Menu.ROE or {}) do local FlightMenuROE = MENU_GROUP:New( self.PlayerGroup, "Rule Of Engagement", self.FlightMenu ) local FlightMenuROEHoldFire = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Hold fire", FlightMenuROE, AI_ESCORT._FlightROEHoldFire, self, "Holding weapons!" ) @@ -1261,7 +1275,7 @@ end function AI_ESCORT:SetEscortMenuROE( EscortGroup ) - for _, MenuROE in pairs( self.Menu.ROE) do + for _, MenuROE in pairs( self.Menu.ROE or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() @@ -1302,7 +1316,7 @@ end function AI_ESCORT:SetFlightMenuROT() - for _, MenuROT in pairs( self.Menu.ROT) do + for _, MenuROT in pairs( self.Menu.ROT or {}) do local FlightMenuROT = MENU_GROUP:New( self.PlayerGroup, "Reaction On Threat", self.FlightMenu ) local FlightMenuROTNoReaction = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Fight until death", FlightMenuROT, AI_ESCORT._FlightROTNoReaction, self, "Fighting until death!" ) @@ -1317,7 +1331,7 @@ end function AI_ESCORT:SetEscortMenuROT( EscortGroup ) - for _, MenuROT in pairs( self.Menu.ROT) do + for _, MenuROT in pairs( self.Menu.ROT or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 7209bdac5..255b7f5da 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -207,6 +207,7 @@ do -- MENU_BASE return self end + function MENU_BASE:SetParentMenu( MenuText, Menu ) if self.ParentMenu then self.ParentMenu.Menus = self.ParentMenu.Menus or {} diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index df5a14320..a86a4d0af 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -901,8 +901,11 @@ do -- SET_BASE end -do -- SET_GROUP +do + -- SET_GROUP + + --- -- @type SET_GROUP #SET_GROUP -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval From 6fa90c7f39079d09f69826f77bdb7e530de1ff5e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 25 Jul 2023 12:02:42 +0200 Subject: [PATCH 310/603] #AWACS --- Moose Development/Moose/Ops/Awacs.lua | 147 +++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 5861d097b..5c7256fc8 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -17,7 +17,7 @@ -- === -- -- ### Author: **applevangelist** --- @date Last Update December 2022 +-- @date Last Update July 2023 -- @module Ops.AWACS -- @image OPS_AWACS.jpg @@ -113,6 +113,13 @@ do -- @field Wrapper.Group#GROUP GCIGroup EWR group object for GCI ops -- @field #string locale Localization -- @field #boolean IncludeHelicopters +-- @field #boolean TacticalMenu +-- @field #table TacticalFrequencies +-- @field #table TacticalSubscribers +-- @field #number TacticalBaseFreq +-- @field #number TacticalIncrFreq +-- @field #number TacticalModulation +-- @field #number TacticalInterval -- @extends Core.Fsm#FSM @@ -499,7 +506,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.55", -- #string + version = "0.2.56", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -587,6 +594,13 @@ AWACS = { GCIGroup = nil, locale = "en", IncludeHelicopters = false, + TacticalMenu = false, + TacticalFrequencies = {}, + TacticalSubscribers = {}, + TacticalBaseFreq = 130, + TacticalIncrFreq = 0.5, + TacticalModulation = radio.modulation.AM, + TacticalInterval = 120, } --- @@ -917,7 +931,7 @@ AWACS.TaskStatus = { --@field #boolean FromAI ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO-List 0.2.52 +-- TODO-List 0.2.53 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- -- DONE - WIP - Player tasking, VID @@ -1180,6 +1194,15 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station self.clientmenus = FIFO:New() -- Utilities.FiFo#FIFO + -- Tactical Menu + self.TacticalMenu = false + self.TacticalBaseFreq = 130 + self.TacticalIncrFreq = 0.5 + self.TacticalModulation = radio.modulation.AM + self.acticalFrequencies = {} + self.TacticalSubscribers = {} + self.TacticalInterval = 120 + -- SET for Intel Detection self.DetectionSet=SET_GROUP:New() @@ -1204,6 +1227,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station self:AddTransition("*", "LostCluster", "*") self:AddTransition("*", "LostContact", "*") self:AddTransition("*", "CheckRadioQueue", "*") + self:AddTransition("*", "CheckTacticalQueue", "*") self:AddTransition("*", "EscortShiftChange", "*") self:AddTransition("*", "AwacsShiftChange", "*") self:AddTransition("*", "FlightOnMission", "*") @@ -1354,6 +1378,106 @@ end -- Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- [User] Set the tactical information option, create 10 radio channels groups can subscribe and get Bogey Dope on a specific frequency automatically. +-- @param #AWACS self +-- @param #number BaseFreq Base Frequency to use, defaults to 130. +-- @param #number Increase Increase to use, defaults to 0.5, thus channels created are 130, 130.5, 131 .. etc. +-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM. +-- @param #number Interval Seconds between each update call. +-- @return #AWACS self +function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval) + self:T(self.lid.."SetTacticalRadios") + self.TacticalMenu = true + self.TacticalBaseFreq = BaseFreq or 130 + self.TacticalIncrFreq = Increase or 0.5 + self.TacticalModulation = Modulation or radio.modulation.AM + self.TacticalInterval = Interval or 120 + for i=1,10 do + local freq = self.TacticalBaseFreq + ((i-1)*self.TacticalIncrFreq) + self.TacticalFrequencies[freq] = freq + end + return self +end + + +--- TODO +function AWACS:_RefreshMenuNonSubscribed() + self:T(self.lid.."_RefreshMenuNonSubscribed") + local menustr = self.clientmenus:ReadByID(gname) + local menu = menustr.tactical -- Core.Menu#MENU_GROUP + menu:RemoveSubMenus() + for _,_freq in UTILS.spairs(self.TacticalFrequencies) do + local modu = UTILS.GetModulationName(self.TacticalModulation) + local text = string.format("Subscribe to %.3f %s",_freq,modu) + local entry = MENU_GROUP_COMMAND:New(Group,text,menu,self._SubScribeTactRadio,self,Group,_freq) + end + return self +end + +--- [Internal] _UnsubScribeTactRadio +-- @param #AWACS self +-- @param Wrapper.Group#GROUP Group +-- @return #AWACS self +function AWACS:_UnsubScribeTactRadio(Group) + self:T(self.lid.."_UnsubScribeTactRadio") + local text = "" + local textScreen = "" + local GID, Outcome = self:_GetManagedGrpID(Group) + local gcallsign = self:_GetCallSign(Group,GID) or "Ghost 1" + local gname = Group:GetName() or "unknown" + + if Outcome and self.TacticalSubscribers[gname] then + -- Pilot is checked in + local Freq = self.TacticalSubscribers[gname] + self.TacticalFrequencies[Freq] = Freq + self.TacticalSubscribers[gname] = nil + local modu = self.TacticalModulation == 0 and "AM" or "FM" + text = string.format("%s, %s, switch back to AWACS main frequency!",gcallsign,self.callsigntxt) + self:_NewRadioEntry(text,text,GID,true,true,true,false,true) + self:_RefreshMenuNonSubscribed() + elseif self.AwacsFG then + -- no, unknown + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) + end + return self +end + +--- [Internal] _SubScribeTactRadio +-- @param #AWACS self +-- @param Wrapper.Group#GROUP Group +-- @param #number Frequency +-- @return #AWACS self +function AWACS:_SubScribeTactRadio(Group,Frequency) + self:T(self.lid.."_SubScribeTactRadio") + local text = "" + local textScreen = "" + local GID, Outcome = self:_GetManagedGrpID(Group) + local gcallsign = self:_GetCallSign(Group,GID) or "Ghost 1" + local gname = Group:GetName() or "unknown" + + if Outcome then + -- Pilot is checked in + self.TacticalSubscribers[gname] = Frequency + self.TacticalFrequencies[Frequency] = nil + local modu = self.TacticalModulation == 0 and "AM" or "FM" + text = string.format("%s, %s, switch to %.3f %s for tactical information!",gcallsign,self.callsigntxt,Frequency,modu) + self:_NewRadioEntry(text,text,GID,true,true,true,false,true) + local menustr = self.clientmenus:ReadByID(gname) + local menu = menustr.tactical -- Core.Menu#MENU_GROUP + menu:RemoveSubMenus() + local entry = MENU_GROUP_COMMAND:New(Group,"Unsubscribe",menu,self._UnsubScribeTactRadio,self,Group) + elseif self.AwacsFG then + -- no, unknown + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) + end + + return self +end + --- [Internal] Init localization -- @param #AWACS self -- @return #AWACS self @@ -3614,6 +3738,22 @@ function AWACS:_SetClientMenus() local friendly = MENU_GROUP_COMMAND:New(cgrp,"Friendly",vid,self._VID,self,cgrp,AWACS.IFF.FRIENDLY) end + local tactical + if self.TacticalMenu then + tactical = MENU_GROUP:New(cgrp,"Tactical Radio",basemenu) + if self.TacticalSubscribers[cgrpname] then + -- unsubscribe + local entry = MENU_GROUP_COMMAND:New(cgrp,"Unsubscribe",tactical,self._UnsubScribeTactRadio,self,cgrp) + else + -- subscribe + for _,_freq in UTILS.spairs(self.TacticalFrequencies) do + local modu = UTILS.GetModulationName(self.TacticalModulation) + local text = string.format("Subscribe to %.3f %s",_freq,modu) + local entry = MENU_GROUP_COMMAND:New(cgrp,text,tactical,self._SubScribeTactRadio,self,cgrp,_freq) + end + end + end + local ainfo = MENU_GROUP_COMMAND:New(cgrp,"Awacs Info",basemenu,self._ShowAwacsInfo,self,cgrp) local checkout = MENU_GROUP_COMMAND:New(cgrp,"Check Out",basemenu,self._CheckOut,self,cgrp) @@ -3631,6 +3771,7 @@ function AWACS:_SetClientMenus() unable = unable, abort = abort, commit=commit, + tactical=tactical, } self.clientmenus:PullByID(cgrpname) self.clientmenus:Push(menus,cgrpname) From b9828f3cd157c4360b6bfec2362d213d55926e5c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 25 Jul 2023 14:49:28 +0200 Subject: [PATCH 311/603] #AWACS * Tactical Radioi Option added --- Moose Development/Moose/Ops/Awacs.lua | 178 ++++++++++++++++++++++---- 1 file changed, 153 insertions(+), 25 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 5c7256fc8..f9bd30be8 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -63,6 +63,7 @@ do -- @field Utilities.FiFo#FIFO RadioQueue -- @field Utilities.FiFo#FIFO PrioRadioQueue -- @field Utilities.FiFo#FIFO CAPAirwings +-- @field Utilities.FiFo#FIFO TacticalQueue -- @field #number AwacsTimeOnStation -- @field #number AwacsTimeStamp -- @field #number EscortsTimeOnStation @@ -539,6 +540,7 @@ AWACS = { ContactsAO = {}, -- Utilities.FiFo#FIFO RadioQueue = {}, -- Utilities.FiFo#FIFO PrioRadioQueue = {}, -- Utilities.FiFo#FIFO + TacticalQueue = {}, -- Utilities.FiFo#FIFO AwacsTimeOnStation = 4, AwacsTimeStamp = 0, EscortsTimeOnStation = 4, @@ -1124,6 +1126,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station self.Volume = 1.0 self.RadioQueue = FIFO:New() -- Utilities.FiFo#FIFO self.PrioRadioQueue = FIFO:New() -- Utilities.FiFo#FIFO + self.TacticalQueue = FIFO:New() -- Utilities.FiFo#FIFO self.maxspeakentries = 3 self.GoogleTTSPadding = 1 self.WindowsTTSPadding = 2.5 @@ -1384,32 +1387,65 @@ end -- @param #number Increase Increase to use, defaults to 0.5, thus channels created are 130, 130.5, 131 .. etc. -- @param #number Modulation Modulation to use, defaults to radio.modulation.AM. -- @param #number Interval Seconds between each update call. +-- @param #number Number Number of Frequencies to create, can be 1..10. -- @return #AWACS self -function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval) +function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number) self:T(self.lid.."SetTacticalRadios") self.TacticalMenu = true self.TacticalBaseFreq = BaseFreq or 130 self.TacticalIncrFreq = Increase or 0.5 self.TacticalModulation = Modulation or radio.modulation.AM self.TacticalInterval = Interval or 120 - for i=1,10 do + local number = Number or 10 + if number < 1 then number = 1 end + if number > 10 then number = 10 end + for i=1,number do local freq = self.TacticalBaseFreq + ((i-1)*self.TacticalIncrFreq) self.TacticalFrequencies[freq] = freq end + if self.AwacsSRS then + self.TacticalSRS = MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation,self.Volume) + self.TacticalSRS:SetCoalition(self.coalition) + self.TacticalSRS:SetGender(self.Gender) + self.TacticalSRS:SetCulture(self.Culture) + self.TacticalSRS:SetVoice(self.Voice) + self.TacticalSRS:SetPort(self.Port) + self.TacticalSRS:SetLabel("AWACS") + if self.PathToGoogleKey then + self.TacticalSRS:SetGoogle(self.PathToGoogleKey) + end + self.TacticalSRSQ = MSRSQUEUE:New("Tactical AWACS") + end return self end - --- TODO +-- [Internal] _RefreshMenuNonSubscribed +-- @param #AWACS self +-- @return #AWACS self function AWACS:_RefreshMenuNonSubscribed() self:T(self.lid.."_RefreshMenuNonSubscribed") - local menustr = self.clientmenus:ReadByID(gname) - local menu = menustr.tactical -- Core.Menu#MENU_GROUP - menu:RemoveSubMenus() - for _,_freq in UTILS.spairs(self.TacticalFrequencies) do - local modu = UTILS.GetModulationName(self.TacticalModulation) - local text = string.format("Subscribe to %.3f %s",_freq,modu) - local entry = MENU_GROUP_COMMAND:New(Group,text,menu,self._SubScribeTactRadio,self,Group,_freq) + local aliveset = self.clientset:GetAliveSet() + + for _,_group in pairs(aliveset) do + -- go through set and re-build the sub-menu + local grp = _group -- Wrapper.Client#CLIENT + local Group = grp:GetGroup() + local gname = nil + if Group and Group:IsAlive() then + gname = Group:GetName() + self:T(gname) + end + local menustr = self.clientmenus:ReadByID(gname) + local menu = menustr.tactical -- Core.Menu#MENU_GROUP + if not self.TacticalSubscribers[gname] and menu then + menu:RemoveSubMenus() + for _,_freq in UTILS.spairs(self.TacticalFrequencies) do + local modu = UTILS.GetModulationName(self.TacticalModulation) + local text = string.format("Subscribe to %.3f %s",_freq,modu) + local entry = MENU_GROUP_COMMAND:New(Group,text,menu,self._SubScribeTactRadio,self,Group,_freq) + end + end end return self end @@ -1466,8 +1502,11 @@ function AWACS:_SubScribeTactRadio(Group,Frequency) self:_NewRadioEntry(text,text,GID,true,true,true,false,true) local menustr = self.clientmenus:ReadByID(gname) local menu = menustr.tactical -- Core.Menu#MENU_GROUP - menu:RemoveSubMenus() - local entry = MENU_GROUP_COMMAND:New(Group,"Unsubscribe",menu,self._UnsubScribeTactRadio,self,Group) + if menu then + menu:RemoveSubMenus() + local text = string.format("Unsubscribe %.3f %s",Frequency,modu) + local entry = MENU_GROUP_COMMAND:New(Group,text,menu,self._UnsubScribeTactRadio,self,Group) + end elseif self.AwacsFG then -- no, unknown local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) @@ -1550,8 +1589,9 @@ end -- @param #boolean IsNew New -- @param #boolean FromAI From AI -- @param #boolean IsPrio Priority entry +-- @param #boolean Tactical Is for tactical info -- @return #AWACS self -function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,FromAI,IsPrio) +function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,FromAI,IsPrio,Tactical) self:T(self.lid.."_NewRadioEntry") local RadioEntry = {} -- #AWACS.RadioEntry RadioEntry.IsNew = IsNew @@ -1562,7 +1602,9 @@ function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,Fro RadioEntry.Duration = STTS.getSpeechTime(TextTTS,0.95,false) or 8 RadioEntry.FromAI = FromAI RadioEntry.IsGroup = IsGroup - if IsPrio then + if Tactical then + self.TacticalQueue:Push(RadioEntry) + elseif IsPrio then self.PrioRadioQueue:Push(RadioEntry) else self.RadioQueue:Push(RadioEntry) @@ -2813,8 +2855,9 @@ end -- @param #AWACS self -- @param #string Callsign Callsign to address -- @param #number GID GroupID for comms +-- @param #boolean Tactical Is for tactical info -- @return #AWACS self -function AWACS:_CreateBogeyDope(Callsign,GID) +function AWACS:_CreateBogeyDope(Callsign,GID,Tactical) self:T(self.lid.."_CreateBogeyDope for "..Callsign.." GID "..GID) local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup @@ -2840,7 +2883,7 @@ function AWACS:_CreateBogeyDope(Callsign,GID) local tag = contact.TargetGroupNaming local reportingname = contact.ReportingName -- DONE - add tag - self:_AnnounceContact(contact,false,group,true,tag,false,reportingname) + self:_AnnounceContact(contact,false,group,true,tag,false,reportingname,Tactical) end end @@ -2965,8 +3008,9 @@ end --- [Internal] AWACS Menu for Bogey Dope -- @param #AWACS self -- @param Wrapper.Group#GROUP Group Group to use +-- @param #boolean Tactical Check for tactical info -- @return #AWACS self -function AWACS:_BogeyDope(Group) +function AWACS:_BogeyDope(Group,Tactical) self:T(self.lid.."_BogeyDope") local text = "" local textScreen = "" @@ -2977,8 +3021,7 @@ function AWACS:_BogeyDope(Group) -- no intel yet! local clean = self.gettext:GetEntry("CLEAN",self.locale) text = string.format(clean,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - self:_NewRadioEntry(text,text,0,false,true,true,false,true) - + self:_NewRadioEntry(text,text,0,false,true,true,false,true,Tactical) return self end @@ -3020,7 +3063,7 @@ function AWACS:_BogeyDope(Group) local clean = self.gettext:GetEntry("CLEAN",self.locale) text = string.format(clean,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - self:_NewRadioEntry(text,textScreen,GID,Outcome,Outcome,true,false,true) + self:_NewRadioEntry(text,textScreen,GID,Outcome,Outcome,true,false,true,Tactical) else @@ -3039,9 +3082,9 @@ function AWACS:_BogeyDope(Group) textScreen = string.format("%s%d %s.\n",textScreen,contactsAO,groupstxt) end - self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false,true) + self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false,true,Tactical) - self:_CreateBogeyDope(self:_GetCallSign(Group,GID) or "Ghost 1",GID) + self:_CreateBogeyDope(self:_GetCallSign(Group,GID) or "Ghost 1",GID,Tactical) end end @@ -3049,7 +3092,7 @@ function AWACS:_BogeyDope(Group) -- no, unknown local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) + self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,Tactical) end return self @@ -4953,8 +4996,9 @@ end -- @param #string Tag Tag name for this contact. Alpha, Brave, Charlie ... -- @param #boolean IsPopup This is a pop-up group -- @param #string ReportingName The NATO code reporting name for the contact, e.g. "Foxbat". "Bogey" if unknown. +-- @param #boolean Tactical -- @return #AWACS self -function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,ReportingName) +function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,ReportingName,Tactical) self:T(self.lid.."_AnnounceContact") -- do we have a group to talk to? local tag = "" @@ -5081,7 +5125,7 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo BRAText = string.gsub(BRAText,"BRA","brah") local prio = IsNew or IsBogeyDope - self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio) + self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio,Tactical) return self end @@ -5469,6 +5513,15 @@ function AWACS:_MergedCall(GID) local merge = self.gettext:GetEntry("MERGED",self.locale) local text = string.format("%s. %s. %s.",self.callsigntxt,pilotcallsign,merge) self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + if GID and GID ~= 0 then + local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup + if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then + local name = managedgroup.GroupName + if self.TacticalSubscribers[name] then + self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true,true) + end + end + end return self end @@ -5844,6 +5897,10 @@ function AWACS:onafterStart(From, Event, To) self:__Started(-5) end + if self.TacticalMenu then + self:__CheckTacticalQueue(55) + end + self:__Status(-30) return self end @@ -6457,6 +6514,77 @@ function AWACS:onafterLostCluster(From,Event,To,Cluster,Mission) return self end +--- [Internal] onafterCheckTacticalQueue +-- @param #AWACS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #AWACS self +function AWACS:onafterCheckTacticalQueue(From,Event,To) + self:T({From, Event, To}) + -- do we have messages queued? + + if self.clientset:CountAlive() == 0 then + self:T(self.lid.."No player connected.") + self:__CheckTacticalQueue(-5) + return self + end + + for _name,_freq in pairs(self.TacticalSubscribers) do + local Group = nil + if _name then + Group = GROUP:FindByName(_name) + end + if Group and Group:IsAlive() then + self:_BogeyDope(Group,true) + end + end + + if (self.TacticalQueue:IsNotEmpty()) then + + while self.TacticalQueue:Count() > 0 do + + local RadioEntry = self.TacticalQueue:Pull() -- #AWACS.RadioEntry + self:T({RadioEntry}) + local frequency = self.TacticalBaseFreq + if RadioEntry.GroupID and RadioEntry.GroupID ~= 0 then + local managedgroup = self.ManagedGrps[RadioEntry.GroupID] -- #AWACS.ManagedGroup + if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then + local name = managedgroup.GroupName + frequency = self.TacticalSubscribers[name] + end + end + -- AI AWACS Speaking + local gtext = RadioEntry.TextTTS + if self.PathToGoogleKey then + gtext = string.format("%s",gtext) + end + self.TacticalSRSQ:NewTransmission(gtext,nil,self.TacticalSRS,nil,0.5,nil,nil,nil,frequency,self.TacticalModulation,nil,nil,nil,nil,nil) + + self:T(RadioEntry.TextTTS) + + if RadioEntry.ToScreen and RadioEntry.TextScreen and (not self.SuppressScreenOutput) then + if RadioEntry.GroupID and RadioEntry.GroupID ~= 0 then + local managedgroup = self.ManagedGrps[RadioEntry.GroupID] -- #AWACS.ManagedGroup + if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then + MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToGroup(managedgroup.Group) + self:T(RadioEntry.TextScreen) + end + else + MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToCoalition(self.coalition) + end + end + end + + end -- end while + + if self:Is("Running") then + self:__CheckTacticalQueue(-self.TacticalInterval) + end + return self +end + + --- [Internal] onafterCheckRadioQueue -- @param #AWACS self -- @param #string From From 9a695ef5bfb4b826331d795120d12647348a7e61 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 26 Jul 2023 07:49:52 +0200 Subject: [PATCH 312/603] #AWACS --- Moose Development/Moose/Ops/Awacs.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index f9bd30be8..ff57e7ad9 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -507,7 +507,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.56", -- #string + version = "0.2.57", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -1784,7 +1784,7 @@ function AWACS:_EventHandler(EventData) if WeaponDesc.category == 1 then Type = "Missile" -- AAM - local guidance = WeaponDesc.guidance -- IR=2, Radar Active=3, Radar Semi Active=4, Radar Passive = 5 + local guidance = WeaponDesc.guidance or 4 -- IR=2, Radar Active=3, Radar Semi Active=4, Radar Passive = 5 if guidance == 2 then warndist = 10 elseif guidance == 3 then @@ -3695,11 +3695,17 @@ function AWACS:_CheckOut(Group,GID,dead) local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup local Stack = managedgroup.AnchorStackNo local Angels = managedgroup.AnchorStackAngels + local GroupName = managedgroup.GroupName -- remove menus if managedgroup.IsPlayer then - if self.clientmenus:HasUniqueID(managedgroup.GroupName) then - local menus = self.clientmenus:PullByID(managedgroup.GroupName) --#AWACS.MenuStructure + if self.clientmenus:HasUniqueID(GroupName) then + local menus = self.clientmenus:PullByID(GroupName) --#AWACS.MenuStructure menus.basemenu:Remove() + if self.TacticalSubscribers[GroupName] then + local Freq = self.TacticalSubscribers[GroupName] + self.TacticalFrequencies[Freq] = Freq + self.TacticalSubscribers[GroupName] = nil + end end end -- delete open tasks From 9c95b91086f21f597d5e27abb329fa367d0bad19 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 29 Jul 2023 15:56:20 +0200 Subject: [PATCH 313/603] #changes --- Moose Development/Moose/Ops/ATIS.lua | 45 +-- Moose Development/Moose/Ops/Airboss.lua | 348 +++++++++----------- Moose Development/Moose/Ops/FlightGroup.lua | 9 + 3 files changed, 196 insertions(+), 206 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index a7222956b..c604c281e 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1278,7 +1278,8 @@ end -- @param #string Event Event. -- @param #string To To state. function ATIS:onafterStart( From, Event, To ) - self:I("Airbase category is "..self.airbase:GetAirbaseCategory()) + self:T({From, Event, To}) + self:T("Airbase category is "..self.airbase:GetAirbaseCategory()) -- Check that this is an airdrome. if self.airbase:GetAirbaseCategory() == Airbase.Category.SHIP then @@ -1340,7 +1341,7 @@ end -- @param #string Event Event. -- @param #string To To state. function ATIS:onafterStatus( From, Event, To ) - + self:T({From, Event, To}) -- Get FSM state. local fsmstate = self:GetState() @@ -1362,7 +1363,7 @@ function ATIS:onafterStatus( From, Event, To ) self:T( self.lid .. text ) if not self:Is("Stopped") then - self:__Status( -60 ) + self:__Status( 60 ) end end @@ -1376,25 +1377,26 @@ end -- @param #string Event Event. -- @param #string To To state. function ATIS:onafterCheckQueue( From, Event, To ) - - if self.useSRS then - - self:Broadcast() - - else - - if #self.radioqueue.queue == 0 then - self:T( self.lid .. string.format( "Radio queue empty. Repeating message." ) ) - self:Broadcast() - else - self:T2( self.lid .. string.format( "Radio queue %d transmissions queued.", #self.radioqueue.queue ) ) - end - - end - + self:T({From, Event, To}) if not self:Is("Stopped") then + if self.useSRS then + + self:Broadcast() + + else + + if #self.radioqueue.queue == 0 then + self:T( self.lid .. string.format( "Radio queue empty. Repeating message." ) ) + self:Broadcast() + else + self:T2( self.lid .. string.format( "Radio queue %d transmissions queued.", #self.radioqueue.queue ) ) + end + + end + + -- Check back in 5 seconds. - self:__CheckQueue( -math.abs( self.dTQueueCheck ) ) + self:__CheckQueue( math.abs( self.dTQueueCheck ) ) end end @@ -1404,7 +1406,7 @@ end -- @param #string Event Event. -- @param #string To To state. function ATIS:onafterBroadcast( From, Event, To ) - + self:T({From, Event, To}) -- Get current coordinate. local coord = self.airbase:GetCoordinate() @@ -2417,6 +2419,7 @@ end -- @param #string To To state. -- @param #string Text Report text. function ATIS:onafterReport( From, Event, To, Text ) + self:T({From, Event, To}) self:T( self.lid .. string.format( "Report:\n%s", Text ) ) if self.useSRS and self.msrs then diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index c786ea4e9..96828dc05 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -152,12 +152,16 @@ -- @field #boolean ICLSon Automatic ICLS is activated. -- @field #number ICLSchannel ICLS channel. -- @field #string ICLSmorse ICLS morse code, e.g. "STN". +-- @field #AIRBOSS.Radio PilotRadio Radio for Pilot calls. -- @field #AIRBOSS.Radio LSORadio Radio for LSO calls. -- @field #number LSOFreq LSO radio frequency in MHz. -- @field #string LSOModu LSO radio modulation "AM" or "FM". -- @field #AIRBOSS.Radio MarshalRadio Radio for carrier calls. -- @field #number MarshalFreq Marshal radio frequency in MHz. -- @field #string MarshalModu Marshal radio modulation "AM" or "FM". +-- @field #AIRBOSS.Radio AirbossRadio Radio for carrier calls. +-- @field #number AirbossFreq Airboss radio frequency in MHz. +-- @field #string AirbossModu Airboss radio modulation "AM" or "FM". -- @field #number TowerFreq Tower radio frequency in MHz. -- @field Core.Scheduler#SCHEDULER radiotimer Radio queue scheduler. -- @field Core.Zone#ZONE_UNIT zoneCCA Carrier controlled area (CCA), i.e. a zone of 50 NM radius around the carrier. @@ -1731,10 +1735,10 @@ AIRBOSS.Difficulty = { -- @field #table trapsheet Groove data table recorded every 0.5 seconds. -- @field #boolean trapon If true, save trap sheets. -- @field #string debriefschedulerID Debrief scheduler ID. --- +-- -- @field Sound.SRS#MSRS SRS -- @field Sound.SRS#MSRSQUEUE SRSQ --- +-- -- @extends #AIRBOSS.FlightGroup --- Main group level radio menu: F10 Other/Airboss. @@ -1880,6 +1884,7 @@ function AIRBOSS:New( carriername, alias ) -- Set up Airboss radio. self:SetMarshalRadio() + self:SetAirbossRadio() -- Set up LSO radio. self:SetLSORadio() @@ -3053,7 +3058,7 @@ end -- @param #number Port Port of the SRS server, defaults to 5002. -- @param #string Culture (Optional, Airboss Culture) Culture, defaults to "en-US". -- @param #string Gender (Optional, Airboss Gender) Gender, e.g. "male" or "female". Defaults to "male". --- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings. +-- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings. -- @param #string GoogleCreds (Optional) Path to Google credentials, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourgooglekey.json". -- @param #number Volume (Optional) E.g. 0.75. Defaults to 1.0 (loudest). -- @param #table AltBackend (Optional) See MSRS for details. @@ -3082,16 +3087,19 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum -- SRSQUEUE self.SRSQ = MSRSQUEUE:New("AIRBOSS") self.SRSQ:SetTransmitOnlyWithPlayers(true) - if not self.PilotRadio then + if not self.PilotRadio then self:SetSRSPilotVoice() end - return self + return self end --- Set LSO radio frequency and modulation. Default frequency is 264 MHz AM. -- @param #AIRBOSS self -- @param #number Frequency (Optional) Frequency in MHz. Default 264 MHz. -- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM". +-- @param #string Voice (Optional) SRS specific voice +-- @param #string Gender (Optional) SRS specific gender +-- @param #string Culture (Optional) SRS specific culture -- @return #AIRBOSS self function AIRBOSS:SetLSORadio( Frequency, Modulation, Voice, Gender, Culture ) @@ -3108,6 +3116,9 @@ function AIRBOSS:SetLSORadio( Frequency, Modulation, Voice, Gender, Culture ) self.LSORadio.frequency = self.LSOFreq self.LSORadio.modulation = self.LSOModu self.LSORadio.alias = "LSO" + self.LSORadio.voice = Voice + self.LSORadio.gender = Gender or "male" + self.LSORadio.culture = Culture or "en-US" return self end @@ -3156,6 +3167,9 @@ end -- @param #AIRBOSS self -- @param #number Frequency (Optional) Frequency in MHz. Default 305 MHz. -- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM". +-- @param #string Voice (Optional) SRS specific voice +-- @param #string Gender (Optional) SRS specific gender +-- @param #string Culture (Optional) SRS specific culture -- @return #AIRBOSS self function AIRBOSS:SetMarshalRadio( Frequency, Modulation, Voice, Gender, Culture ) @@ -3172,6 +3186,9 @@ function AIRBOSS:SetMarshalRadio( Frequency, Modulation, Voice, Gender, Culture self.MarshalRadio.frequency = self.MarshalFreq self.MarshalRadio.modulation = self.MarshalModu self.MarshalRadio.alias = "MARSHAL" + self.MarshalRadio.voice = Voice + self.MarshalRadio.gender = Gender or "male" + self.MarshalRadio.culture = Culture or "en-US" return self end @@ -3353,7 +3370,7 @@ function AIRBOSS:SetExtraVoiceOversAI(status) self.xtVoiceOversAI=status return self end - + --- Do not handle AI aircraft. -- @param #AIRBOSS self -- @return #AIRBOSS self @@ -3468,9 +3485,9 @@ end -- @param #string Host Host. Default `"127.0.0.1"`. -- @return #AIRBOSS self function AIRBOSS:SetFunkManOn(Port, Host) - + self.funkmanSocket=SOCKET:New(Port, Host) - + return self end @@ -5442,6 +5459,7 @@ function AIRBOSS:_GetAircraftParameters( playerData, step ) local skyhawk = playerData.actype == AIRBOSS.AircraftCarrier.A4EC local tomcat = playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B local harrier = playerData.actype == AIRBOSS.AircraftCarrier.AV8B + local goshawk = playerData.actype == AIRBOSS.AircraftCarrier.T45C -- Return values. local alt @@ -5907,6 +5925,7 @@ function AIRBOSS:_ScanCarrierZone() -- Get aircraft type name. local actype = group:GetTypeName() + -- Create a new flight group if knownflight then -- Debug output. @@ -6452,7 +6471,7 @@ function AIRBOSS:_LandAI( flight ) -- Aircraft speed when flying the pattern. local Speed = UTILS.KnotsToKmph( 200 ) - if flight.actype == AIRBOSS.AircraftCarrier.HORNET + if flight.actype == AIRBOSS.AircraftCarrier.HORNET or flight.actype == AIRBOSS.AircraftCarrier.FA18C or flight.actype == AIRBOSS.AircraftCarrier.RHINOE or flight.actype == AIRBOSS.AircraftCarrier.RHINOF @@ -8190,7 +8209,7 @@ end -- @param Core.Event#EVENTDATA EventData function AIRBOSS:OnEventBirth( EventData ) self:F3( { eventbirth = EventData } ) - + -- Nil checks. if EventData == nil then self:E( self.lid .. "ERROR: EventData=nil in event BIRTH!" ) @@ -8202,9 +8221,9 @@ function AIRBOSS:OnEventBirth( EventData ) self:E( EventData ) return end - + if EventData.IniObjectCategory ~= Object.Category.UNIT then return end - + local _unitName = EventData.IniUnitName local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) @@ -9282,9 +9301,9 @@ function AIRBOSS:_DirtyUp( playerData ) self:_PlayerHint( playerData ) -- Radio call "Say/Fly needles". Delayed by 10/15 seconds. - if playerData.actype == AIRBOSS.AircraftCarrier.HORNET - or playerData.actype == AIRBOSS.AircraftCarrier.F14A - or playerData.actype == AIRBOSS.AircraftCarrier.F14B + if playerData.actype == AIRBOSS.AircraftCarrier.HORNET + or playerData.actype == AIRBOSS.AircraftCarrier.F14A + or playerData.actype == AIRBOSS.AircraftCarrier.F14B or playerData.actype == AIRBOSS.AircraftCarrier.RHINOE or playerData.actype == AIRBOSS.AircraftCarrier.RHINOF or playerData.actype == AIRBOSS.AircraftCarrier.GROWLER @@ -9881,24 +9900,24 @@ function AIRBOSS:_Groove( playerData ) return end - end - + end + -- Long V/STOL groove time Wave Off over 75 seconds to IC - TOPGUN level Only. --pene testing (WIP)--- Need to think more about this. - + --if rho>=RAR and rho<=RIC and not playerData.waveoff and playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype== AIRBOSS.AircraftCarrier.AV8B then -- Get groove time --local vSlow=groovedata.time - -- If too slow wave off. + -- If too slow wave off. --if vSlow >75 then - + -- LSO Wave off! --self:RadioTransmission(self.LSORadio, self.LSOCall.WAVEOFF, nil, nil, nil, true) --playerData.Tlso=timer.getTime() - + -- Player was waved Off --playerData.waveoff=true --return - --end + --end --end -- Groovedata step. @@ -10096,7 +10115,7 @@ function AIRBOSS:_CheckWaveOff( glideslopeError, lineupError, AoA, playerData ) waveoff = true end - -- Too slow or too fast? Only for pros. + -- Too slow or too fast? Only for pros. if playerData.difficulty == AIRBOSS.Difficulty.HARD and playerData.actype ~= AIRBOSS.AircraftCarrier.AV8B then -- Get aircraft specific AoA values. Not for AV-8B due to transition to Stable Hover. @@ -10252,7 +10271,7 @@ function AIRBOSS:_GetSternCoord() elseif case==2 or case==1 then -- V/Stol: Translate 8 meters port. self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true) - end + end elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then -- Stennis: translate 7 meters starboard wrt Final bearing. self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 7, FB + 90, true, true ) @@ -10401,7 +10420,7 @@ function AIRBOSS:_Trapped( playerData ) -- Get current wire (estimate). This now based on the position where the player comes to a standstill which should reflect the trapped wire better. local dcorr = 100 - if playerData.actype == AIRBOSS.AircraftCarrier.HORNET + if playerData.actype == AIRBOSS.AircraftCarrier.HORNET or playerData.actype == AIRBOSS.AircraftCarrier.RHINOE or playerData.actype == AIRBOSS.AircraftCarrier.RHINOF or playerData.actype == AIRBOSS.AircraftCarrier.GROWLER then @@ -10926,7 +10945,7 @@ function AIRBOSS:_GetZoneAbeamLandingSpot() -- Coordinate array. Pene Testing extended Abeam landing spot V/STOL. local p={} - + -- Points. p[1] = S:Translate( 15, FB ):Translate( 15, FB + 90 ) -- Top-Right p[2] = S:Translate( -45, FB ):Translate( 15, FB + 90 ) -- Bottom-Right @@ -11374,7 +11393,7 @@ function AIRBOSS:_GetOptLandingCoordinate() -- set Case III V/STOL abeam landing spot over deck -- Pene Testing if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then - + if case==3 then -- Landing coordinate. @@ -11382,7 +11401,7 @@ function AIRBOSS:_GetOptLandingCoordinate() -- Altitude 120ft -- is this corect for Case III? self.landingcoord:SetAltitude(UTILS.FeetToMeters(120)) - + elseif case==2 or case==1 then -- Landing 100 ft abeam, 120 ft alt. @@ -11392,7 +11411,7 @@ function AIRBOSS:_GetOptLandingCoordinate() self.landingcoord:SetAltitude(UTILS.FeetToMeters(120)) end - + else -- Ideally we want to land between 2nd and 3rd wire. @@ -11536,14 +11555,10 @@ end -- @return #number Carrier heading in degrees. function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) -<<<<<<< HEAD - -- Get direction the wind is blowing from. This is where we want to go. - local windfrom, vwind = self:GetWind( nil, nil, coord ) -======= local function adjustDegreesForWindSpeed(windSpeed) local degreesAdjustment = 0 -- the windspeeds are in m/s - + -- +0 degrees at 15m/s = 37kts -- +0 degrees at 14m/s = 35kts -- +0 degrees at 13m/s = 33kts @@ -11558,7 +11573,7 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) -- +20 degrees at 4m/s = 26kts -- +20 degrees at 3m/s = 26kts -- +30 degrees at 2m/s = 26kts 1s - + if windSpeed > 0 and windSpeed < 3 then degreesAdjustment = 30 elseif windSpeed >= 3 and windSpeed < 5 then @@ -11570,7 +11585,7 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) elseif windSpeed >= 13 then degreesAdjustment = 0 end - + return degreesAdjustment end @@ -11582,7 +11597,6 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) vwind = vwind + adjustDegreesForWindSpeed(vwind) --self:I("windfrom="..windfrom.." (c)vwind="..vwind) ->>>>>>> origin/develop -- Actually, we want the runway in the wind. local intowind = windfrom - self.carrierparam.rwyangle @@ -12032,7 +12046,7 @@ function AIRBOSS:_LSOgrade( playerData ) local nS=count(G, '%(') local nN=N-nS-nL local nNv=Nv-nS-nL - + -- Groove time 15-18.99 sec for a unicorn. Or 60-65 for V/STOL unicorn. local Tgroove=playerData.Tgroove local TgrooveUnicorn=Tgroove and (Tgroove>=15.0 and Tgroove<=18.99) or false @@ -12149,7 +12163,7 @@ function AIRBOSS:_LSOgrade( playerData ) elseif not playerData.hover and playerData.actype == AIRBOSS.AircraftCarrier.AV8B then ------------------------------- - -- AV-8B not cleared to land -- -- Landing clearence is carrier from LC to Landing + -- AV-8B not cleared to land -- -- Landing clearence is carrier from LC to Landing ------------------------------- if playerData.landed then -- AIRBOSS wants your balls! @@ -13517,8 +13531,8 @@ function AIRBOSS:CarrierTurnIntoWind( time, vdeck, uturn ) -- Wind speed. local _, vwind = self:GetWind() - -- Speed of carrier in m/s but at least 2 knots. - local vtot = math.max( vdeck - vwind, UTILS.KnotsToMps( 2 ) ) + -- Speed of carrier in m/s but at least 4 knots. + local vtot = math.max( vdeck - vwind, UTILS.KnotsToMps( 4 ) ) -- Distance to travel local dist = vtot * time @@ -14721,29 +14735,12 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p if radio == nil or call == nil then return end -<<<<<<< HEAD - - -- Create a new radio transmission item. - local transmission = {} -- #AIRBOSS.Radioitem - - transmission.radio = radio - transmission.call = call - transmission.Tplay = timer.getAbsTime() + (delay or 0) - transmission.interval = interval - transmission.isplaying = false - transmission.Tstarted = nil - transmission.loud = loud and call.loud - - -- Player onboard number if sender has one. - if self:_IsOnboard( call.modexsender ) then - self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall ) -======= - + if not self.SRS then - + -- Create a new radio transmission item. local transmission = {} -- #AIRBOSS.Radioitem - + transmission.radio = radio transmission.call = call transmission.Tplay = timer.getAbsTime() + (delay or 0) @@ -14751,58 +14748,58 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p transmission.isplaying = false transmission.Tstarted = nil transmission.loud = loud and call.loud - + -- Player onboard number if sender has one. if self:_IsOnboard( call.modexsender ) then self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall ) end - + -- Play onboard number if receiver has one. if self:_IsOnboard( call.modexreceiver ) then self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall ) end - + -- Add transmission to the right queue. local caller = "" if radio.alias == "LSO" then - + table.insert( self.RQLSO, transmission ) - + caller = "LSOCall" - + -- Schedule radio queue checks. if not self.RQLid then self:T( self.lid .. string.format( "Starting LSO radio queue." ) ) self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 ) end - + elseif radio.alias == "MARSHAL" then - + table.insert( self.RQMarshal, transmission ) - + caller = "MarshalCall" - + if not self.RQMid then self:T( self.lid .. string.format( "Starting Marhal radio queue." ) ) self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 ) end - + end - + -- Append radio click sound at the end of the transmission. if click then self:RadioTransmission( radio, self[caller].CLICK, false, delay ) end - + else -- SRS transmission - + local frequency = self.MarshalRadio.frequency local modulation = self.MarshalRadio.modulation local voice = nil local gender = nil local culture = nil - + if radio.alias == "AIRBOSS" then frequency = self.AirbossRadio.frequency modulation = self.AirbossRadio.modulation @@ -14810,13 +14807,13 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p gender = self.AirbossRadio.gender culture = self.AirbossRadio.culture end - + if radio.alias == "MARSHAL" then voice = self.MarshalRadio.voice gender = self.MarshalRadio.gender culture = self.MarshalRadio.culture end - + if radio.alias == "LSO" then frequency = self.LSORadio.frequency modulation = self.LSORadio.modulation @@ -14824,7 +14821,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p gender = self.LSORadio.gender culture = self.LSORadio.culture end - + if pilotcall then voice = self.PilotRadio.voice gender = self.PilotRadio.gender @@ -14838,59 +14835,21 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p modulation = self.AirbossRadio.modulation radio.alias = "AIRBOSS" end - + local volume = nil - + if loud then volume = 1.0 end - + --local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle local text = call.subtitle - self:I(self.lid..text) + self:I(self.lid..text) local srstext = self:_GetNiceSRSText(text) - self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, tstart, 0.1, subgroups, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) ->>>>>>> origin/develop + self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, nil, 0.1, nil, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) end +end -<<<<<<< HEAD - -- Play onboard number if receiver has one. - if self:_IsOnboard( call.modexreceiver ) then - self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall ) - end - - -- Add transmission to the right queue. - local caller = "" - if radio.alias == "LSO" then - - table.insert( self.RQLSO, transmission ) - - caller = "LSOCall" - - -- Schedule radio queue checks. - if not self.RQLid then - self:T( self.lid .. string.format( "Starting LSO radio queue." ) ) - self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 ) - end - - elseif radio.alias == "MARSHAL" then - - table.insert( self.RQMarshal, transmission ) - - caller = "MarshalCall" - - if not self.RQMid then - self:T( self.lid .. string.format( "Starting Marhal radio queue." ) ) - self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 ) - end - - end - - -- Append radio click sound at the end of the transmission. - if click then - self:RadioTransmission( radio, self[caller].CLICK, false, delay ) - end -======= --- Set SRS voice for the pilot calls. -- @param #AIRBOSS self -- @param #string Voice (Optional) SRS specific voice @@ -14904,13 +14863,12 @@ function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture ) self.PilotRadio.voice = Voice or MSRS.Voices.Microsoft.David self.PilotRadio.gender = Gender or "male" self.PilotRadio.culture = Culture or "en-US" - + if (not Voice) and self.SRS and self.SRS.google then self.PilotRadio.voice = MSRS.Voices.Google.Standard.en_US_Standard_J end - + return self ->>>>>>> origin/develop end --- Check if a call needs a subtitle because the complete voice overs are not available. @@ -15156,6 +15114,39 @@ function AIRBOSS:_RadioFilename( call, loud, channel ) return filename end +--- Format text into SRS friendly string +-- @param #AIRBOSS self +-- @param #string text +-- @return #string text +function AIRBOSS:_GetNiceSRSText(text) + text = string.gsub(text,"================================\n","") + text = string.gsub(text,"||","parallel") + text = string.gsub(text,"==","perpendicular") + text = string.gsub(text,"BRC","Base recovery") + --text = string.gsub(text,"#","Number") + text = string.gsub(text,"%((%a+)%)","Morse %1") + text = string.gsub(text,"°C","° Celsius") + text = string.gsub(text,"°"," degrees") + text = string.gsub(text," FB "," Final bearing ") + text = string.gsub(text," ops"," operations ") + text = string.gsub(text," kts"," knots") + text = string.gsub(text,"TACAN","Tackan") + text = string.gsub(text,"ICLS","I.C.L.S.") + text = string.gsub(text,"LSO","L.S.O.") + text = string.gsub(text,"inHg","inches of Mercury") + text = string.gsub(text,"QFE","Q.F.E.") + text = string.gsub(text,"hPa","hecto pascal") + text = string.gsub(text," NM"," nautical miles") + text = string.gsub(text," ft"," feet") + text = string.gsub(text,"A/C","aircraft") + text = string.gsub(text,"(#[%a%d%p%s]+)\n","") + text = string.gsub(text,"%.000"," dot zero") + text = string.gsub(text,"00"," double zero") + text = string.gsub(text," 0 "," zero " ) + text = string.gsub(text,"\n","; ") + return text +end + --- Send text message to player client. -- Message format will be "SENDER: RECCEIVER, MESSAGE". -- @param #AIRBOSS self @@ -15167,7 +15158,7 @@ end -- @param #boolean clear If true, clear screen from previous messages. -- @param #number delay Delay in seconds, before the message is displayed. function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duration, clear, delay ) - + self:I({sender,receiver,message}) if playerData and message and message ~= "" then -- Default duration. @@ -15190,46 +15181,44 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio -- SCHEDULER:New(nil, self.MessageToPlayer, {self, playerData, message, sender, receiver, duration, clear}, delay) self:ScheduleOnce( delay, self.MessageToPlayer, self, playerData, message, sender, receiver, duration, clear ) else -<<<<<<< HEAD -======= - + if not self.SRS then -- Wait until previous sound finished. local wait = 0 - + -- Onboard number to get the attention. if receiver == playerData.onboard then - + -- Which voice over number to use. if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then - + -- User sound of board number. wait = wait + self:_Number2Sound( playerData, sender, receiver ) - + end end - + -- Negative. if string.find( text:lower(), "negative" ) then local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" ) USERSOUND:New( filename ):ToGroup( playerData.group, wait ) wait = wait + self.MarshalCall.NEGATIVE.duration end - + -- Affirm. if string.find( text:lower(), "affirm" ) then local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" ) USERSOUND:New( filename ):ToGroup( playerData.group, wait ) wait = wait + self.MarshalCall.AFFIRMATIVE.duration end - + -- Roger. if string.find( text:lower(), "roger" ) then local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" ) USERSOUND:New( filename ):ToGroup( playerData.group, wait ) wait = wait + self.MarshalCall.ROGER.duration end - + -- Play click sound to end message. if wait > 0 then local filename = self:_RadioFilename( self.MarshalCall.CLICK ) @@ -15242,52 +15231,41 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio local voice = self.MarshalRadio.voice local gender = self.MarshalRadio.gender local culture = self.MarshalRadio.culture - + if not sender then sender = "AIRBOSS" end ->>>>>>> origin/develop - - -- Wait until previous sound finished. - local wait = 0 - - -- Onboard number to get the attention. - if receiver == playerData.onboard then - - -- Which voice over number to use. - if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then - - -- User sound of board number. - wait = wait + self:_Number2Sound( playerData, sender, receiver ) + if string.find(sender,"AIRBOSS" ) then + frequency = self.AirbossRadio.frequency + modulation = self.AirbossRadio.modulation + voice = self.AirbossRadio.voice + gender = self.AirbossRadio.gender + culture = self.AirbossRadio.culture end - end - -- Negative. - if string.find( text:lower(), "negative" ) then - local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" ) - USERSOUND:New( filename ):ToGroup( playerData.group, wait ) - wait = wait + self.MarshalCall.NEGATIVE.duration - end + --if sender == "MARSHAL" then + --voice = self.MarshalRadio.voice + --gender = self.MarshalRadio.gender + --culture = self.MarshalRadio.culture + --end - -- Affirm. - if string.find( text:lower(), "affirm" ) then - local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" ) - USERSOUND:New( filename ):ToGroup( playerData.group, wait ) - wait = wait + self.MarshalCall.AFFIRMATIVE.duration - end + if sender == "LSO" then + frequency = self.LSORadio.frequency + modulation = self.LSORadio.modulation + voice = self.LSORadio.voice + gender = self.LSORadio.gender + culture = self.LSORadio.culture + --elseif not sender then + -- TODO - what freq to use here? + --frequency = self.AirbossRadio.frequency + --modulation = self.AirbossRadio.modulation + --sender = "AIRBOSS" + end - -- Roger. - if string.find( text:lower(), "roger" ) then - local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" ) - USERSOUND:New( filename ):ToGroup( playerData.group, wait ) - wait = wait + self.MarshalCall.ROGER.duration + self:I(self.lid..text) + self:I({sender,frequency,modulation,voice}) + local srstext = self:_GetNiceSRSText(text) + self.SRSQ:NewTransmission(srstext,duration,self.SRS,nil,0.1,nil,nil,nil,frequency,modulation,gender,culture,voice,nil,sender) end - - -- Play click sound to end message. - if wait > 0 then - local filename = self:_RadioFilename( self.MarshalCall.CLICK ) - USERSOUND:New( filename ):ToGroup( playerData.group, wait ) - end - -- Text message to player client. if playerData.client then MESSAGE:New( text, duration, sender, clear ):ToClient( playerData.client ) @@ -15571,13 +15549,13 @@ end -- @param #string modex Tail number. function AIRBOSS:_MarshallInboundCall(unit, modex) - -- Calculate + -- Calculate local vectorCarrier = self:GetCoordinate():GetDirectionVec3(unit:GetCoordinate()) local bearing = UTILS.Round(unit:GetCoordinate():GetAngleDegrees( vectorCarrier ), 0) local distance = UTILS.Round(UTILS.MetersToNM(unit:GetCoordinate():Get2DDistance(self:GetCoordinate())),0) local angels = UTILS.Round(UTILS.MetersToFeet(unit:GetHeight()/1000),0) local state = UTILS.Round(self:_GetFuelState(unit)/1000,1) - + -- Pilot: "Marshall, [modex], marking mom's [bearing] for [distance], angels [XX], state [X.X]" local text=string.format("Marshal, %s, marking mom's %d for %d, angels %d, state %.1f", modex, bearing, distance, angels, state) -- Debug message. @@ -16404,8 +16382,8 @@ function AIRBOSS:_RequestMarshal( _unitName ) -- Voice over of inbound call (regardless of airboss rejecting it or not) if self.xtVoiceOvers then self:_MarshallInboundCall(_unit, playerData.onboard) - end - + end + -- Check if player is in CCA local inCCA = playerData.unit:IsInZone( self.zoneCCA ) @@ -16626,7 +16604,7 @@ function AIRBOSS:_RequestSpinning( _unitName ) -- Some advice. if playerData.difficulty == AIRBOSS.Difficulty.EASY then local text = "Climb to 1200 feet and proceed to the initial again." - self:MessageToPlayer( playerData, text, "INSTRUCTOR", "" ) + self:MessageToPlayer( playerData, text, "AIRBOSS", "" ) end return @@ -16653,12 +16631,12 @@ function AIRBOSS:_RequestCommence( _unitName ) local playerData = self.players[_playername] -- #AIRBOSS.PlayerData if playerData then - + -- Voice over of Commencing call (regardless of Airboss will rejected or not) if self.xtVoiceOvers then self:_CommencingCall(_unit, playerData.onboard) end - + -- Check if unit is in CCA. local text = "" local cleared = false @@ -17381,7 +17359,7 @@ function AIRBOSS:_DisplayCarrierInfo( _unitname ) state = "Deck closed" end if self.turning then - state = state .. " (turning currently)" + state = state .. " (currently turning)" end -- Message text. diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index e4554330e..7a1d9f2fd 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -327,6 +327,15 @@ function FLIGHTGROUP:New(group) -- TODO: Add pseudo functions. + --- FSM Function OnAfterLandAtAirbase + -- @function [parent=#FLIGHTGROUP] OnAfterLandAtAirbase + -- @param #FLIGHTGROUP self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Airbase#AIRBASE Airbase. + -- @return #FLIGHTGROUP self + -- Handle events: self:HandleEvent(EVENTS.Birth, self.OnEventBirth) self:HandleEvent(EVENTS.EngineStartup, self.OnEventEngineStartup) From 5c2b77cf762230832c46fa586461da5451472a38 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 29 Jul 2023 15:56:28 +0200 Subject: [PATCH 314/603] #Chmages --- Moose Development/Moose/.vscode/settings.json | 7 +++++-- Moose Development/Moose/Ops/PlayerTask.lua | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/.vscode/settings.json b/Moose Development/Moose/.vscode/settings.json index a8201d6ad..4656f0257 100644 --- a/Moose Development/Moose/.vscode/settings.json +++ b/Moose Development/Moose/.vscode/settings.json @@ -1,5 +1,5 @@ { - "Lua.workspace.preloadFileSize": 1000, + "Lua.workspace.preloadFileSize": 10000, "Lua.diagnostics.disable": [ "undefined-doc-name", "duplicate-set-field", @@ -8,7 +8,10 @@ "ambiguity-1", "undefined-doc-param", "redundant-parameter", - "param-type-mismatch" + "param-type-mismatch", + "deprecated", + "undefined-global", + "lowercase-global" ], "Lua.diagnostics.globals": [ "BASE", diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index d44e9c644..de8c78abe 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.18" +PLAYERTASK.version="0.1.19" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -3594,28 +3594,28 @@ function PLAYERTASKCONTROLLER:_RemoveMenuEntriesForTask(Task,Client) if Task then if Task.UUIDS and self.JoinTaskMenuTemplate then --self:I("***** JoinTaskMenuTemplate") - UTILS.PrintTableToLog(Task.UUIDS) + --UTILS.PrintTableToLog(Task.UUIDS) local controller = self.JoinTaskMenuTemplate for _,_uuid in pairs(Task.UUIDS) do local Entry = controller:FindEntryByUUID(_uuid) if Entry then controller:DeleteF10Entry(Entry,Client) controller:DeleteGenericEntry(Entry) - UTILS.PrintTableToLog(controller.menutree) + --UTILS.PrintTableToLog(controller.menutree) end end end if Task.AUUIDS and self.ActiveTaskMenuTemplate then --self:I("***** ActiveTaskMenuTemplate") - UTILS.PrintTableToLog(Task.AUUIDS) + --UTILS.PrintTableToLog(Task.AUUIDS) for _,_uuid in pairs(Task.AUUIDS) do local controller = self.ActiveTaskMenuTemplate local Entry = controller:FindEntryByUUID(_uuid) if Entry then controller:DeleteF10Entry(Entry,Client) controller:DeleteGenericEntry(Entry) - UTILS.PrintTableToLog(controller.menutree) + --UTILS.PrintTableToLog(controller.menutree) end end end @@ -4210,6 +4210,15 @@ function PLAYERTASKCONTROLLER:onafterTaskFailed(From, Event, To, Task) taskname = string.format(failtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) end + local clients=Task:GetClientObjects() + for _,client in pairs(clients) do + self:_RemoveMenuEntriesForTask(Task,client) + --self:_SwitchMenuForClient(client,"Info") + end + for _,client in pairs(clients) do + -- self:_RemoveMenuEntriesForTask(Task,client) + self:_SwitchMenuForClient(client,"Info",5) + end return self end From 3b2850e04264be9fc688c22cd168f74903f5a602 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 29 Jul 2023 16:02:42 +0200 Subject: [PATCH 315/603] changes --- Moose Development/Moose/Ops/Chief.lua | 21 ++ Moose Development/Moose/Ops/Commander.lua | 40 +++ Moose Development/Moose/Ops/FlightGroup.lua | 320 +++++++++++++++++++- 3 files changed, 374 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 0272ec659..503cbfc2b 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -1501,6 +1501,17 @@ function CHIEF:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg) return zone end +--- Remove a AWACS zone. +-- @param #CHIEF self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function CHIEF:RemoveAwacsZone(Zone) + + -- Hand over to commander. + local zone=self.commander:RemoveAwacsZone(Zone) + + return zone +end + --- Add a refuelling tanker zone. -- @param #CHIEF self -- @param Core.Zone#ZONE Zone Zone. @@ -1518,6 +1529,16 @@ function CHIEF:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSystem) return zone end +--- Remove a refuelling tanker zone. +-- @param #CHIEF self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function CHIEF:RemoveTankerZone(Zone) + + -- Hand over to commander. + local zone=self.commander:RemoveTankerZone(Zone) + + return zone +end --- Set border zone set, defining your territory. -- diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index 0af40ed2d..b0bd1673e 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -735,6 +735,26 @@ function COMMANDER:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg) return awacszone end +--- Remove a AWACS zone. +-- @param #COMMANDER self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function COMMANDER:RemoveAwacsZone(Zone) + + local awacszone={} --Ops.AirWing#AIRWING.PatrolZone + + awacszone.zone=Zone + for i,_awacszone in pairs(self.awacsZones) do + if _awacszone.zone == awacszone.zone then + if _awacszone.mission and _awacszone.mission:IsNotOver() then + _awacszone.mission:Cancel() + end + table.remove(self.awacsZones, i) + break + end + end + return awacszone +end + --- Add a refuelling tanker zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE Zone Zone. @@ -762,6 +782,26 @@ function COMMANDER:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSyst return tankerzone end +--- Remove a refuelling tanker zone. +-- @param #COMMANDER self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function COMMANDER:RemoveTankerZone(Zone) + + local tankerzone={} --Ops.AirWing#AIRWING.PatrolZone + + tankerzone.zone=Zone + for i,_tankerzone in pairs(self.tankerZones) do + if _tankerzone.zone == tankerzone.zone then + if _tankerzone.mission and _tankerzone.mission:IsNotOver() then + _tankerzone.mission:Cancel() + end + table.remove(self.tankerZones, i) + break + end + end + return tankerzone +end + --- Check if this mission is already in the queue. -- @param #COMMANDER self -- @param Ops.Auftrag#AUFTRAG Mission The mission. diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 7a1d9f2fd..46cd5f2d6 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -325,16 +325,322 @@ function FLIGHTGROUP:New(group) -- @param #FLIGHTGROUP self -- @param #number delay Delay in seconds. - -- TODO: Add pseudo functions. + --- FSM Function OnAfterElementSpawned. + -- @function [parent=#FLIGHTGROUP] OnAfterElementSpawned + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. - --- FSM Function OnAfterLandAtAirbase + --- FSM Function OnAfterElementParking. + -- @function [parent=#FLIGHTGROUP] OnAfterElementParking + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE.ParkingSpot Spot Parking Spot. + + --- FSM Function OnAfterElementEngineOn. + -- @function [parent=#FLIGHTGROUP] OnAfterElementEngineOn + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementTaxiing. + -- @function [parent=#FLIGHTGROUP] OnAfterElementTaxiing + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementTakeoff. + -- @function [parent=#FLIGHTGROUP] OnAfterElementTakeoff + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil. + + --- FSM Function OnAfterElementAirborne. + -- @function [parent=#FLIGHTGROUP] OnAfterElementAirborne + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementLanded. + -- @function [parent=#FLIGHTGROUP] OnAfterElementLanded + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil. + + --- FSM Function OnAfterElementArrived. + -- @function [parent=#FLIGHTGROUP] OnAfterElementArrived + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase, where the element arrived. + -- @param Wrapper.Airbase#AIRBASE.ParkingSpot Parking The Parking spot the element has. + + --- FSM Function OnAfterElementDestroyed. + -- @function [parent=#FLIGHTGROUP] OnAfterElementDestroyed + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementDead. + -- @function [parent=#FLIGHTGROUP] OnAfterElementDead + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterSpawned. + -- @function [parent=#FLIGHTGROUP] OnAfterSpawned + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterParking. + -- @function [parent=#FLIGHTGROUP] OnAfterParking + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterTaxiing. + -- @function [parent=#FLIGHTGROUP] OnAfterTaxiing + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterTakeoff. + -- @function [parent=#FLIGHTGROUP] OnAfterTakeoff + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterAirborne. + -- @function [parent=#FLIGHTGROUP] OnAfterAirborne + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterCruise. + -- @function [parent=#FLIGHTGROUP] OnAfterCruise + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterLanding. + -- @function [parent=#FLIGHTGROUP] OnAfterLanding + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterLanded. + -- @function [parent=#FLIGHTGROUP] OnAfterLanded + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase the flight landed. + + --- FSM Function OnAfterLandedAt. + -- @function [parent=#FLIGHTGROUP] OnAfterLandedAt + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterArrived. + -- @function [parent=#FLIGHTGROUP] OnAfterArrived + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterDead. + -- @function [parent=#FLIGHTGROUP] OnAfterDead + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterUpdateRoute. + -- @function [parent=#FLIGHTGROUP] OnAfterUpdateRoute + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number n Next waypoint index. Default is the one coming after that one that has been passed last. + -- @param #number N Waypoint Max waypoint index to be included in the route. Default is the final waypoint. + + --- FSM Function OnAfterOutOfMissilesAA. + -- @function [parent=#FLIGHTGROUP] OnAfterOutOfMissilesAA + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterOutOfMissilesAG. + -- @function [parent=#FLIGHTGROUP] OnAfterOutOfMissilesAG + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterRTB. + -- @function [parent=#FLIGHTGROUP] OnAfterRTB + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + -- @param #number SpeedTo Speed used for traveling from current position to holding point in knots. Default 75% of max speed. + -- @param #number SpeedHold Holding speed in knots. Default 250 kts. + -- @param #number SpeedLand Landing speed in knots. Default 170 kts. + + --- FSM Function OnAfterLandAtAirbase. -- @function [parent=#FLIGHTGROUP] OnAfterLandAtAirbase -- @param #FLIGHTGROUP self - -- @param #string From State. - -- @param #string Event Trigger. - -- @param #string To State. - -- @param Wrapper.Airbase#AIRBASE Airbase. - -- @return #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + + --- FSM Function OnAfterWait. + -- @function [parent=#FLIGHTGROUP] OnAfterWait + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever). + -- @param #number Altitude Altitude in feet. Default 10,000 ft for airplanes and 1,000 feet for helos. + -- @param #number Speed Speed in knots. Default 250 kts for airplanes and 20 kts for helos. + + --- FSM Function OnAfterRefuel. + -- @function [parent=#FLIGHTGROUP] OnAfterRefuel + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Point#COORDINATE Coordinate The coordinate. + + --- FSM Function OnAfterRefueled. + -- @function [parent=#FLIGHTGROUP] OnAfterRefueled + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterDisengage. + -- @function [parent=#FLIGHTGROUP] OnAfterDisengage + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Set#SET_UNIT TargetUnitSet + + --- FSM Function OnAfterEngageTarget. + -- @function [parent=#FLIGHTGROUP] OnAfterEngageTarget + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table Target Target object. Can be a UNIT, STATIC, GROUP, SET_UNIT or SET_GROUP object. + + --- FSM Function OnAfterLandAt. + -- @function [parent=#FLIGHTGROUP] OnAfterLandAt + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Set#SET_UNIT TargetUnitSet + + --- FSM Function OnAfterFuelLow. + -- @function [parent=#FLIGHTGROUP] OnAfterFuelLow + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterFuelCritical. + -- @function [parent=#FLIGHTGROUP] OnAfterFuelCritical + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnBeforeUpdateRoute. + -- @function [parent=#FLIGHTGROUP] OnBeforeUpdateRoute + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number n Next waypoint index. Default is the one coming after that one that has been passed last. + -- @param #number N Waypoint Max waypoint index to be included in the route. Default is the final waypoint. + -- @return #boolean Transision allowed? + + --- FSM Function OnBeforeRTB. + -- @function [parent=#FLIGHTGROUP] OnBeforeRTB + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + -- @param #number SpeedTo Speed used for travelling from current position to holding point in knots. + -- @param #number SpeedHold Holding speed in knots. + + --- FSM Function OnBeforeLandAtAirbase. + -- @function [parent=#FLIGHTGROUP] OnBeforeLandAtAirbase + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + + --- FSM Function OnBeforeWait. + -- @function [parent=#FLIGHTGROUP] OnBeforeWait + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever). + -- @param #number Altitude Altitude in feet. Default 10,000 ft for airplanes and 1,000 feet for helos. + -- @param #number Speed Speed in knots. Default 250 kts for airplanes and 20 kts for helos. + + --- FSM Function OnBeforeLandAt. + -- @function [parent=#FLIGHTGROUP] OnBeforeLandAt + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Point#COORDINATE Coordinate The coordinate where to land. Default is current position. + -- @param #number Duration The duration in seconds to remain on ground. Default 600 sec (10 min). + + -- TODO: Add pseudo functions ? Normally done, but should be double check -- Handle events: self:HandleEvent(EVENTS.Birth, self.OnEventBirth) From 29947d69c3c229b3b3dafaa1e420b9e81eca05b3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 29 Jul 2023 16:40:46 +0200 Subject: [PATCH 316/603] Changes --- .../Moose/Functional/Scoring.lua | 4 ++-- Moose Development/Moose/Ops/Awacs.lua | 19 +++++++++++++++++++ Moose Development/Moose/Ops/PlayerTask.lua | 9 +++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 3d0c56b9f..ec95dc8a3 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -1420,7 +1420,7 @@ function SCORING:ReportDetailedPlayerHits( PlayerName ) Penalty = Penalty + UnitData.Penalty PenaltyHit = UnitData.PenaltyHit end - local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) + local ScoreMessageHit = string.format( "%s: %d ", CategoryName, Score - Penalty ) self:T( ScoreMessageHit ) ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit PlayerScore = PlayerScore + Score @@ -1476,7 +1476,7 @@ function SCORING:ReportDetailedPlayerDestroys( PlayerName ) end end - local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) + local ScoreMessageDestroy = string.format( " %s: %d ", CategoryName, Score - Penalty ) self:T( ScoreMessageDestroy ) ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index ff57e7ad9..54196e5de 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1517,6 +1517,23 @@ function AWACS:_SubScribeTactRadio(Group,Frequency) return self end +--- [Internal] _CheckSubscribers +-- @param #AWACS self +-- @return #AWACS self +function AWACS:_CheckSubscribers() + self:T(self.lid.."_InitLocalization") + + for _name,_freq in pairs(self.TacticalSubscribers or {}) do + local grp = GROUP:FindByName(_name) + if (not grp) or (not grp:IsAlive()) then + self.TacticalFrequencies[_freq] = _freq + self.TacticalSubscribers[_name] = nil + end + end + + return self +end + --- [Internal] Init localization -- @param #AWACS self -- @return #AWACS self @@ -6180,6 +6197,8 @@ function AWACS:onafterStatus(From, Event, To) self:_CheckMerges() + self:_CheckSubscribers() + local outcome, targets = self:_TargetSelectionProcess(true) self:_CheckTaskQueue() diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index de8c78abe..b589bdd63 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -2483,6 +2483,15 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue() self:T("*****Removing player " .. _id) self.TasksPerPlayer:PullByID(_id) end + local clients=task:GetClientObjects() + for _,client in pairs(clients) do + self:_RemoveMenuEntriesForTask(task,client) + --self:_SwitchMenuForClient(client,"Info") + end + for _,client in pairs(clients) do + -- self:_RemoveMenuEntriesForTask(Task,client) + self:_SwitchMenuForClient(client,"Info",5) + end -- Follow-up tasks? local nexttasks = {} if task.FinalState == "Success" then From 9d4b5cfc0b5a446f884ab43eb2304363303ea997 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 29 Jul 2023 17:20:06 +0200 Subject: [PATCH 317/603] Debugs --- Moose Development/Moose/Ops/Awacs.lua | 2 +- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 54196e5de..d55b0534d 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1613,7 +1613,7 @@ function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,Fro local RadioEntry = {} -- #AWACS.RadioEntry RadioEntry.IsNew = IsNew RadioEntry.TextTTS = TextTTS - RadioEntry.TextScreen = TextScreen + RadioEntry.TextScreen = TextScreen or TextTTS RadioEntry.GroupID = GID RadioEntry.ToScreen = ToScreen RadioEntry.Duration = STTS.getSpeechTime(TextTTS,0.95,false) or 8 diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index b589bdd63..6b9af9ca2 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -2177,7 +2177,7 @@ end -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_EventHandler(EventData) self:T(self.lid.."_EventHandler: "..EventData.id) - self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName) +-- self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then if EventData.IniPlayerName then self:T(self.lid.."Event for player: "..EventData.IniPlayerName) From 090de853884589d6e2c53c86bd684821c9e2322e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 31 Jul 2023 18:03:54 +0200 Subject: [PATCH 318/603] joa --- Moose Development/Moose/Ops/PlayerTask.lua | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 6b9af9ca2..f91893c7e 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update June 2023 +-- @date Last Update July 2023 do @@ -1656,13 +1656,6 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) local starttime = math.random(5,10) self:__Status(starttime) - -- Player leaves - self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) - self:HandleEvent(EVENTS.Ejection, self._EventHandler) - self:HandleEvent(EVENTS.Crash, self._EventHandler) - self:HandleEvent(EVENTS.PilotDead, self._EventHandler) - self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) - self:I(self.lid..self.version.." Started.") return self @@ -2177,7 +2170,7 @@ end -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_EventHandler(EventData) self:T(self.lid.."_EventHandler: "..EventData.id) --- self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName) + --self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then if EventData.IniPlayerName then self:T(self.lid.."Event for player: "..EventData.IniPlayerName) @@ -4072,6 +4065,12 @@ function PLAYERTASKCONTROLLER:onafterStart(From, Event, To) self:T(self.lid.."onafterStart") self:_CreateJoinMenuTemplate() self:_CreateActiveTaskMenuTemplate() + -- Player Events + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) + self:HandleEvent(EVENTS.Ejection, self._EventHandler) + self:HandleEvent(EVENTS.Crash, self._EventHandler) + self:HandleEvent(EVENTS.PilotDead, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) return self end From 9a9ac6f2e1f0de24b43dd8dc7b34053b42b6ee58 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 1 Aug 2023 11:38:02 +0200 Subject: [PATCH 319/603] SCENERY --- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Wrapper/Scenery.lua | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index a86a4d0af..b1e545f52 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -7621,7 +7621,7 @@ do -- SET_SCENERY --- Add SCENERY(s) to SET_SCENERY. -- @param #SET_SCENERY self - -- @param #string AddScenery A single SCENERY. + -- @param Wrapper.Scenery#SCENERY AddScenery A single SCENERY object. -- @return #SET_SCENERY self function SET_SCENERY:AddScenery( AddScenery ) self:F2( AddScenery:GetName() ) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 6a6f70bd9..d50c922e5 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -175,10 +175,11 @@ end function SCENERY:FindByZoneName( ZoneName ) local zone = ZoneName -- Core.Zone#ZONE if type(ZoneName) == "string" then - zone = ZONE:FindByName(ZoneName) + zone = ZONE:FindByName(ZoneName) -- Core.Zone#ZONE_BASE end local _id = zone:GetProperty('OBJECT ID') - BASE:T("Object ID ".._id) + --BASE:I(string.format("Object ID is: %s",_id or "none")) + --BASE:T("Object ID ".._id) if not _id then -- this zone has no object ID BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) From 2ccb4c128a406fd61313520b77ceb4897b6a72ec Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 1 Aug 2023 16:16:20 +0200 Subject: [PATCH 320/603] Zone --- Moose Development/Moose/Core/Zone.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 8859ca7ed..52f4675f0 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -953,7 +953,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) local Include = false if not UnitCategories then - -- Anythink found is included. + -- Anything found is included. Include = true else -- Check if found object is in specified categories. @@ -984,9 +984,9 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) if ObjectCategory == Object.Category.SCENERY then local SceneryType = ZoneObject:getTypeName() local SceneryName = ZoneObject:getName() - --BASE:I("SceneryType "..SceneryType.."SceneryName"..SceneryName) + --BASE:I("SceneryType "..SceneryType.." SceneryName "..tostring(SceneryName)) self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {} - self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject ) + self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( tostring(SceneryName), ZoneObject) table.insert(self.ScanData.SceneryTable,self.ScanData.Scenery[SceneryType][SceneryName] ) self:T( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } ) end @@ -2586,7 +2586,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories ) local minmarkcoord = COORDINATE:NewFromVec3(minVec3) local maxmarkcoord = COORDINATE:NewFromVec3(maxVec3) local ZoneRadius = minmarkcoord:Get2DDistance(maxmarkcoord)/2 - +-- self:I("Scan Radius:" ..ZoneRadius) local CenterVec3 = self:GetCoordinate():GetVec3() --[[ this a bit shaky in functionality it seems From f085a5423a5a8acb48eba74f1575cd3056d1ab6d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 1 Aug 2023 16:18:41 +0200 Subject: [PATCH 321/603] SCENERY --- Moose Development/Moose/Core/Set.lua | 135 +++++++++++++++++--- Moose Development/Moose/Wrapper/Scenery.lua | 90 +++++++++---- 2 files changed, 179 insertions(+), 46 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index b1e545f52..973090bb7 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -46,7 +46,8 @@ -- @image Core_Sets.JPG do -- SET_BASE - + + --- -- @type SET_BASE -- @field #table Filter Table of filters. -- @field #table Set Table of objects. @@ -242,7 +243,7 @@ do -- SET_BASE function SET_BASE:Add( ObjectName, Object ) -- Debug info. - self:T( { ObjectName = ObjectName, Object = Object } ) + self:T2( { ObjectName = ObjectName, Object = Object } ) -- Ensure that the existing element is removed from the Set before a new one is inserted to the Set if self.Set[ObjectName] then @@ -257,6 +258,8 @@ do -- SET_BASE -- Trigger Added event. self:Added( ObjectName, Object ) + + return self end --- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index. @@ -1948,7 +1951,7 @@ do if self.Filter.Zones then local MGroupZone = false for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:I( "Zone:", ZoneName ) + --self:T( "Zone:", ZoneName ) if MGroup:IsInZone(Zone) then MGroupZone = true end @@ -2014,7 +2017,8 @@ do end do -- SET_UNIT - + + --- -- @type SET_UNIT -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval @@ -3194,7 +3198,8 @@ do -- SET_UNIT end do -- SET_STATIC - + + --- -- @type SET_STATIC -- @extends Core.Set#SET_BASE @@ -4463,7 +4468,7 @@ do -- SET_CLIENT if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true and MClient:IsAlive() == true) then MClientActive = true end - --self:I( { "Evaluated Active", MClientActive } ) + --self:T( { "Evaluated Active", MClientActive } ) MClientInclude = MClientInclude and MClientActive end @@ -4545,7 +4550,7 @@ do -- SET_CLIENT if self.Filter.Playernames then local MClientPlayername = false local playername = MClient:GetPlayerName() or "Unknown" - --self:I(playername) + --self:T(playername) for _,_Playername in pairs(self.Filter.Playernames) do if playername and string.find(playername,_Playername) then MClientPlayername = true @@ -4558,7 +4563,7 @@ do -- SET_CLIENT if self.Filter.Callsigns then local MClientCallsigns = false local callsign = MClient:GetCallsign() - --self:I(callsign) + --self:T(callsign) for _,_Callsign in pairs(self.Filter.Callsigns) do if callsign and string.find(callsign,_Callsign) then MClientCallsigns = true @@ -4577,6 +4582,7 @@ end do -- SET_PLAYER + --- -- @type SET_PLAYER -- @extends Core.Set#SET_BASE @@ -5002,7 +5008,8 @@ do -- SET_PLAYER end do -- SET_AIRBASE - + + --- -- @type SET_AIRBASE -- @extends Core.Set#SET_BASE @@ -5345,7 +5352,8 @@ do -- SET_AIRBASE end do -- SET_CARGO - + + --- -- @type SET_CARGO -- @extends Core.Set#SET_BASE @@ -5771,7 +5779,8 @@ do -- SET_CARGO end do -- SET_ZONE - + + --- -- @type SET_ZONE -- @extends Core.Set#SET_BASE @@ -6139,7 +6148,8 @@ do -- SET_ZONE end do -- SET_ZONE_GOAL - + + --- -- @type SET_ZONE_GOAL -- @extends Core.Set#SET_BASE @@ -6450,7 +6460,8 @@ do -- SET_ZONE_GOAL end do -- SET_OPSZONE - + + --- -- @type SET_OPSZONE -- @extends Core.Set#SET_BASE @@ -6907,7 +6918,8 @@ end do -- SET_OPSGROUP - + + --- -- @type SET_OPSGROUP -- @extends Core.Set#SET_BASE @@ -7575,6 +7587,7 @@ do -- SET_SCENERY Scenerys = {}, Filter = { SceneryPrefixes = nil, + SceneryRoles = nil, Zones = nil, }, } @@ -7597,7 +7610,7 @@ do -- SET_SCENERY if ZoneSet then for _,_zone in pairs(ZoneSet.Set) do - self:T("Zone type handed: "..tostring(_zone.ClassName)) + self:T("Zone type handed: "..tostring(_zone.ClassName)) table.insert(zonenames,_zone:GetName()) end self:AddSceneryByName(zonenames) @@ -7691,6 +7704,7 @@ do -- SET_SCENERY end for _,Zone in pairs( zones ) do local zonename = Zone:GetName() + self:T(zonename) self.Filter.Zones[zonename] = Zone end return self @@ -7709,11 +7723,30 @@ do -- SET_SCENERY Prefixes = { Prefixes } end for PrefixID, Prefix in pairs( Prefixes ) do + --self:T(Prefix) self.Filter.SceneryPrefixes[Prefix] = Prefix end return self end - + + --- Builds a set of SCENERYs that **contain** an exact match of the "ROLE" property. + -- @param #SET_SCENERY self + -- @param #string Role The string pattern(s) that needs to exactly match the scenery "ROLE" property from the ME quad-zone properties. Can also be passed as a `#table` of strings. + -- @return #SET_SCENERY self + function SET_SCENERY:FilterRoles( Role ) + if not self.Filter.SceneryRoles then + self.Filter.SceneryRoles = {} + end + if type( Role ) ~= "table" then + Role = { Role } + end + for PrefixID, Prefix in pairs( Role ) do + --self:T(Prefix) + self.Filter.SceneryRoles[Prefix] = Prefix + end + return self + end + --- Iterate the SET_SCENERY and count how many SCENERYSs are alive. -- @param #SET_SCENERY self -- @return #number The number of SCENERYSs alive. @@ -7806,10 +7839,76 @@ do -- SET_SCENERY -- @param Wrapper.Scenery#SCENERY MScenery -- @return #SET_SCENERY self function SET_SCENERY:IsIncludeObject( MScenery ) - self:F2( MScenery ) - return true + self:T( MScenery.SceneryName ) + + local MSceneryInclude = true + + if MScenery then + local MSceneryName = MScenery:GetName() + + -- Filter Prefixes + if self.Filter.Prefixes then + local MSceneryPrefix = false + for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do + self:T( { "Prefix:", string.find( MSceneryName, ZonePrefix, 1 ), ZonePrefix } ) + if string.find( MSceneryName, ZonePrefix, 1 ) then + MSceneryPrefix = true + end + end + self:T( { "Evaluated Prefix", MSceneryPrefix } ) + MSceneryInclude = MSceneryInclude and MSceneryPrefix + end + + if self.Filter.Zones then + local MSceneryZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + --self:T( "Zone:", ZoneName ) + local coord = MScenery:GetCoordinate() + if coord and Zone:IsCoordinateInZone(coord) then + MSceneryZone = true + end + self:T( { "Evaluated Zone", MSceneryZone } ) + end + MSceneryInclude = MSceneryInclude and MSceneryZone + end + + -- Filter Roles + if self.Filter.SceneryRoles then + local MSceneryRole = false + local Role = MScenery:GetProperty("ROLE") or "none" + for ZoneRoleId, ZoneRole in pairs( self.Filter.SceneryRoles ) do + self:T( { "Role:", ZoneRole, Role } ) + if ZoneRole == Role then + MSceneryRole = true + end + end + self:T( { "Evaluated Role ", MSceneryRole } ) + MSceneryInclude = MSceneryInclude and MSceneryRole + end + end + + self:T2( MSceneryInclude ) + return MSceneryInclude end + --- Filters for the defined collection. + -- @param #SET_SCENERY self + -- @return #SET_SCENERY self + function SET_SCENERY:FilterOnce() + + for ObjectName, Object in pairs( self:GetSet() ) do + self:T(ObjectName) + if self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) + else + self:Remove(ObjectName, true) + end + end + + return self --FilteredSet + end + + --- Count overall initial (Life0) lifepoints of the SET objects. -- @param #SET_SCENERY self -- @return #number LIfe0Points diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index d50c922e5..3c697579c 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -9,7 +9,7 @@ -- === -- -- @module Wrapper.Scenery --- @image Wrapper_Scenery.JPG +-- @image Wrapper_Scenery.JPG+ --- SCENERY Class @@ -18,6 +18,7 @@ -- @field #string SceneryName Name of the scenery object. -- @field DCS#Object SceneryObject DCS scenery object. -- @field #number Life0 Initial life points. +-- @field #table Properties -- @extends Wrapper.Positionable#POSITIONABLE @@ -43,7 +44,7 @@ function SCENERY:Register( SceneryName, SceneryObject ) local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) - self.SceneryName = SceneryName + self.SceneryName = tostring(SceneryName) self.SceneryObject = SceneryObject @@ -52,9 +53,43 @@ function SCENERY:Register( SceneryName, SceneryObject ) else self.Life0 = 0 end + + self.Properties = {} + return self end +--- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists. +-- @param #SCENERY self +-- @param #string PropertyName The name of a the QuadZone Property from the scenery assignment to be retrieved. +-- @return #string The Value of the QuadZone Property from the scenery assignment with the given PropertyName, or nil if absent. +function SCENERY:GetProperty(PropertyName) + return self.Properties[PropertyName] +end + +--- Returns the scenery Properties table. +-- @param #SCENERY self +-- @return #table The Key:Value table of QuadZone properties of the zone from the scenery assignment . +function SCENERY:GetAllProperties() + return self.Properties +end + +--- Set a scenery property +-- @param #SCENERY self +-- @param #string PropertyName +-- @param #string PropertyValue +-- @return #SCENERY self +function SCENERY:SetProperty(PropertyName, PropertyValue) + self.Properties[PropertyName] = PropertyValue + return self +end +--- Obtain object name. +--@param #SCENERY self +--@return #string Name +function SCENERY:GetName() + return self.SceneryName +end + --- Obtain DCS Object from the SCENERY Object. --@param #SCENERY self --@return DCS#Object DCS scenery object. @@ -115,26 +150,28 @@ end --@param Core.Point#COORDINATE Coordinate Where to find the scenery object --@param #number Radius (optional) Search radius around coordinate, defaults to 100 --@return #SCENERY Scenery Object or `nil` if it cannot be found -function SCENERY:FindByName(Name, Coordinate, Radius) - +function SCENERY:FindByName(Name, Coordinate, Radius, Role) + local radius = Radius or 100 local name = Name or "unknown" local scenery = nil - BASE:T({name, radius, Coordinate:GetVec2()}) - --- -- @param Core.Point#COORDINATE coordinate -- @param #number radius -- @param #string name - local function SceneryScan(coordinate, radius, name) - if coordinate ~= nil then - local scenerylist = coordinate:ScanScenery(radius) - local rscenery = nil - for _,_scenery in pairs(scenerylist) do + local function SceneryScan(scoordinate, sradius, sname) + if scoordinate ~= nil then + local Vec2 = scoordinate:GetVec2() + local scanzone = ZONE_RADIUS:New("Zone-"..sname,Vec2,sradius,true) + scanzone:Scan({Object.Category.SCENERY}) + local scanned = scanzone:GetScannedSceneryObjects() + local rscenery = nil -- Wrapper.Scenery#SCENERY + for _,_scenery in pairs(scanned) do local scenery = _scenery -- Wrapper.Scenery#SCENERY - if tostring(scenery.SceneryName) == tostring(name) then + if tostring(scenery.SceneryName) == tostring(sname) then rscenery = scenery + if Role then rscenery:SetProperty("ROLE",Role) end break end end @@ -144,6 +181,7 @@ function SCENERY:FindByName(Name, Coordinate, Radius) end if Coordinate then + --BASE:I("Coordinate Scenery Scan") scenery = SceneryScan(Coordinate, radius, name) end @@ -154,7 +192,7 @@ end -- to find the correct object. --@param #SCENERY self --@param #string Name The name or id of the scenery object as taken from the ME. Ex. '595785449' ---@param Core.Zone#ZONE Zone Where to find the scenery object. Can be handed as zone name. +--@param Core.Zone#ZONE_BASE Zone Where to find the scenery object. Can be handed as zone name. --@param #number Radius (optional) Search radius around coordinate, defaults to 100 --@return #SCENERY Scenery Object or `nil` if it cannot be found function SCENERY:FindByNameInZone(Name, Zone, Radius) @@ -164,7 +202,7 @@ function SCENERY:FindByNameInZone(Name, Zone, Radius) Zone = ZONE:FindByName(Zone) end local coordinate = Zone:GetCoordinate() - return self:FindByName(Name,coordinate,Radius) + return self:FindByName(Name,coordinate,Radius,Zone:GetProperty("ROLE")) end --- Find a SCENERY object from its zone name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first @@ -173,11 +211,12 @@ end --@param #string ZoneName The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object. --@return #SCENERY First found Scenery Object or `nil` if it cannot be found function SCENERY:FindByZoneName( ZoneName ) - local zone = ZoneName -- Core.Zone#ZONE + local zone = ZoneName -- Core.Zone#ZONE_BASE if type(ZoneName) == "string" then - zone = ZONE:FindByName(ZoneName) -- Core.Zone#ZONE_BASE + zone = ZONE:FindByName(ZoneName) -- Core.Zone#ZONE_POLYGON end local _id = zone:GetProperty('OBJECT ID') + --local properties = zone:GetAllProperties() or {} --BASE:I(string.format("Object ID is: %s",_id or "none")) --BASE:T("Object ID ".._id) if not _id then @@ -185,27 +224,21 @@ function SCENERY:FindByZoneName( ZoneName ) BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) if string.find(zone.ClassName,"POLYGON") then zone:Scan({Object.Category.SCENERY}) - local scanned = zone:GetScannedScenery() + local scanned = zone:GetScannedSceneryObjects() for _,_scenery in (scanned) do local scenery = _scenery -- Wrapper.Scenery#SCENERY if scenery:IsAlive() then + local role = zone:GetProperty("ROLE") + if role then scenery:SetProperty("ROLE",role) end return scenery end end return nil else - local coordinate = zone:GetCoordinate() - local scanned = coordinate:ScanScenery() - for _,_scenery in (scanned) do - local scenery = _scenery -- Wrapper.Scenery#SCENERY - if scenery:IsAlive() then - return scenery - end - end - return nil + return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE")) end else - return self:FindByName(_id, zone:GetCoordinate()) + return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE")) end end @@ -220,6 +253,7 @@ function SCENERY:FindAllByZoneName( ZoneName ) zone = ZONE:FindByName(ZoneName) end local _id = zone:GetProperty('OBJECT ID') + --local properties = zone:GetAllProperties() or {} if not _id then -- this zone has no object ID --BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) @@ -231,7 +265,7 @@ function SCENERY:FindAllByZoneName( ZoneName ) return nil end else - local obj = self:FindByName(_id, zone:GetCoordinate()) + local obj = self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE")) if obj then return {obj} else From 5231bc8372f19753535bbd3ba7c83b827b570e74 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 1 Aug 2023 16:26:51 +0200 Subject: [PATCH 322/603] # --- Moose Development/Moose/Wrapper/Scenery.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 3c697579c..1ef31a039 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -9,7 +9,7 @@ -- === -- -- @module Wrapper.Scenery --- @image Wrapper_Scenery.JPG+ +-- @image Wrapper_Scenery.JPG --- SCENERY Class From bbae91bb6c81c16d500c7554ea8740fb5be50863 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 2 Aug 2023 18:02:22 +0200 Subject: [PATCH 323/603] #CONTROLLABLE * Added EnRouteTaskCAP() --- .../Moose/Wrapper/Controllable.lua | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 37067d492..79f794b48 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1734,6 +1734,27 @@ function CONTROLLABLE:EnRouteTaskSEAD(TargetTypes, Priority) return DCSTask end +--- (AIR) Enroute CAP task. +-- @param #CONTROLLABLE self +-- @param DCS#AttributeNameArray TargetTypes Array of target categories allowed to engage. Default `{"Air"}`. +-- @param #number Priority (Optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. Default 0. +-- @return DCS#Task The DCS task structure. +function CONTROLLABLE:EnRouteTaskCAP(TargetTypes, Priority) + + local DCSTask = { + id = 'EngageTargets', + key = "CAP", + --auto = true, + enabled = true, + params = { + targetTypes = TargetTypes or {"Air"}, + priority = Priority or 0 + } + } + + return DCSTask +end + --- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; -- it just allows the unit/controllable to engage the target controllable as well as other assigned targets. -- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_engageGroup). From 53712b8e407a71246b4dedf7c7ab44271be5bb76 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 18 Aug 2023 11:05:48 +0200 Subject: [PATCH 324/603] POS --- Moose Development/Moose/Modules_local.lua | 1 + .../Moose/Wrapper/Positionable.lua | 182 ++++++++++-------- 2 files changed, 101 insertions(+), 82 deletions(-) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index a52a4a74a..959766e8f 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -49,6 +49,7 @@ __Moose.Include( 'Wrapper\\Scenery.lua' ) __Moose.Include( 'Wrapper\\Marker.lua' ) __Moose.Include( 'Wrapper\\Net.lua' ) __Moose.Include( 'Wrapper\\Weapon.lua' ) +__Moose.Include( 'Wrapper\\Storage.lua' ) __Moose.Include( 'Cargo\\Cargo.lua' ) __Moose.Include( 'Cargo\\CargoUnit.lua' ) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 8a951fb79..57e50c7be 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -11,10 +11,10 @@ -- @module Wrapper.Positionable -- @image Wrapper_Positionable.JPG --- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used internally by the moose designer :-) +--- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used internally by the moose designer :-) -- @extends Wrapper.Identifiable#IDENTIFIABLE --- @type POSITIONABLE +--- @type POSITIONABLE -- @field Core.Point#COORDINATE coordinate Coordinate object. -- @field Core.Point#POINT_VEC3 pointvec3 Point Vec3 object. -- @extends Wrapper.Identifiable#IDENTIFIABLE @@ -51,10 +51,10 @@ POSITIONABLE = { pointvec3 = nil, } --- @field #POSITIONABLE.__ +--- @field #POSITIONABLE.__ POSITIONABLE.__ = {} --- @field #POSITIONABLE.__.Cargo +--- @field #POSITIONABLE.__.Cargo POSITIONABLE.__.Cargo = {} --- A DCSPositionable @@ -591,10 +591,10 @@ function POSITIONABLE:GetRandomVec3( Radius ) if Radius then local PositionableRandomVec3 = {} - local angle = math.random() * math.pi * 2; - PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius; + local angle = math.random() * math.pi * 2 + PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius PositionableRandomVec3.y = PositionablePointVec3.y - PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius; + PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius self:T3( PositionableRandomVec3 ) return PositionableRandomVec3 @@ -1768,6 +1768,93 @@ do -- Cargo end return self.__.CargoBayWeightLimit - CargoWeight end + + --- + -- @field DefaultInfantryWeight + -- Abstract out the "XYZ*95" calculation in the Ground POSITIONABLE case, where the table + -- holds number of infantry that the vehicle could carry, instead of actual cargo weights. + POSITIONABLE.DefaultInfantryWeight = 95 + + --- + -- @field CargoBayCapacityValues + -- @field CargoBayCapacityValues.Air + -- @field CargoBayCapacityValues.Naval + -- @field CargoBayCapacityValues.Ground + POSITIONABLE.CargoBayCapacityValues = { + ["Air"] = { + -- C-17A + -- Wiki says: max=265,352, empty=128,140, payload=77,516 (134 troops, 1 M1 Abrams tank, 2 M2 Bradley or 3 Stryker) + -- DCS says: max=265,350, empty=125,645, fuel=132,405 ==> Cargo Bay=7300 kg with a full fuel load (lot of fuel!) and 73300 with half a fuel load. + --["C-17A"] = 35000, --77519 cannot be used, because it loads way too much apcs and infantry. + -- C-130: + -- DCS says: max=79,380, empty=36,400, fuel=10,415 kg ==> Cargo Bay=32,565 kg with fuel load. + -- Wiki says: max=70,307, empty=34,382, payload=19,000 kg (92 passengers, 2-3 Humvees or 2 M113s), max takeoff weight 70,037 kg. + -- Here we say two M113s should be transported. Each one weights 11,253 kg according to DCS. So the cargo weight should be 23,000 kg with a full load of fuel. + -- This results in a max takeoff weight of 69,815 kg (23,000+10,415+36,400), which is very close to the Wiki value of 70,037 kg. + ["C_130"] = 70000, + }, + ["Naval"] = { + ["Type_071"] = 245000, + ["LHA_Tarawa"] = 500000, + ["Ropucha_class"] = 150000, + ["Dry_cargo_ship_1"] = 70000, + ["Dry_cargo_ship_2"] = 70000, + ["Higgins_boat"] = 3700, -- Higgins Boat can load 3700 kg of general cargo or 36 men (source wikipedia). + ["USS_Samuel_Chase"] = 25000, -- Let's say 25 tons for now. Wiki says 33 Higgins boats, which would be 264 tons (can't be right!) and/or 578 troops. + ["LST_Mk2"] = 2100000, -- Can carry 2100 tons according to wiki source! + ["speedboat"] = 500, -- 500 kg ~ 5 persons + ["Seawise_Giant"] =261000000, -- Gross tonnage is 261,000 tonns. + }, + ["Ground"] = { + ["AAV7"] = 25*POSITIONABLE.DefaultInfantryWeight, + ["Bedford_MWD"] = 8*POSITIONABLE.DefaultInfantryWeight, -- new by kappa + ["Blitz_36_6700A"] = 10*POSITIONABLE.DefaultInfantryWeight, -- new by kappa + ["BMD_1"] = 9*POSITIONABLE.DefaultInfantryWeight, -- IRL should be 4 passengers + ["BMP_1"] = 8*POSITIONABLE.DefaultInfantryWeight, + ["BMP_2"] = 7*POSITIONABLE.DefaultInfantryWeight, + ["BMP_3"] = 8*POSITIONABLE.DefaultInfantryWeight, -- IRL should be 7+2 passengers + ["Boman"] = 25*POSITIONABLE.DefaultInfantryWeight, + ["BTR_80"] = 9*POSITIONABLE.DefaultInfantryWeight, -- IRL should be 7 passengers + ["BTR_82A"] = 9*POSITIONABLE.DefaultInfantryWeight, -- new by kappa -- IRL should be 7 passengers + ["BTR_D"] = 12*POSITIONABLE.DefaultInfantryWeight, -- IRL should be 10 passengers + ["Cobra"] = 8*POSITIONABLE.DefaultInfantryWeight, + ["Land_Rover_101_FC"] = 11*POSITIONABLE.DefaultInfantryWeight, -- new by kappa + ["Land_Rover_109_S3"] = 7*POSITIONABLE.DefaultInfantryWeight, -- new by kappa + ["LAV_25"] = 6*POSITIONABLE.DefaultInfantryWeight, + ["M_2_Bradley"] = 6*POSITIONABLE.DefaultInfantryWeight, + ["M1043_HMMWV_Armament"] = 4*POSITIONABLE.DefaultInfantryWeight, + ["M1045_HMMWV_TOW"] = 4*POSITIONABLE.DefaultInfantryWeight, + ["M1126_Stryker_ICV"] = 9*POSITIONABLE.DefaultInfantryWeight, + ["M1134_Stryker_ATGM"] = 9*POSITIONABLE.DefaultInfantryWeight, + ["M2A1_halftrack"] = 9*POSITIONABLE.DefaultInfantryWeight, + ["M_113"] = 9*POSITIONABLE.DefaultInfantryWeight, -- IRL should be 11 passengers + ["Marder"] = 6*POSITIONABLE.DefaultInfantryWeight, + ["MCV_80"] = 9*POSITIONABLE.DefaultInfantryWeight, -- IRL should be 7 passengers + ["MLRS_FDDM"] = 4*POSITIONABLE.DefaultInfantryWeight, + ["MTLB"] = 25*POSITIONABLE.DefaultInfantryWeight, -- IRL should be 11 passengers + ["GAZ_66"] = 8*POSITIONABLE.DefaultInfantryWeight, + ["GAZ_3307"] = 12*POSITIONABLE.DefaultInfantryWeight, + ["GAZ_3308"] = 14*POSITIONABLE.DefaultInfantryWeight, + ["Grad_FDDM"] = 6*POSITIONABLE.DefaultInfantryWeight, -- new by kappa + ["KAMAZ_Truck"] = 12*POSITIONABLE.DefaultInfantryWeight, + ["KrAZ6322"] = 12*POSITIONABLE.DefaultInfantryWeight, + ["M_818"] = 12*POSITIONABLE.DefaultInfantryWeight, + ["Tigr_233036"] = 6*POSITIONABLE.DefaultInfantryWeight, + ["TPZ"] = 10*POSITIONABLE.DefaultInfantryWeight, -- Fuchs + ["UAZ_469"] = 4*POSITIONABLE.DefaultInfantryWeight, -- new by kappa + ["Ural_375"] = 12*POSITIONABLE.DefaultInfantryWeight, + ["Ural_4320_31"] = 14*POSITIONABLE.DefaultInfantryWeight, + ["Ural_4320_APA_5D"] = 10*POSITIONABLE.DefaultInfantryWeight, + ["Ural_4320T"] = 14*POSITIONABLE.DefaultInfantryWeight, + ["ZBD04A"] = 7*POSITIONABLE.DefaultInfantryWeight, -- new by kappa + ["VAB_Mephisto"] = 8*POSITIONABLE.DefaultInfantryWeight, -- new by Apple + ["tt_KORD"] = 6*POSITIONABLE.DefaultInfantryWeight, -- 2.7.1 HL/TT + ["tt_DSHK"] = 6*POSITIONABLE.DefaultInfantryWeight, + ["HL_KORD"] = 6*POSITIONABLE.DefaultInfantryWeight, + ["HL_DSHK"] = 6*POSITIONABLE.DefaultInfantryWeight, + ["CCKW_353"] = 16*POSITIONABLE.DefaultInfantryWeight, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers, + } + } --- Set Cargo Bay Weight Limit in kg. -- @param #POSITIONABLE self @@ -1792,23 +1879,13 @@ do -- Cargo -- Unit type name. local TypeName=Desc.typeName or "Unknown Type" - + TypeName = string.gsub(TypeName,"[%p%s]","_") + -- When an airplane or helicopter, we calculate the WeightLimit based on the descriptor. if self:IsAir() then -- Max takeoff weight if DCS descriptors have unrealstic values. - local Weights = { - -- C-17A - -- Wiki says: max=265,352, empty=128,140, payload=77,516 (134 troops, 1 M1 Abrams tank, 2 M2 Bradley or 3 Stryker) - -- DCS says: max=265,350, empty=125,645, fuel=132,405 ==> Cargo Bay=7300 kg with a full fuel load (lot of fuel!) and 73300 with half a fuel load. - --["C-17A"] = 35000, --77519 cannot be used, because it loads way too much apcs and infantry. - -- C-130: - -- DCS says: max=79,380, empty=36,400, fuel=10,415 kg ==> Cargo Bay=32,565 kg with fuel load. - -- Wiki says: max=70,307, empty=34,382, payload=19,000 kg (92 passengers, 2-3 Humvees or 2 M113s), max takeoff weight 70,037 kg. - -- Here we say two M113s should be transported. Each one weights 11,253 kg according to DCS. So the cargo weight should be 23,000 kg with a full load of fuel. - -- This results in a max takeoff weight of 69,815 kg (23,000+10,415+36,400), which is very close to the Wiki value of 70,037 kg. - ["C-130"] = 70000, - } + local Weights = POSITIONABLE.CargoBayCapacityValues.Air -- Max (takeoff) weight (empty+fuel+cargo weight). local massMax= Desc.massMax or 0 @@ -1843,75 +1920,16 @@ do -- Cargo elseif self:IsShip() then -- Hard coded cargo weights in kg. - local Weights = { - ["Type_071"] = 245000, - ["LHA_Tarawa"] = 500000, - ["Ropucha-class"] = 150000, - ["Dry-cargo ship-1"] = 70000, - ["Dry-cargo ship-2"] = 70000, - ["Higgins_boat"] = 3700, -- Higgins Boat can load 3700 kg of general cargo or 36 men (source wikipedia). - ["USS_Samuel_Chase"] = 25000, -- Let's say 25 tons for now. Wiki says 33 Higgins boats, which would be 264 tons (can't be right!) and/or 578 troops. - ["LST_Mk2"] = 2100000, -- Can carry 2100 tons according to wiki source! - ["speedboat"] = 500, -- 500 kg ~ 5 persons - ["Seawise_Giant"] =261000000, -- Gross tonnage is 261,000 tonns. - } + local Weights = POSITIONABLE.CargoBayCapacityValues.Naval self.__.CargoBayWeightLimit = ( Weights[TypeName] or 50000 ) else -- Hard coded number of soldiers. - local Weights = { - ["AAV7"] = 25, - ["Bedford_MWD"] = 8, -- new by kappa - ["Blitz_36-6700A"] = 10, -- new by kappa - ["BMD-1"] = 9, -- IRL should be 4 passengers - ["BMP-1"] = 8, - ["BMP-2"] = 7, - ["BMP-3"] = 8, -- IRL should be 7+2 passengers - ["Boman"] = 25, - ["BTR-80"] = 9, -- IRL should be 7 passengers - ["BTR-82A"] = 9, -- new by kappa -- IRL should be 7 passengers - ["BTR_D"] = 12, -- IRL should be 10 passengers - ["Cobra"] = 8, - ["Land_Rover_101_FC"] = 11, -- new by kappa - ["Land_Rover_109_S3"] = 7, -- new by kappa - ["LAV-25"] = 6, - ["M-2 Bradley"] = 6, - ["M1043 HMMWV Armament"] = 4, - ["M1045 HMMWV TOW"] = 4, - ["M1126 Stryker ICV"] = 9, - ["M1134 Stryker ATGM"] = 9, - ["M2A1_halftrack"] = 9, - ["M-113"] = 9, -- IRL should be 11 passengers - ["Marder"] = 6, - ["MCV-80"] = 9, -- IRL should be 7 passengers - ["MLRS FDDM"] = 4, - ["MTLB"] = 25, -- IRL should be 11 passengers - ["GAZ-66"] = 8, - ["GAZ-3307"] = 12, - ["GAZ-3308"] = 14, - ["Grad_FDDM"] = 6, -- new by kappa - ["KAMAZ Truck"] = 12, - ["KrAZ6322"] = 12, - ["M 818"] = 12, - ["Tigr_233036"] = 6, - ["TPZ"] = 10, -- Fuchs - ["UAZ-469"] = 4, -- new by kappa - ["Ural-375"] = 12, - ["Ural-4320-31"] = 14, - ["Ural-4320 APA-5D"] = 10, - ["Ural-4320T"] = 14, - ["ZBD04A"] = 7, -- new by kappa - ["VAB_Mephisto"] = 8, -- new by Apple - ["tt_KORD"] = 6, -- 2.7.1 HL/TT - ["tt_DSHK"] = 6, - ["HL_KORD"] = 6, - ["HL_DSHK"] = 6, - ["CCKW_353"] = 16, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers - } + local Weights = POSITIONABLE.CargoBayCapacityValues.Ground -- Assuming that each passenger weighs 95 kg on average. - local CargoBayWeightLimit = ( Weights[TypeName] or 0 ) * 95 + local CargoBayWeightLimit = ( Weights[TypeName] or 0 ) self.__.CargoBayWeightLimit = CargoBayWeightLimit end From 2ee8516427adb273be889c5955a565d11262cedd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 20 Aug 2023 11:20:07 +0200 Subject: [PATCH 325/603] Fixes --- Moose Development/Moose/Ops/ATIS.lua | 6 ++++-- Moose Development/Moose/Ops/Auftrag.lua | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index c604c281e..b90973b05 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -46,6 +46,7 @@ -- === -- -- ### Author: **funkyfranky** +-- ### Additions for SRS and FARP: **applevangelist** -- -- @module Ops.ATIS -- @image OPS_ATIS.png @@ -615,7 +616,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.9.15" +ATIS.version = "0.9.16" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -2158,8 +2159,9 @@ function ATIS:onafterBroadcast( From, Event, To ) if not self.ATISforFARPs then -- Active runway. + local subtitle if runwayLanding then - local subtitle=string.format("Active runway %s", runwayLanding) + subtitle=string.format("Active runway %s", runwayLanding) if rwyLandingLeft==true then subtitle=subtitle.." Left" elseif rwyLandingLeft==false then diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 1bf4f8710..12fb98427 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1336,9 +1336,9 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ return mission end ---- **[AIR]** Create a CAP mission on a group. +--- **[AIR]** Create a CAP mission over a (moving) group. -- @param #AUFTRAG self --- @param Wrapper.Group#GROUP Grp. +-- @param Wrapper.Group#GROUP Grp The grp to perform the CAP over. -- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft. -- @param #number Speed Orbit speed in knots. Default 250 KIAS. -- @param #number RelHeading Relative heading [0, 360) of race-track pattern in degrees wrt heading of the carrier. Default is heading of the carrier. From 7f4baaae4debdcce19e8c508e0106cd66c3b30f2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 20 Aug 2023 13:27:46 +0200 Subject: [PATCH 326/603] Fix for #1984 --- Moose Development/Moose/Functional/Range.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index a6b6606e9..ab44ef3cd 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -591,7 +591,7 @@ RANGE.MenuF10Root = nil --- Range script version. -- @field #string version -RANGE.version = "2.7.0" +RANGE.version = "2.7.1" -- TODO list: -- TODO: Verbosity level for messages. @@ -2044,6 +2044,7 @@ function RANGE:OnEventShot( EventData ) -- Attack parameters. local attackHdg=_unit:GetHeading() local attackAlt=_unit:GetHeight() + attackAlt = UTILS.MetersToFeet(attackAlt) local attackVel=_unit:GetVelocityKNOTS() -- Tracking info and init of last bomb position. From a0e8f7f3a93bf71db5f6d769f57b8429654df0d4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 20 Aug 2023 14:17:39 +0200 Subject: [PATCH 327/603] #PLAYERRECCE * QoL Fixes for menus --- Moose Development/Moose/Ops/PlayerRecce.lua | 29 ++++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index d8e9d8dc8..120afb451 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -104,7 +104,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.17", + version = "0.0.18", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, @@ -807,6 +807,7 @@ function PLAYERRECCE:_SetClientLaserCode(client,group,playername,code) self.ClientMenus[playername]:Remove() self.ClientMenus[playername]=nil end + self:_BuildMenus() return self end @@ -829,6 +830,7 @@ function PLAYERRECCE:_SwitchOnStation(client,group,playername) self.ClientMenus[playername]:Remove() self.ClientMenus[playername]=nil end + self:_BuildMenus(client) return self end @@ -851,6 +853,7 @@ function PLAYERRECCE:_SwitchSmoke(client,group,playername) self.ClientMenus[playername]:Remove() self.ClientMenus[playername]=nil end + self:_BuildMenus(client) return self end @@ -873,6 +876,7 @@ function PLAYERRECCE:_SwitchLasing(client,group,playername) self.ClientMenus[playername]:Remove() self.ClientMenus[playername]=nil end + self:_BuildMenus(client) return self end @@ -902,6 +906,7 @@ function PLAYERRECCE:_SwitchLasingDist(client,group,playername,mindist,maxdist) self.ClientMenus[playername]:Remove() self.ClientMenus[playername]=nil end + self:_BuildMenus(client) return self end @@ -930,6 +935,8 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) if cameraset:CountAlive() > 0 then self:__TargetsSmoked(-1,client,playername,cameraset) + else + return self end local highsmoke = self.SmokeColor.highsmoke @@ -1088,14 +1095,14 @@ self:T(self.lid.."_ReportLaserTargets") report:Add("Laser Code: "..self.UnitLaserCodes[playername] or 1688) report:Add(string.rep("-",15)) local text = report:Text() - self:__TargetReport(-1,client,targetset,target,text) + self:__TargetReport(1,client,targetset,target,text) else local report = REPORT:New("Lasing Report") report:Add(string.rep("-",15)) report:Add("N O T A R G E T S") report:Add(string.rep("-",15)) local text = report:Text() - self:__TargetReport(-1,client,nil,nil,text) + self:__TargetReport(1,client,nil,nil,text) end return self end @@ -1130,25 +1137,27 @@ function PLAYERRECCE:_ReportVisualTargets(client,group,playername) end report:Add(string.rep("-",15)) local text = report:Text() - self:__TargetReport(-1,client,targetset,nil,text) + self:__TargetReport(1,client,targetset,nil,text) else local report = REPORT:New("Target Report") report:Add(string.rep("-",15)) report:Add("N O T A R G E T S") report:Add(string.rep("-",15)) local text = report:Text() - self:__TargetReport(-1,client,nil,nil,text) + self:__TargetReport(1,client,nil,nil,text) end return self end ---- [Internal] +--- [Internal] Build Menus -- @param #PLAYERRECCE self --- @param #PLAYERRECCE self -function PLAYERRECCE:_BuildMenus() +-- @param Wrapper.Client#CLIENT Client (optional) Client object +-- @return #PLAYERRECCE self +function PLAYERRECCE:_BuildMenus(Client) self:T(self.lid.."_BuildMenus") local clients = self.PlayerSet -- Core.Set#SET_CLIENT local clientset = clients:GetSetObjects() + if Client then clientset = {Client} end for _,_client in pairs(clientset) do local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then @@ -1156,7 +1165,7 @@ function PLAYERRECCE:_BuildMenus() if not self.UnitLaserCodes[playername] then self:_SetClientLaserCode(nil,nil,playername,1688) end - if not self.SmokeOwn[playername] then + if self.SmokeOwn[playername] == nil then self.SmokeOwn[playername] = self.smokeownposition end local group = client:GetGroup() @@ -1919,7 +1928,7 @@ function PLAYERRECCE:onafterTargetReport(From, Event, To, Client, TargetSet, Tar -- send message to AttackSet for _,_client in pairs(self.AttackSet.Set) do local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then + if client and client:IsAlive() and client ~= Client then MESSAGE:New(Text,45,self.Name or "FACA"):ToClient(client) end end From 0460cc7e3ee26411281a5cae9ecedf93ee543e6f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 20 Aug 2023 15:13:32 +0200 Subject: [PATCH 328/603] ENUMS.Storage.weapons --- Moose Development/Moose/Utilities/Enums.lua | 576 ++++++++++++++++++++ 1 file changed, 576 insertions(+) diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index b00552dcf..3d15cedd5 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -562,3 +562,579 @@ ENUMS.ReportingName = Predator = "MQ-1A", } } + + + +--- Enums for the STORAGE class for stores - which need to be in "" +-- @type ENUMS.Storage +-- @type ENUMS.Storage.weapons +ENUMS.Storage = { + weapons = { + missiles = {}, -- Missiles + bombs = {}, -- Bombs + nurs = {}, -- Rockets and unguided + containers = {}, -- Containers + droptanks = {}, -- Droptanks + adapters = {}, -- Adapter + torpedoes = {}, -- Torpedoes + } +} + +ENUMS.Storage.weapons.nurs.SNEB_TYPE253_F1B = "weapons.nurs.SNEB_TYPE253_F1B" +ENUMS.Storage.weapons.missiles.P_24T = "weapons.missiles.P_24T" +ENUMS.Storage.weapons.bombs.BLU_3B_OLD = "weapons.bombs.BLU-3B_OLD" +ENUMS.Storage.weapons.missiles.AGM_154 = "weapons.missiles.AGM_154" +ENUMS.Storage.weapons.nurs.HYDRA_70_M151_M433 = "weapons.nurs.HYDRA_70_M151_M433" +ENUMS.Storage.weapons.bombs.SAM_Avenger_M1097_Skid_7090lb = "weapons.bombs.SAM Avenger M1097 Skid [7090lb]" +ENUMS.Storage.weapons.bombs.British_GP_250LB_Bomb_Mk5 = "weapons.bombs.British_GP_250LB_Bomb_Mk5" +ENUMS.Storage.weapons.containers._OV10_SMOKE = "weapons.containers.{OV10_SMOKE}" +ENUMS.Storage.weapons.bombs.BLU_4B_OLD = "weapons.bombs.BLU-4B_OLD" +ENUMS.Storage.weapons.bombs.FAB_500M54 = "weapons.bombs.FAB-500M54" +ENUMS.Storage.weapons.bombs.GBU_38 = "weapons.bombs.GBU_38" +ENUMS.Storage.weapons.containers.F_15E_AXQ_14_DATALINK = "weapons.containers.F-15E_AXQ-14_DATALINK" +ENUMS.Storage.weapons.bombs.BEER_BOMB = "weapons.bombs.BEER_BOMB" +ENUMS.Storage.weapons.bombs.P_50T = "weapons.bombs.P-50T" +ENUMS.Storage.weapons.nurs.C_8CM_GN = "weapons.nurs.C_8CM_GN" +ENUMS.Storage.weapons.bombs.FAB_500SL = "weapons.bombs.FAB-500SL" +ENUMS.Storage.weapons.bombs.KAB_1500Kr = "weapons.bombs.KAB_1500Kr" +ENUMS.Storage.weapons.bombs.two50_2 = "weapons.bombs.250-2" +ENUMS.Storage.weapons.droptanks.Spitfire_tank_1 = "weapons.droptanks.Spitfire_tank_1" +ENUMS.Storage.weapons.missiles.AGM_65G = "weapons.missiles.AGM_65G" +ENUMS.Storage.weapons.missiles.AGM_65A = "weapons.missiles.AGM_65A" +ENUMS.Storage.weapons.containers.Hercules_JATO = "weapons.containers.Hercules_JATO" +ENUMS.Storage.weapons.nurs.HYDRA_70_M259 = "weapons.nurs.HYDRA_70_M259" +ENUMS.Storage.weapons.missiles.AGM_84E = "weapons.missiles.AGM_84E" +ENUMS.Storage.weapons.bombs.AN_M30A1 = "weapons.bombs.AN_M30A1" +ENUMS.Storage.weapons.nurs.C_25 = "weapons.nurs.C_25" +ENUMS.Storage.weapons.containers.AV8BNA_ALQ164 = "weapons.containers.AV8BNA_ALQ164" +ENUMS.Storage.weapons.containers.lav_25 = "weapons.containers.lav-25" +ENUMS.Storage.weapons.missiles.P_60 = "weapons.missiles.P_60" +ENUMS.Storage.weapons.bombs.FAB_1500 = "weapons.bombs.FAB_1500" +ENUMS.Storage.weapons.droptanks.FuelTank_350L = "weapons.droptanks.FuelTank_350L" +ENUMS.Storage.weapons.bombs.AAA_Vulcan_M163_Skid_21577lb = "weapons.bombs.AAA Vulcan M163 Skid [21577lb]" +ENUMS.Storage.weapons.missiles.Kormoran = "weapons.missiles.Kormoran" +ENUMS.Storage.weapons.droptanks.HB_F14_EXT_DROPTANK_EMPTY = "weapons.droptanks.HB_F14_EXT_DROPTANK_EMPTY" +ENUMS.Storage.weapons.droptanks.FuelTank_150L = "weapons.droptanks.FuelTank_150L" +ENUMS.Storage.weapons.missiles.Rb_15F_for_A_I = "weapons.missiles.Rb 15F (for A.I.)" +ENUMS.Storage.weapons.missiles.RB75T = "weapons.missiles.RB75T" +ENUMS.Storage.weapons.missiles.Vikhr_M = "weapons.missiles.Vikhr_M" +ENUMS.Storage.weapons.nurs.FFAR_M156_WP = "weapons.nurs.FFAR M156 WP" +ENUMS.Storage.weapons.nurs.British_HE_60LBSAPNo2_3INCHNo1 = "weapons.nurs.British_HE_60LBSAPNo2_3INCHNo1" +ENUMS.Storage.weapons.missiles.DWS39_MJ2 = "weapons.missiles.DWS39_MJ2" +ENUMS.Storage.weapons.bombs.HEBOMBD = "weapons.bombs.HEBOMBD" +ENUMS.Storage.weapons.missiles.CATM_9M = "weapons.missiles.CATM_9M" +ENUMS.Storage.weapons.bombs.Mk_81 = "weapons.bombs.Mk_81" +ENUMS.Storage.weapons.droptanks.Drop_Tank_300_Liter = "weapons.droptanks.Drop_Tank_300_Liter" +ENUMS.Storage.weapons.containers.HMMWV_M1025 = "weapons.containers.HMMWV_M1025" +ENUMS.Storage.weapons.bombs.SAM_CHAPARRAL_Air_21624lb = "weapons.bombs.SAM CHAPARRAL Air [21624lb]" +ENUMS.Storage.weapons.missiles.AGM_154A = "weapons.missiles.AGM_154A" +ENUMS.Storage.weapons.bombs.Mk_84AIR_TP = "weapons.bombs.Mk_84AIR_TP" +ENUMS.Storage.weapons.bombs.GBU_31_V_3B = "weapons.bombs.GBU_31_V_3B" +ENUMS.Storage.weapons.nurs.C_8CM_WH = "weapons.nurs.C_8CM_WH" +ENUMS.Storage.weapons.missiles.Matra_Super_530D = "weapons.missiles.Matra Super 530D" +ENUMS.Storage.weapons.nurs.ARF8M3TPSM = "weapons.nurs.ARF8M3TPSM" +ENUMS.Storage.weapons.missiles.TGM_65H = "weapons.missiles.TGM_65H" +ENUMS.Storage.weapons.nurs.M8rocket = "weapons.nurs.M8rocket" +ENUMS.Storage.weapons.bombs.GBU_27 = "weapons.bombs.GBU_27" +ENUMS.Storage.weapons.missiles.AGR_20A = "weapons.missiles.AGR_20A" +ENUMS.Storage.weapons.missiles.LS_6_250 = "weapons.missiles.LS-6-250" +ENUMS.Storage.weapons.droptanks.M2KC_RPL_522_EMPTY = "weapons.droptanks.M2KC_RPL_522_EMPTY" +ENUMS.Storage.weapons.droptanks.M2KC_02_RPL541 = "weapons.droptanks.M2KC_02_RPL541" +ENUMS.Storage.weapons.missiles.AGM_45 = "weapons.missiles.AGM_45" +ENUMS.Storage.weapons.missiles.AGM_84A = "weapons.missiles.AGM_84A" +ENUMS.Storage.weapons.bombs.APC_BTR_80_Air_23936lb = "weapons.bombs.APC BTR-80 Air [23936lb]" +ENUMS.Storage.weapons.missiles.P_33E = "weapons.missiles.P_33E" +ENUMS.Storage.weapons.missiles.Ataka_9M120 = "weapons.missiles.Ataka_9M120" +ENUMS.Storage.weapons.bombs.MK76 = "weapons.bombs.MK76" +ENUMS.Storage.weapons.bombs.AB_250_2_SD_2 = "weapons.bombs.AB_250_2_SD_2" +ENUMS.Storage.weapons.missiles.Rb_05A = "weapons.missiles.Rb 05A" +ENUMS.Storage.weapons.bombs.ART_GVOZDIKA_34720lb = "weapons.bombs.ART GVOZDIKA [34720lb]" +ENUMS.Storage.weapons.bombs.Generic_Crate_20000lb = "weapons.bombs.Generic Crate [20000lb]" +ENUMS.Storage.weapons.bombs.FAB_100SV = "weapons.bombs.FAB_100SV" +ENUMS.Storage.weapons.bombs.BetAB_500 = "weapons.bombs.BetAB_500" +ENUMS.Storage.weapons.droptanks.M2KC_02_RPL541_EMPTY = "weapons.droptanks.M2KC_02_RPL541_EMPTY" +ENUMS.Storage.weapons.droptanks.PTB600_MIG15 = "weapons.droptanks.PTB600_MIG15" +ENUMS.Storage.weapons.missiles.Rb_24J = "weapons.missiles.Rb 24J" +ENUMS.Storage.weapons.nurs.C_8CM_BU = "weapons.nurs.C_8CM_BU" +ENUMS.Storage.weapons.nurs.SNEB_TYPE259E_F1B = "weapons.nurs.SNEB_TYPE259E_F1B" +ENUMS.Storage.weapons.nurs.WGr21 = "weapons.nurs.WGr21" +ENUMS.Storage.weapons.bombs.SAMP250HD = "weapons.bombs.SAMP250HD" +ENUMS.Storage.weapons.containers.alq_184long = "weapons.containers.alq-184long" +ENUMS.Storage.weapons.nurs.SNEB_TYPE259E_H1 = "weapons.nurs.SNEB_TYPE259E_H1" +ENUMS.Storage.weapons.bombs.British_SAP_250LB_Bomb_Mk5 = "weapons.bombs.British_SAP_250LB_Bomb_Mk5" +ENUMS.Storage.weapons.bombs.Transport_UAZ_469_Air_3747lb = "weapons.bombs.Transport UAZ-469 Air [3747lb]" +ENUMS.Storage.weapons.bombs.Mk_83CT = "weapons.bombs.Mk_83CT" +ENUMS.Storage.weapons.missiles.AIM_7P = "weapons.missiles.AIM-7P" +ENUMS.Storage.weapons.missiles.AT_6 = "weapons.missiles.AT_6" +ENUMS.Storage.weapons.nurs.SNEB_TYPE254_H1_GREEN = "weapons.nurs.SNEB_TYPE254_H1_GREEN" +ENUMS.Storage.weapons.nurs.SNEB_TYPE250_F1B = "weapons.nurs.SNEB_TYPE250_F1B" +ENUMS.Storage.weapons.containers.U22A = "weapons.containers.U22A" +ENUMS.Storage.weapons.bombs.British_GP_250LB_Bomb_Mk1 = "weapons.bombs.British_GP_250LB_Bomb_Mk1" +ENUMS.Storage.weapons.bombs.CBU_105 = "weapons.bombs.CBU_105" +ENUMS.Storage.weapons.droptanks.FW_190_Fuel_Tank = "weapons.droptanks.FW-190_Fuel-Tank" +ENUMS.Storage.weapons.missiles.X_58 = "weapons.missiles.X_58" +ENUMS.Storage.weapons.missiles.BK90_MJ1_MJ2 = "weapons.missiles.BK90_MJ1_MJ2" +ENUMS.Storage.weapons.missiles.TGM_65D = "weapons.missiles.TGM_65D" +ENUMS.Storage.weapons.containers.BRD_4_250 = "weapons.containers.BRD-4-250" +ENUMS.Storage.weapons.missiles.P_73 = "weapons.missiles.P_73" +ENUMS.Storage.weapons.bombs.AN_M66 = "weapons.bombs.AN_M66" +ENUMS.Storage.weapons.bombs.APC_LAV_25_Air_22520lb = "weapons.bombs.APC LAV-25 Air [22520lb]" +ENUMS.Storage.weapons.missiles.AIM_7MH = "weapons.missiles.AIM-7MH" +ENUMS.Storage.weapons.containers.MB339_TravelPod = "weapons.containers.MB339_TravelPod" +ENUMS.Storage.weapons.bombs.GBU_12 = "weapons.bombs.GBU_12" +ENUMS.Storage.weapons.bombs.SC_250_T3_J = "weapons.bombs.SC_250_T3_J" +ENUMS.Storage.weapons.missiles.KD_20 = "weapons.missiles.KD-20" +ENUMS.Storage.weapons.missiles.AGM_86C = "weapons.missiles.AGM_86C" +ENUMS.Storage.weapons.missiles.X_35 = "weapons.missiles.X_35" +ENUMS.Storage.weapons.bombs.MK106 = "weapons.bombs.MK106" +ENUMS.Storage.weapons.bombs.BETAB_500S = "weapons.bombs.BETAB-500S" +ENUMS.Storage.weapons.nurs.C_5 = "weapons.nurs.C_5" +ENUMS.Storage.weapons.nurs.S_24B = "weapons.nurs.S-24B" +ENUMS.Storage.weapons.bombs.British_MC_500LB_Bomb_Mk2 = "weapons.bombs.British_MC_500LB_Bomb_Mk2" +ENUMS.Storage.weapons.containers.ANAWW_13 = "weapons.containers.ANAWW_13" +ENUMS.Storage.weapons.droptanks.droptank_108_gal = "weapons.droptanks.droptank_108_gal" +ENUMS.Storage.weapons.droptanks.DFT_300_GAL_A4E_LR = "weapons.droptanks.DFT_300_GAL_A4E_LR" +ENUMS.Storage.weapons.bombs.CBU_87 = "weapons.bombs.CBU_87" +ENUMS.Storage.weapons.missiles.GAR_8 = "weapons.missiles.GAR-8" +ENUMS.Storage.weapons.bombs.BELOUGA = "weapons.bombs.BELOUGA" +ENUMS.Storage.weapons.containers._EclairM_33 = "weapons.containers.{EclairM_33}" +ENUMS.Storage.weapons.bombs.ART_2S9_NONA_Air_19140lb = "weapons.bombs.ART 2S9 NONA Air [19140lb]" +ENUMS.Storage.weapons.bombs.BR_250 = "weapons.bombs.BR_250" +ENUMS.Storage.weapons.bombs.IAB_500 = "weapons.bombs.IAB-500" +ENUMS.Storage.weapons.containers.AN_ASQ_228 = "weapons.containers.AN_ASQ_228" +ENUMS.Storage.weapons.missiles.P_27P = "weapons.missiles.P_27P" +ENUMS.Storage.weapons.bombs.SD_250_Stg = "weapons.bombs.SD_250_Stg" +ENUMS.Storage.weapons.missiles.R_530F_IR = "weapons.missiles.R_530F_IR" +ENUMS.Storage.weapons.bombs.British_SAP_500LB_Bomb_Mk5 = "weapons.bombs.British_SAP_500LB_Bomb_Mk5" +ENUMS.Storage.weapons.bombs.FAB_250M54 = "weapons.bombs.FAB-250M54" +ENUMS.Storage.weapons.containers._M2KC_AAF = "weapons.containers.{M2KC_AAF}" +ENUMS.Storage.weapons.missiles.CM_802AKG_AI = "weapons.missiles.CM-802AKG_AI" +ENUMS.Storage.weapons.bombs.CBU_103 = "weapons.bombs.CBU_103" +ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_RED = "weapons.containers.{US_M10_SMOKE_TANK_RED}" +ENUMS.Storage.weapons.missiles.X_29T = "weapons.missiles.X_29T" +ENUMS.Storage.weapons.bombs.HEMTT_TFFT_34400lb = "weapons.bombs.HEMTT TFFT [34400lb]" +ENUMS.Storage.weapons.missiles.C_701IR = "weapons.missiles.C-701IR" +ENUMS.Storage.weapons.containers.fullCargoSeats = "weapons.containers.fullCargoSeats" +ENUMS.Storage.weapons.bombs.GBU_15_V_31_B = "weapons.bombs.GBU_15_V_31_B" +ENUMS.Storage.weapons.bombs.APC_M1043_HMMWV_Armament_Air_7023lb = "weapons.bombs.APC M1043 HMMWV Armament Air [7023lb]" +ENUMS.Storage.weapons.missiles.PL_5EII = "weapons.missiles.PL-5EII" +ENUMS.Storage.weapons.bombs.SC_250_T1_L2 = "weapons.bombs.SC_250_T1_L2" +ENUMS.Storage.weapons.torpedoes.mk46torp_name = "weapons.torpedoes.mk46torp_name" +ENUMS.Storage.weapons.containers.F_15E_AAQ_33_XR_ATP_SE = "weapons.containers.F-15E_AAQ-33_XR_ATP-SE" +ENUMS.Storage.weapons.missiles.AIM_7 = "weapons.missiles.AIM_7" +ENUMS.Storage.weapons.missiles.AGM_122 = "weapons.missiles.AGM_122" +ENUMS.Storage.weapons.bombs.HEBOMB = "weapons.bombs.HEBOMB" +ENUMS.Storage.weapons.bombs.CBU_97 = "weapons.bombs.CBU_97" +ENUMS.Storage.weapons.bombs.MK_81SE = "weapons.bombs.MK-81SE" +ENUMS.Storage.weapons.nurs.Zuni_127 = "weapons.nurs.Zuni_127" +ENUMS.Storage.weapons.containers._M2KC_AGF = "weapons.containers.{M2KC_AGF}" +ENUMS.Storage.weapons.droptanks.Hercules_ExtFuelTank = "weapons.droptanks.Hercules_ExtFuelTank" +ENUMS.Storage.weapons.containers._SMOKE_WHITE = "weapons.containers.{SMOKE_WHITE}" +ENUMS.Storage.weapons.droptanks.droptank_150_gal = "weapons.droptanks.droptank_150_gal" +ENUMS.Storage.weapons.nurs.HYDRA_70_WTU1B = "weapons.nurs.HYDRA_70_WTU1B" +ENUMS.Storage.weapons.missiles.GB_6_SFW = "weapons.missiles.GB-6-SFW" +ENUMS.Storage.weapons.missiles.KD_63 = "weapons.missiles.KD-63" +ENUMS.Storage.weapons.bombs.GBU_28 = "weapons.bombs.GBU_28" +ENUMS.Storage.weapons.nurs.C_8CM_YE = "weapons.nurs.C_8CM_YE" +ENUMS.Storage.weapons.droptanks.HB_F14_EXT_DROPTANK = "weapons.droptanks.HB_F14_EXT_DROPTANK" +ENUMS.Storage.weapons.missiles.Super_530F = "weapons.missiles.Super_530F" +ENUMS.Storage.weapons.missiles.Ataka_9M220 = "weapons.missiles.Ataka_9M220" +ENUMS.Storage.weapons.bombs.BDU_33 = "weapons.bombs.BDU_33" +ENUMS.Storage.weapons.bombs.British_GP_250LB_Bomb_Mk4 = "weapons.bombs.British_GP_250LB_Bomb_Mk4" +ENUMS.Storage.weapons.missiles.TOW = "weapons.missiles.TOW" +ENUMS.Storage.weapons.bombs.ATGM_M1045_HMMWV_TOW_Air_7183lb = "weapons.bombs.ATGM M1045 HMMWV TOW Air [7183lb]" +ENUMS.Storage.weapons.missiles.X_25MR = "weapons.missiles.X_25MR" +ENUMS.Storage.weapons.droptanks.fueltank230 = "weapons.droptanks.fueltank230" +ENUMS.Storage.weapons.droptanks.PTB_490C_MIG21 = "weapons.droptanks.PTB-490C-MIG21" +ENUMS.Storage.weapons.bombs.M1025_HMMWV_Air_6160lb = "weapons.bombs.M1025 HMMWV Air [6160lb]" +ENUMS.Storage.weapons.nurs.SNEB_TYPE254_F1B_GREEN = "weapons.nurs.SNEB_TYPE254_F1B_GREEN" +ENUMS.Storage.weapons.missiles.R_550 = "weapons.missiles.R_550" +ENUMS.Storage.weapons.bombs.KAB_1500LG = "weapons.bombs.KAB_1500LG" +ENUMS.Storage.weapons.missiles.AGM_84D = "weapons.missiles.AGM_84D" +ENUMS.Storage.weapons.missiles.YJ_83K = "weapons.missiles.YJ-83K" +ENUMS.Storage.weapons.missiles.AIM_54C_Mk47 = "weapons.missiles.AIM_54C_Mk47" +ENUMS.Storage.weapons.missiles.BRM_1_90MM = "weapons.missiles.BRM-1_90MM" +ENUMS.Storage.weapons.missiles.Ataka_9M120F = "weapons.missiles.Ataka_9M120F" +ENUMS.Storage.weapons.droptanks.Eleven00L_Tank = "weapons.droptanks.1100L Tank" +ENUMS.Storage.weapons.bombs.BAP_100 = "weapons.bombs.BAP_100" +ENUMS.Storage.weapons.adapters.lau_88 = "weapons.adapters.lau-88" +ENUMS.Storage.weapons.missiles.P_40T = "weapons.missiles.P_40T" +ENUMS.Storage.weapons.missiles.GB_6 = "weapons.missiles.GB-6" +ENUMS.Storage.weapons.bombs.FAB_250M54TU = "weapons.bombs.FAB-250M54TU" +ENUMS.Storage.weapons.missiles.DWS39_MJ1 = "weapons.missiles.DWS39_MJ1" +ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM-802AKG" +ENUMS.Storage.weapons.bombs.FAB_250 = "weapons.bombs.FAB_250" +ENUMS.Storage.weapons.missiles.C_802AK = "weapons.missiles.C_802AK" +ENUMS.Storage.weapons.bombs.SD_500_A = "weapons.bombs.SD_500_A" +ENUMS.Storage.weapons.bombs.GBU_32_V_2B = "weapons.bombs.GBU_32_V_2B" +ENUMS.Storage.weapons.containers.marder = "weapons.containers.marder" +ENUMS.Storage.weapons.missiles.ADM_141B = "weapons.missiles.ADM_141B" +ENUMS.Storage.weapons.bombs.ROCKEYE = "weapons.bombs.ROCKEYE" +ENUMS.Storage.weapons.missiles.BK90_MJ1 = "weapons.missiles.BK90_MJ1" +ENUMS.Storage.weapons.containers.BTR_80 = "weapons.containers.BTR-80" +ENUMS.Storage.weapons.bombs.SAM_ROLAND_ADS_34720lb = "weapons.bombs.SAM ROLAND ADS [34720lb]" +ENUMS.Storage.weapons.containers.wmd7 = "weapons.containers.wmd7" +ENUMS.Storage.weapons.missiles.C_701T = "weapons.missiles.C-701T" +ENUMS.Storage.weapons.missiles.AIM_7E_2 = "weapons.missiles.AIM-7E-2" +ENUMS.Storage.weapons.nurs.HVAR = "weapons.nurs.HVAR" +ENUMS.Storage.weapons.containers.HMMWV_M1043 = "weapons.containers.HMMWV_M1043" +ENUMS.Storage.weapons.droptanks.PTB_800_MIG21 = "weapons.droptanks.PTB-800-MIG21" +ENUMS.Storage.weapons.missiles.AGM_114 = "weapons.missiles.AGM_114" +ENUMS.Storage.weapons.bombs.APC_M1126_Stryker_ICV_29542lb = "weapons.bombs.APC M1126 Stryker ICV [29542lb]" +ENUMS.Storage.weapons.bombs.APC_M113_Air_21624lb = "weapons.bombs.APC M113 Air [21624lb]" +ENUMS.Storage.weapons.bombs.M_117 = "weapons.bombs.M_117" +ENUMS.Storage.weapons.missiles.AGM_65D = "weapons.missiles.AGM_65D" +ENUMS.Storage.weapons.droptanks.MB339_TT320_L = "weapons.droptanks.MB339_TT320_L" +ENUMS.Storage.weapons.missiles.AGM_86 = "weapons.missiles.AGM_86" +ENUMS.Storage.weapons.bombs.BDU_45LGB = "weapons.bombs.BDU_45LGB" +ENUMS.Storage.weapons.missiles.AGM_65H = "weapons.missiles.AGM_65H" +ENUMS.Storage.weapons.nurs.RS_82 = "weapons.nurs.RS-82" +ENUMS.Storage.weapons.nurs.SNEB_TYPE252_F1B = "weapons.nurs.SNEB_TYPE252_F1B" +ENUMS.Storage.weapons.bombs.BL_755 = "weapons.bombs.BL_755" +ENUMS.Storage.weapons.containers.F_15E_AAQ_28_LITENING = "weapons.containers.F-15E_AAQ-28_LITENING" +ENUMS.Storage.weapons.nurs.SNEB_TYPE256_F1B = "weapons.nurs.SNEB_TYPE256_F1B" +ENUMS.Storage.weapons.missiles.AGM_84H = "weapons.missiles.AGM_84H" +ENUMS.Storage.weapons.missiles.AIM_54 = "weapons.missiles.AIM_54" +ENUMS.Storage.weapons.missiles.X_31A = "weapons.missiles.X_31A" +ENUMS.Storage.weapons.bombs.KAB_500Kr = "weapons.bombs.KAB_500Kr" +ENUMS.Storage.weapons.containers.SPS_141_100 = "weapons.containers.SPS-141-100" +ENUMS.Storage.weapons.missiles.BK90_MJ2 = "weapons.missiles.BK90_MJ2" +ENUMS.Storage.weapons.missiles.Super_530D = "weapons.missiles.Super_530D" +ENUMS.Storage.weapons.bombs.CBU_52B = "weapons.bombs.CBU_52B" +ENUMS.Storage.weapons.droptanks.PTB_450 = "weapons.droptanks.PTB-450" +ENUMS.Storage.weapons.bombs.IFV_MCV_80_34720lb = "weapons.bombs.IFV MCV-80 [34720lb]" +ENUMS.Storage.weapons.containers.Two_c9 = "weapons.containers.2-c9" +ENUMS.Storage.weapons.missiles.AIM_9JULI = "weapons.missiles.AIM-9JULI" +ENUMS.Storage.weapons.droptanks.MB339_TT500_R = "weapons.droptanks.MB339_TT500_R" +ENUMS.Storage.weapons.nurs.C_8CM = "weapons.nurs.C_8CM" +ENUMS.Storage.weapons.containers.BARAX = "weapons.containers.BARAX" +ENUMS.Storage.weapons.missiles.P_40R = "weapons.missiles.P_40R" +ENUMS.Storage.weapons.missiles.YJ_12 = "weapons.missiles.YJ-12" +ENUMS.Storage.weapons.missiles.CM_802AKG = "weapons.missiles.CM_802AKG" +ENUMS.Storage.weapons.nurs.SNEB_TYPE254_H1_YELLOW = "weapons.nurs.SNEB_TYPE254_H1_YELLOW" +ENUMS.Storage.weapons.bombs.Durandal = "weapons.bombs.Durandal" +ENUMS.Storage.weapons.droptanks.i16_eft = "weapons.droptanks.i16_eft" +ENUMS.Storage.weapons.droptanks.AV8BNA_AERO1D_EMPTY = "weapons.droptanks.AV8BNA_AERO1D_EMPTY" +ENUMS.Storage.weapons.containers.Hercules_Battle_Station_TGP = "weapons.containers.Hercules_Battle_Station_TGP" +ENUMS.Storage.weapons.nurs.C_8CM_VT = "weapons.nurs.C_8CM_VT" +ENUMS.Storage.weapons.missiles.PL_12 = "weapons.missiles.PL-12" +ENUMS.Storage.weapons.missiles.R_3R = "weapons.missiles.R-3R" +ENUMS.Storage.weapons.bombs.GBU_54_V_1B = "weapons.bombs.GBU_54_V_1B" +ENUMS.Storage.weapons.droptanks.MB339_TT320_R = "weapons.droptanks.MB339_TT320_R" +ENUMS.Storage.weapons.bombs.RN_24 = "weapons.bombs.RN-24" +ENUMS.Storage.weapons.containers.Twoc6m = "weapons.containers.2c6m" +ENUMS.Storage.weapons.bombs.ARV_BRDM_2_Air_12320lb = "weapons.bombs.ARV BRDM-2 Air [12320lb]" +ENUMS.Storage.weapons.bombs.ARV_BRDM_2_Skid_12210lb = "weapons.bombs.ARV BRDM-2 Skid [12210lb]" +ENUMS.Storage.weapons.nurs.SNEB_TYPE251_F1B = "weapons.nurs.SNEB_TYPE251_F1B" +ENUMS.Storage.weapons.missiles.X_41 = "weapons.missiles.X_41" +ENUMS.Storage.weapons.containers._MIG21_SMOKE_WHITE = "weapons.containers.{MIG21_SMOKE_WHITE}" +ENUMS.Storage.weapons.bombs.MK_82AIR = "weapons.bombs.MK_82AIR" +ENUMS.Storage.weapons.missiles.R_530F_EM = "weapons.missiles.R_530F_EM" +ENUMS.Storage.weapons.bombs.SAMP400LD = "weapons.bombs.SAMP400LD" +ENUMS.Storage.weapons.bombs.FAB_50 = "weapons.bombs.FAB_50" +ENUMS.Storage.weapons.bombs.AB_250_2_SD_10A = "weapons.bombs.AB_250_2_SD_10A" +ENUMS.Storage.weapons.missiles.ADM_141A = "weapons.missiles.ADM_141A" +ENUMS.Storage.weapons.containers.KBpod = "weapons.containers.KBpod" +ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk4 = "weapons.bombs.British_GP_500LB_Bomb_Mk4" +ENUMS.Storage.weapons.missiles.AGM_65E = "weapons.missiles.AGM_65E" +ENUMS.Storage.weapons.containers.sa342_dipole_antenna = "weapons.containers.sa342_dipole_antenna" +ENUMS.Storage.weapons.bombs.OFAB_100_Jupiter = "weapons.bombs.OFAB-100 Jupiter" +ENUMS.Storage.weapons.nurs.SNEB_TYPE257_F1B = "weapons.nurs.SNEB_TYPE257_F1B" +ENUMS.Storage.weapons.missiles.Rb_04E_for_A_I = "weapons.missiles.Rb 04E (for A.I.)" +ENUMS.Storage.weapons.bombs.AN_M66A2 = "weapons.bombs.AN-M66A2" +ENUMS.Storage.weapons.missiles.P_27T = "weapons.missiles.P_27T" +ENUMS.Storage.weapons.droptanks.LNS_VIG_XTANK = "weapons.droptanks.LNS_VIG_XTANK" +ENUMS.Storage.weapons.missiles.R_55 = "weapons.missiles.R-55" +ENUMS.Storage.weapons.torpedoes.YU_6 = "weapons.torpedoes.YU-6" +ENUMS.Storage.weapons.bombs.British_MC_250LB_Bomb_Mk2 = "weapons.bombs.British_MC_250LB_Bomb_Mk2" +ENUMS.Storage.weapons.droptanks.PTB_120_F86F35 = "weapons.droptanks.PTB_120_F86F35" +ENUMS.Storage.weapons.missiles.PL_8B = "weapons.missiles.PL-8B" +ENUMS.Storage.weapons.droptanks.F_15E_Drop_Tank_Empty = "weapons.droptanks.F-15E_Drop_Tank_Empty" +ENUMS.Storage.weapons.nurs.British_HE_60LBFNo1_3INCHNo1 = "weapons.nurs.British_HE_60LBFNo1_3INCHNo1" +ENUMS.Storage.weapons.missiles.P_77 = "weapons.missiles.P_77" +ENUMS.Storage.weapons.torpedoes.LTF_5B = "weapons.torpedoes.LTF_5B" +ENUMS.Storage.weapons.missiles.R_3S = "weapons.missiles.R-3S" +ENUMS.Storage.weapons.nurs.SNEB_TYPE253_H1 = "weapons.nurs.SNEB_TYPE253_H1" +ENUMS.Storage.weapons.missiles.PL_8A = "weapons.missiles.PL-8A" +ENUMS.Storage.weapons.bombs.APC_BTR_82A_Skid_24888lb = "weapons.bombs.APC BTR-82A Skid [24888lb]" +ENUMS.Storage.weapons.containers.Sborka = "weapons.containers.Sborka" +ENUMS.Storage.weapons.missiles.AGM_65L = "weapons.missiles.AGM_65L" +ENUMS.Storage.weapons.missiles.X_28 = "weapons.missiles.X_28" +ENUMS.Storage.weapons.missiles.TGM_65G = "weapons.missiles.TGM_65G" +ENUMS.Storage.weapons.nurs.SNEB_TYPE257_H1 = "weapons.nurs.SNEB_TYPE257_H1" +ENUMS.Storage.weapons.missiles.RB75B = "weapons.missiles.RB75B" +ENUMS.Storage.weapons.missiles.X_25ML = "weapons.missiles.X_25ML" +ENUMS.Storage.weapons.droptanks.FPU_8A = "weapons.droptanks.FPU_8A" +ENUMS.Storage.weapons.bombs.BLG66 = "weapons.bombs.BLG66" +ENUMS.Storage.weapons.nurs.C_8CM_RD = "weapons.nurs.C_8CM_RD" +ENUMS.Storage.weapons.containers._EclairM_06 = "weapons.containers.{EclairM_06}" +ENUMS.Storage.weapons.bombs.RBK_500AO = "weapons.bombs.RBK_500AO" +ENUMS.Storage.weapons.missiles.AIM_9P = "weapons.missiles.AIM-9P" +ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk4_Short = "weapons.bombs.British_GP_500LB_Bomb_Mk4_Short" +ENUMS.Storage.weapons.containers.MB339_Vinten = "weapons.containers.MB339_Vinten" +ENUMS.Storage.weapons.missiles.Rb_15F = "weapons.missiles.Rb 15F" +ENUMS.Storage.weapons.nurs.ARAKM70BHE = "weapons.nurs.ARAKM70BHE" +ENUMS.Storage.weapons.bombs.AAA_Vulcan_M163_Air_21666lb = "weapons.bombs.AAA Vulcan M163 Air [21666lb]" +ENUMS.Storage.weapons.missiles.X_29L = "weapons.missiles.X_29L" +ENUMS.Storage.weapons.containers._F14_LANTIRN_TP = "weapons.containers.{F14-LANTIRN-TP}" +ENUMS.Storage.weapons.bombs.FAB_250_M62 = "weapons.bombs.FAB-250-M62" +ENUMS.Storage.weapons.missiles.AIM_120C = "weapons.missiles.AIM_120C" +ENUMS.Storage.weapons.bombs.EWR_SBORKA_Air_21624lb = "weapons.bombs.EWR SBORKA Air [21624lb]" +ENUMS.Storage.weapons.bombs.SAMP250LD = "weapons.bombs.SAMP250LD" +ENUMS.Storage.weapons.droptanks.Spitfire_slipper_tank = "weapons.droptanks.Spitfire_slipper_tank" +ENUMS.Storage.weapons.missiles.LS_6_500 = "weapons.missiles.LS-6-500" +ENUMS.Storage.weapons.bombs.GBU_31_V_4B = "weapons.bombs.GBU_31_V_4B" +ENUMS.Storage.weapons.droptanks.PTB400_MIG15 = "weapons.droptanks.PTB400_MIG15" +ENUMS.Storage.weapons.containers.m_113 = "weapons.containers.m-113" +ENUMS.Storage.weapons.bombs.SPG_M1128_Stryker_MGS_33036lb = "weapons.bombs.SPG M1128 Stryker MGS [33036lb]" +ENUMS.Storage.weapons.missiles.AIM_9L = "weapons.missiles.AIM-9L" +ENUMS.Storage.weapons.missiles.AIM_9X = "weapons.missiles.AIM_9X" +ENUMS.Storage.weapons.nurs.C_8 = "weapons.nurs.C_8" +ENUMS.Storage.weapons.bombs.SAM_CHAPARRAL_Skid_21516lb = "weapons.bombs.SAM CHAPARRAL Skid [21516lb]" +ENUMS.Storage.weapons.missiles.P_27TE = "weapons.missiles.P_27TE" +ENUMS.Storage.weapons.bombs.ODAB_500PM = "weapons.bombs.ODAB-500PM" +ENUMS.Storage.weapons.bombs.MK77mod1_WPN = "weapons.bombs.MK77mod1-WPN" +ENUMS.Storage.weapons.droptanks.PTB400_MIG19 = "weapons.droptanks.PTB400_MIG19" +ENUMS.Storage.weapons.torpedoes.Mark_46 = "weapons.torpedoes.Mark_46" +ENUMS.Storage.weapons.containers.rightSeat = "weapons.containers.rightSeat" +ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_ORANGE = "weapons.containers.{US_M10_SMOKE_TANK_ORANGE}" +ENUMS.Storage.weapons.bombs.SAB_100MN = "weapons.bombs.SAB_100MN" +ENUMS.Storage.weapons.nurs.FFAR_Mk5_HEAT = "weapons.nurs.FFAR Mk5 HEAT" +ENUMS.Storage.weapons.bombs.IFV_TPZ_FUCH_33440lb = "weapons.bombs.IFV TPZ FUCH [33440lb]" +ENUMS.Storage.weapons.bombs.IFV_M2A2_Bradley_34720lb = "weapons.bombs.IFV M2A2 Bradley [34720lb]" +ENUMS.Storage.weapons.bombs.MK77mod0_WPN = "weapons.bombs.MK77mod0-WPN" +ENUMS.Storage.weapons.containers.ASO_2 = "weapons.containers.ASO-2" +ENUMS.Storage.weapons.bombs.Mk_84AIR_GP = "weapons.bombs.Mk_84AIR_GP" +ENUMS.Storage.weapons.nurs.S_24A = "weapons.nurs.S-24A" +ENUMS.Storage.weapons.bombs.RBK_250_275_AO_1SCH = "weapons.bombs.RBK_250_275_AO_1SCH" +ENUMS.Storage.weapons.bombs.Transport_Tigr_Skid_15730lb = "weapons.bombs.Transport Tigr Skid [15730lb]" +ENUMS.Storage.weapons.missiles.AIM_7F = "weapons.missiles.AIM-7F" +ENUMS.Storage.weapons.bombs.CBU_99 = "weapons.bombs.CBU_99" +ENUMS.Storage.weapons.bombs.LUU_2B = "weapons.bombs.LUU_2B" +ENUMS.Storage.weapons.bombs.FAB_500TA = "weapons.bombs.FAB-500TA" +ENUMS.Storage.weapons.missiles.AGR_20_M282 = "weapons.missiles.AGR_20_M282" +ENUMS.Storage.weapons.droptanks.MB339_FT330 = "weapons.droptanks.MB339_FT330" +ENUMS.Storage.weapons.bombs.SAMP125LD = "weapons.bombs.SAMP125LD" +ENUMS.Storage.weapons.missiles.X_25MP = "weapons.missiles.X_25MP" +ENUMS.Storage.weapons.nurs.SNEB_TYPE252_H1 = "weapons.nurs.SNEB_TYPE252_H1" +ENUMS.Storage.weapons.missiles.AGM_65F = "weapons.missiles.AGM_65F" +ENUMS.Storage.weapons.missiles.AIM_9P5 = "weapons.missiles.AIM-9P5" +ENUMS.Storage.weapons.bombs.Transport_Tigr_Air_15900lb = "weapons.bombs.Transport Tigr Air [15900lb]" +ENUMS.Storage.weapons.nurs.SNEB_TYPE254_H1_RED = "weapons.nurs.SNEB_TYPE254_H1_RED" +ENUMS.Storage.weapons.nurs.FFAR_Mk1_HE = "weapons.nurs.FFAR Mk1 HE" +ENUMS.Storage.weapons.nurs.SPRD_99 = "weapons.nurs.SPRD-99" +ENUMS.Storage.weapons.bombs.BIN_200 = "weapons.bombs.BIN_200" +ENUMS.Storage.weapons.bombs.BLU_4B_GROUP = "weapons.bombs.BLU_4B_GROUP" +ENUMS.Storage.weapons.bombs.GBU_24 = "weapons.bombs.GBU_24" +ENUMS.Storage.weapons.missiles.Rb_04E = "weapons.missiles.Rb 04E" +ENUMS.Storage.weapons.missiles.Rb_74 = "weapons.missiles.Rb 74" +ENUMS.Storage.weapons.containers.leftSeat = "weapons.containers.leftSeat" +ENUMS.Storage.weapons.bombs.LS_6_100 = "weapons.bombs.LS-6-100" +ENUMS.Storage.weapons.bombs.Transport_URAL_375_14815lb = "weapons.bombs.Transport URAL-375 [14815lb]" +ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_GREEN = "weapons.containers.{US_M10_SMOKE_TANK_GREEN}" +ENUMS.Storage.weapons.missiles.X_22 = "weapons.missiles.X_22" +ENUMS.Storage.weapons.containers.FAS = "weapons.containers.FAS" +ENUMS.Storage.weapons.nurs.S_25_O = "weapons.nurs.S-25-O" +ENUMS.Storage.weapons.droptanks.para = "weapons.droptanks.para" +ENUMS.Storage.weapons.droptanks.F_15E_Drop_Tank = "weapons.droptanks.F-15E_Drop_Tank" +ENUMS.Storage.weapons.droptanks.M2KC_08_RPL541_EMPTY = "weapons.droptanks.M2KC_08_RPL541_EMPTY" +ENUMS.Storage.weapons.missiles.X_31P = "weapons.missiles.X_31P" +ENUMS.Storage.weapons.bombs.RBK_500U = "weapons.bombs.RBK_500U" +ENUMS.Storage.weapons.missiles.AIM_54A_Mk47 = "weapons.missiles.AIM_54A_Mk47" +ENUMS.Storage.weapons.droptanks.oiltank = "weapons.droptanks.oiltank" +ENUMS.Storage.weapons.missiles.AGM_154B = "weapons.missiles.AGM_154B" +ENUMS.Storage.weapons.containers.MB339_SMOKE_POD = "weapons.containers.MB339_SMOKE-POD" +ENUMS.Storage.weapons.containers._ECM_POD_L_175V = "weapons.containers.{ECM_POD_L_175V}" +ENUMS.Storage.weapons.droptanks.PTB_580G_F1 = "weapons.droptanks.PTB_580G_F1" +ENUMS.Storage.weapons.containers._EclairM_15 = "weapons.containers.{EclairM_15}" +ENUMS.Storage.weapons.containers.F_15E_AAQ_13_LANTIRN = "weapons.containers.F-15E_AAQ-13_LANTIRN" +ENUMS.Storage.weapons.droptanks.Eight00L_Tank_Empty = "weapons.droptanks.800L Tank Empty" +ENUMS.Storage.weapons.containers.One6c_hts_pod = "weapons.containers.16c_hts_pod" +ENUMS.Storage.weapons.bombs.AN_M81 = "weapons.bombs.AN-M81" +ENUMS.Storage.weapons.droptanks.Mosquito_Drop_Tank_100gal = "weapons.droptanks.Mosquito_Drop_Tank_100gal" +ENUMS.Storage.weapons.droptanks.Mosquito_Drop_Tank_50gal = "weapons.droptanks.Mosquito_Drop_Tank_50gal" +ENUMS.Storage.weapons.droptanks.DFT_150_GAL_A4E = "weapons.droptanks.DFT_150_GAL_A4E" +ENUMS.Storage.weapons.missiles.AIM_9 = "weapons.missiles.AIM_9" +ENUMS.Storage.weapons.bombs.IFV_BTR_D_Air_18040lb = "weapons.bombs.IFV BTR-D Air [18040lb]" +ENUMS.Storage.weapons.containers._EclairM_42 = "weapons.containers.{EclairM_42}" +ENUMS.Storage.weapons.bombs.KAB_1500T = "weapons.bombs.KAB_1500T" +ENUMS.Storage.weapons.droptanks.PTB_490_MIG21 = "weapons.droptanks.PTB-490-MIG21" +ENUMS.Storage.weapons.droptanks.PTB_200_F86F35 = "weapons.droptanks.PTB_200_F86F35" +ENUMS.Storage.weapons.droptanks.PTB760_MIG19 = "weapons.droptanks.PTB760_MIG19" +ENUMS.Storage.weapons.bombs.GBU_43_B_MOAB = "weapons.bombs.GBU-43/B(MOAB)" +ENUMS.Storage.weapons.torpedoes.G7A_T1 = "weapons.torpedoes.G7A_T1" +ENUMS.Storage.weapons.bombs.IFV_BMD_1_Air_18040lb = "weapons.bombs.IFV BMD-1 Air [18040lb]" +ENUMS.Storage.weapons.bombs.SAM_LINEBACKER_34720lb = "weapons.bombs.SAM LINEBACKER [34720lb]" +ENUMS.Storage.weapons.containers.ais_pod_t50_r = "weapons.containers.ais-pod-t50_r" +ENUMS.Storage.weapons.containers._CE2_SMOKE_WHITE = "weapons.containers.{CE2_SMOKE_WHITE}" +ENUMS.Storage.weapons.droptanks.fuel_tank_230 = "weapons.droptanks.fuel_tank_230" +ENUMS.Storage.weapons.droptanks.M2KC_RPL_522 = "weapons.droptanks.M2KC_RPL_522" +ENUMS.Storage.weapons.missiles.AGM_130 = "weapons.missiles.AGM_130" +ENUMS.Storage.weapons.droptanks.Eight00L_Tank = "weapons.droptanks.800L Tank" +ENUMS.Storage.weapons.bombs.IFV_BTR_D_Skid_17930lb = "weapons.bombs.IFV BTR-D Skid [17930lb]" +ENUMS.Storage.weapons.containers.bmp_1 = "weapons.containers.bmp-1" +ENUMS.Storage.weapons.bombs.GBU_31 = "weapons.bombs.GBU_31" +ENUMS.Storage.weapons.containers.aaq_28LEFT_litening = "weapons.containers.aaq-28LEFT litening" +ENUMS.Storage.weapons.missiles.Kh_66_Grom = "weapons.missiles.Kh-66_Grom" +ENUMS.Storage.weapons.containers._MIG21_SMOKE_RED = "weapons.containers.{MIG21_SMOKE_RED}" +ENUMS.Storage.weapons.containers.U22 = "weapons.containers.U22" +ENUMS.Storage.weapons.bombs.IFV_BMD_1_Skid_17930lb = "weapons.bombs.IFV BMD-1 Skid [17930lb]" +ENUMS.Storage.weapons.droptanks.Bidon = "weapons.droptanks.Bidon" +ENUMS.Storage.weapons.bombs.GBU_31_V_2B = "weapons.bombs.GBU_31_V_2B" +ENUMS.Storage.weapons.bombs.Mk_82Y = "weapons.bombs.Mk_82Y" +ENUMS.Storage.weapons.containers.pl5eii = "weapons.containers.pl5eii" +ENUMS.Storage.weapons.bombs.RBK_500U_OAB_2_5RT = "weapons.bombs.RBK_500U_OAB_2_5RT" +ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk5 = "weapons.bombs.British_GP_500LB_Bomb_Mk5" +ENUMS.Storage.weapons.containers._Eclair = "weapons.containers.{Eclair}" +ENUMS.Storage.weapons.nurs.S5MO_HEFRAG_FFAR = "weapons.nurs.S5MO_HEFRAG_FFAR" +ENUMS.Storage.weapons.bombs.BETAB_500M = "weapons.bombs.BETAB-500M" +ENUMS.Storage.weapons.bombs.Transport_M818_16000lb = "weapons.bombs.Transport M818 [16000lb]" +ENUMS.Storage.weapons.bombs.British_MC_250LB_Bomb_Mk1 = "weapons.bombs.British_MC_250LB_Bomb_Mk1" +ENUMS.Storage.weapons.nurs.SNEB_TYPE251_H1 = "weapons.nurs.SNEB_TYPE251_H1" +ENUMS.Storage.weapons.bombs.TYPE_200A = "weapons.bombs.TYPE-200A" +ENUMS.Storage.weapons.nurs.HYDRA_70_M151 = "weapons.nurs.HYDRA_70_M151" +ENUMS.Storage.weapons.bombs.IFV_BMP_3_32912lb = "weapons.bombs.IFV BMP-3 [32912lb]" +ENUMS.Storage.weapons.bombs.APC_MTLB_Air_26400lb = "weapons.bombs.APC MTLB Air [26400lb]" +ENUMS.Storage.weapons.nurs.HYDRA_70_M229 = "weapons.nurs.HYDRA_70_M229" +ENUMS.Storage.weapons.bombs.BDU_45 = "weapons.bombs.BDU_45" +ENUMS.Storage.weapons.bombs.OFAB_100_120TU = "weapons.bombs.OFAB-100-120TU" +ENUMS.Storage.weapons.missiles.AIM_9J = "weapons.missiles.AIM-9J" +ENUMS.Storage.weapons.nurs.ARF8M3API = "weapons.nurs.ARF8M3API" +ENUMS.Storage.weapons.bombs.BetAB_500ShP = "weapons.bombs.BetAB_500ShP" +ENUMS.Storage.weapons.nurs.C_8OFP2 = "weapons.nurs.C_8OFP2" +ENUMS.Storage.weapons.bombs.GBU_10 = "weapons.bombs.GBU_10" +ENUMS.Storage.weapons.bombs.APC_MTLB_Skid_26290lb = "weapons.bombs.APC MTLB Skid [26290lb]" +ENUMS.Storage.weapons.nurs.SNEB_TYPE254_F1B_RED = "weapons.nurs.SNEB_TYPE254_F1B_RED" +ENUMS.Storage.weapons.missiles.X_65 = "weapons.missiles.X_65" +ENUMS.Storage.weapons.missiles.R_550_M1 = "weapons.missiles.R_550_M1" +ENUMS.Storage.weapons.missiles.AGM_65K = "weapons.missiles.AGM_65K" +ENUMS.Storage.weapons.nurs.SNEB_TYPE254_F1B_YELLOW = "weapons.nurs.SNEB_TYPE254_F1B_YELLOW" +ENUMS.Storage.weapons.missiles.AGM_88 = "weapons.missiles.AGM_88" +ENUMS.Storage.weapons.nurs.C_8OM = "weapons.nurs.C_8OM" +ENUMS.Storage.weapons.bombs.SAM_ROLAND_LN_34720b = "weapons.bombs.SAM ROLAND LN [34720b]" +ENUMS.Storage.weapons.missiles.AIM_120 = "weapons.missiles.AIM_120" +ENUMS.Storage.weapons.missiles.HOT3_MBDA = "weapons.missiles.HOT3_MBDA" +ENUMS.Storage.weapons.missiles.R_13M = "weapons.missiles.R-13M" +ENUMS.Storage.weapons.missiles.AIM_54C_Mk60 = "weapons.missiles.AIM_54C_Mk60" +ENUMS.Storage.weapons.bombs.AAA_GEPARD_34720lb = "weapons.bombs.AAA GEPARD [34720lb]" +ENUMS.Storage.weapons.missiles.R_13M1 = "weapons.missiles.R-13M1" +ENUMS.Storage.weapons.bombs.APC_Cobra_Air_10912lb = "weapons.bombs.APC Cobra Air [10912lb]" +ENUMS.Storage.weapons.bombs.RBK_250 = "weapons.bombs.RBK_250" +ENUMS.Storage.weapons.bombs.SC_500_J = "weapons.bombs.SC_500_J" +ENUMS.Storage.weapons.missiles.AGM_114K = "weapons.missiles.AGM_114K" +ENUMS.Storage.weapons.missiles.ALARM = "weapons.missiles.ALARM" +ENUMS.Storage.weapons.bombs.Mk_83 = "weapons.bombs.Mk_83" +ENUMS.Storage.weapons.missiles.AGM_65B = "weapons.missiles.AGM_65B" +ENUMS.Storage.weapons.bombs.MK_82SNAKEYE = "weapons.bombs.MK_82SNAKEYE" +ENUMS.Storage.weapons.nurs.HYDRA_70_MK1 = "weapons.nurs.HYDRA_70_MK1" +ENUMS.Storage.weapons.bombs.BLG66_BELOUGA = "weapons.bombs.BLG66_BELOUGA" +ENUMS.Storage.weapons.containers._EclairM_51 = "weapons.containers.{EclairM_51}" +ENUMS.Storage.weapons.missiles.AIM_54A_Mk60 = "weapons.missiles.AIM_54A_Mk60" +ENUMS.Storage.weapons.droptanks.DFT_300_GAL_A4E = "weapons.droptanks.DFT_300_GAL_A4E" +ENUMS.Storage.weapons.bombs.ATGM_M1134_Stryker_30337lb = "weapons.bombs.ATGM M1134 Stryker [30337lb]" +ENUMS.Storage.weapons.bombs.BAT_120 = "weapons.bombs.BAT-120" +ENUMS.Storage.weapons.missiles.DWS39_MJ1_MJ2 = "weapons.missiles.DWS39_MJ1_MJ2" +ENUMS.Storage.weapons.containers.SPRD = "weapons.containers.SPRD" +ENUMS.Storage.weapons.bombs.BR_500 = "weapons.bombs.BR_500" +ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk1 = "weapons.bombs.British_GP_500LB_Bomb_Mk1" +ENUMS.Storage.weapons.bombs.BDU_50HD = "weapons.bombs.BDU_50HD" +ENUMS.Storage.weapons.missiles.RS2US = "weapons.missiles.RS2US" +ENUMS.Storage.weapons.bombs.IFV_BMP_2_25168lb = "weapons.bombs.IFV BMP-2 [25168lb]" +ENUMS.Storage.weapons.bombs.SAMP400HD = "weapons.bombs.SAMP400HD" +ENUMS.Storage.weapons.containers.Hercules_Battle_Station = "weapons.containers.Hercules_Battle_Station" +ENUMS.Storage.weapons.bombs.AN_M64 = "weapons.bombs.AN_M64" +ENUMS.Storage.weapons.containers.rearCargoSeats = "weapons.containers.rearCargoSeats" +ENUMS.Storage.weapons.bombs.Mk_82 = "weapons.bombs.Mk_82" +ENUMS.Storage.weapons.missiles.AKD_10 = "weapons.missiles.AKD-10" +ENUMS.Storage.weapons.bombs.BDU_50LGB = "weapons.bombs.BDU_50LGB" +ENUMS.Storage.weapons.missiles.SD_10 = "weapons.missiles.SD-10" +ENUMS.Storage.weapons.containers.IRDeflector = "weapons.containers.IRDeflector" +ENUMS.Storage.weapons.bombs.FAB_500 = "weapons.bombs.FAB_500" +ENUMS.Storage.weapons.bombs.KAB_500 = "weapons.bombs.KAB_500" +ENUMS.Storage.weapons.nurs.S_5M = "weapons.nurs.S-5M" +ENUMS.Storage.weapons.missiles.MICA_R = "weapons.missiles.MICA_R" +ENUMS.Storage.weapons.missiles.X_59M = "weapons.missiles.X_59M" +ENUMS.Storage.weapons.nurs.UG_90MM = "weapons.nurs.UG_90MM" +ENUMS.Storage.weapons.bombs.LYSBOMB = "weapons.bombs.LYSBOMB" +ENUMS.Storage.weapons.nurs.R4M = "weapons.nurs.R4M" +ENUMS.Storage.weapons.containers.dlpod_akg = "weapons.containers.dlpod_akg" +ENUMS.Storage.weapons.missiles.LD_10 = "weapons.missiles.LD-10" +ENUMS.Storage.weapons.bombs.SC_50 = "weapons.bombs.SC_50" +ENUMS.Storage.weapons.nurs.HYDRA_70_MK5 = "weapons.nurs.HYDRA_70_MK5" +ENUMS.Storage.weapons.bombs.FAB_100M = "weapons.bombs.FAB_100M" +ENUMS.Storage.weapons.missiles.Rb_24 = "weapons.missiles.Rb 24" +ENUMS.Storage.weapons.bombs.BDU_45B = "weapons.bombs.BDU_45B" +ENUMS.Storage.weapons.missiles.GB_6_HE = "weapons.missiles.GB-6-HE" +ENUMS.Storage.weapons.missiles.KD_63B = "weapons.missiles.KD-63B" +ENUMS.Storage.weapons.missiles.P_27PE = "weapons.missiles.P_27PE" +ENUMS.Storage.weapons.droptanks = "weapons.droptanks." +ENUMS.Storage.weapons.droptanks.PTB300_MIG15 = "weapons.droptanks.PTB300_MIG15" +ENUMS.Storage.weapons.bombs.Two50_3 = "weapons.bombs.250-3" +ENUMS.Storage.weapons.bombs.SC_500_L2 = "weapons.bombs.SC_500_L2" +ENUMS.Storage.weapons.containers.HMMWV_M1045 = "weapons.containers.HMMWV_M1045" +ENUMS.Storage.weapons.bombs.FAB_500M54TU = "weapons.bombs.FAB-500M54TU" +ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_YELLOW = "weapons.containers.{US_M10_SMOKE_TANK_YELLOW}" +ENUMS.Storage.weapons.containers._EclairM_60 = "weapons.containers.{EclairM_60}" +ENUMS.Storage.weapons.bombs.SAB_250_200 = "weapons.bombs.SAB_250_200" +ENUMS.Storage.weapons.bombs.FAB_100 = "weapons.bombs.FAB_100" +ENUMS.Storage.weapons.bombs.KAB_500S = "weapons.bombs.KAB_500S" +ENUMS.Storage.weapons.missiles.AGM_45A = "weapons.missiles.AGM_45A" +ENUMS.Storage.weapons.missiles.Kh25MP_PRGS1VP = "weapons.missiles.Kh25MP_PRGS1VP" +ENUMS.Storage.weapons.nurs.S5M1_HEFRAG_FFAR = "weapons.nurs.S5M1_HEFRAG_FFAR" +ENUMS.Storage.weapons.containers.kg600 = "weapons.containers.kg600" +ENUMS.Storage.weapons.bombs.AN_M65 = "weapons.bombs.AN_M65" +ENUMS.Storage.weapons.bombs.AN_M57 = "weapons.bombs.AN_M57" +ENUMS.Storage.weapons.bombs.BLU_3B_GROUP = "weapons.bombs.BLU_3B_GROUP" +ENUMS.Storage.weapons.bombs.BAP_100 = "weapons.bombs.BAP-100" +ENUMS.Storage.weapons.containers.HEMTT = "weapons.containers.HEMTT" +ENUMS.Storage.weapons.bombs.British_MC_500LB_Bomb_Mk1_Short = "weapons.bombs.British_MC_500LB_Bomb_Mk1_Short" +ENUMS.Storage.weapons.nurs.ARAKM70BAP = "weapons.nurs.ARAKM70BAP" +ENUMS.Storage.weapons.missiles.AGM_119 = "weapons.missiles.AGM_119" +ENUMS.Storage.weapons.missiles.MMagicII = "weapons.missiles.MMagicII" +ENUMS.Storage.weapons.bombs.AB_500_1_SD_10A = "weapons.bombs.AB_500_1_SD_10A" +ENUMS.Storage.weapons.nurs.HYDRA_70_M282 = "weapons.nurs.HYDRA_70_M282" +ENUMS.Storage.weapons.droptanks.DFT_400_GAL_A4E = "weapons.droptanks.DFT_400_GAL_A4E" +ENUMS.Storage.weapons.nurs.HYDRA_70_M257 = "weapons.nurs.HYDRA_70_M257" +ENUMS.Storage.weapons.droptanks.AV8BNA_AERO1D = "weapons.droptanks.AV8BNA_AERO1D" +ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_BLUE = "weapons.containers.{US_M10_SMOKE_TANK_BLUE}" +ENUMS.Storage.weapons.nurs.ARF8M3HEI = "weapons.nurs.ARF8M3HEI" +ENUMS.Storage.weapons.bombs.RN_28 = "weapons.bombs.RN-28" +ENUMS.Storage.weapons.bombs.Squad_30_x_Soldier_7950lb = "weapons.bombs.Squad 30 x Soldier [7950lb]" +ENUMS.Storage.weapons.containers.uaz_469 = "weapons.containers.uaz-469" +ENUMS.Storage.weapons.containers.Otokar_Cobra = "weapons.containers.Otokar_Cobra" +ENUMS.Storage.weapons.bombs.APC_BTR_82A_Air_24998lb = "weapons.bombs.APC BTR-82A Air [24998lb]" +ENUMS.Storage.weapons.nurs.HYDRA_70_M274 = "weapons.nurs.HYDRA_70_M274" +ENUMS.Storage.weapons.missiles.P_24R = "weapons.missiles.P_24R" +ENUMS.Storage.weapons.nurs.HYDRA_70_MK61 = "weapons.nurs.HYDRA_70_MK61" +ENUMS.Storage.weapons.missiles.Igla_1E = "weapons.missiles.Igla_1E" +ENUMS.Storage.weapons.missiles.C_802AK = "weapons.missiles.C-802AK" +ENUMS.Storage.weapons.nurs.C_24 = "weapons.nurs.C_24" +ENUMS.Storage.weapons.droptanks.M2KC_08_RPL541 = "weapons.droptanks.M2KC_08_RPL541" +ENUMS.Storage.weapons.nurs.C_13 = "weapons.nurs.C_13" +ENUMS.Storage.weapons.droptanks.droptank_110_gal = "weapons.droptanks.droptank_110_gal" +ENUMS.Storage.weapons.bombs.Mk_84 = "weapons.bombs.Mk_84" +ENUMS.Storage.weapons.missiles.Sea_Eagle = "weapons.missiles.Sea_Eagle" +ENUMS.Storage.weapons.droptanks.PTB_1200_F1 = "weapons.droptanks.PTB_1200_F1" +ENUMS.Storage.weapons.nurs.SNEB_TYPE256_H1 = "weapons.nurs.SNEB_TYPE256_H1" +ENUMS.Storage.weapons.containers.MATRA_PHIMAT = "weapons.containers.MATRA-PHIMAT" +ENUMS.Storage.weapons.containers.smoke_pod = "weapons.containers.smoke_pod" +ENUMS.Storage.weapons.containers.F_15E_AAQ_14_LANTIRN = "weapons.containers.F-15E_AAQ-14_LANTIRN" +ENUMS.Storage.weapons.containers._EclairM_24 = "weapons.containers.{EclairM_24}" +ENUMS.Storage.weapons.bombs.GBU_16 = "weapons.bombs.GBU_16" +ENUMS.Storage.weapons.nurs.HYDRA_70_M156 = "weapons.nurs.HYDRA_70_M156" +ENUMS.Storage.weapons.missiles.R_60 = "weapons.missiles.R-60" +ENUMS.Storage.weapons.containers.zsu_23_4 = "weapons.containers.zsu-23-4" +ENUMS.Storage.weapons.missiles.RB75 = "weapons.missiles.RB75" +ENUMS.Storage.weapons.missiles.Mistral = "weapons.missiles.Mistral" +ENUMS.Storage.weapons.droptanks.MB339_TT500_L = "weapons.droptanks.MB339_TT500_L" +ENUMS.Storage.weapons.bombs.SAM_SA_13_STRELA_21624lb = "weapons.bombs.SAM SA-13 STRELA [21624lb]" +ENUMS.Storage.weapons.bombs.SAM_Avenger_M1097_Air_7200lb = "weapons.bombs.SAM Avenger M1097 Air [7200lb]" +ENUMS.Storage.weapons.droptanks.Eleven00L_Tank_Empty = "weapons.droptanks.1100L Tank Empty" +ENUMS.Storage.weapons.bombs.AN_M88 = "weapons.bombs.AN-M88" +ENUMS.Storage.weapons.missiles.S_25L = "weapons.missiles.S_25L" +ENUMS.Storage.weapons.nurs.British_AP_25LBNo1_3INCHNo1 = "weapons.nurs.British_AP_25LBNo1_3INCHNo1" +ENUMS.Storage.weapons.bombs.BDU_50LD = "weapons.bombs.BDU_50LD" +ENUMS.Storage.weapons.bombs.AGM_62 = "weapons.bombs.AGM_62" +ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_WHITE = "weapons.containers.{US_M10_SMOKE_TANK_WHITE}" +ENUMS.Storage.weapons.missiles.MICA_T = "weapons.missiles.MICA_T" +ENUMS.Storage.weapons.containers.HVAR_rocket = "weapons.containers.HVAR_rocket" From 5fff8a60bae7810f7674b0983cd18fc4c21d3706 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 20 Aug 2023 15:22:29 +0200 Subject: [PATCH 329/603] Storages --- Moose Development/Moose/Utilities/Enums.lua | 46 ++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index 3d15cedd5..0b69dc772 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -587,7 +587,7 @@ ENUMS.Storage.weapons.missiles.AGM_154 = "weapons.missiles.AGM_154" ENUMS.Storage.weapons.nurs.HYDRA_70_M151_M433 = "weapons.nurs.HYDRA_70_M151_M433" ENUMS.Storage.weapons.bombs.SAM_Avenger_M1097_Skid_7090lb = "weapons.bombs.SAM Avenger M1097 Skid [7090lb]" ENUMS.Storage.weapons.bombs.British_GP_250LB_Bomb_Mk5 = "weapons.bombs.British_GP_250LB_Bomb_Mk5" -ENUMS.Storage.weapons.containers._OV10_SMOKE = "weapons.containers.{OV10_SMOKE}" +ENUMS.Storage.weapons.containers.OV10_SMOKE = "weapons.containers.{OV10_SMOKE}" ENUMS.Storage.weapons.bombs.BLU_4B_OLD = "weapons.bombs.BLU-4B_OLD" ENUMS.Storage.weapons.bombs.FAB_500M54 = "weapons.bombs.FAB-500M54" ENUMS.Storage.weapons.bombs.GBU_38 = "weapons.bombs.GBU_38" @@ -697,7 +697,7 @@ ENUMS.Storage.weapons.droptanks.DFT_300_GAL_A4E_LR = "weapons.droptanks.DFT_300_ ENUMS.Storage.weapons.bombs.CBU_87 = "weapons.bombs.CBU_87" ENUMS.Storage.weapons.missiles.GAR_8 = "weapons.missiles.GAR-8" ENUMS.Storage.weapons.bombs.BELOUGA = "weapons.bombs.BELOUGA" -ENUMS.Storage.weapons.containers._EclairM_33 = "weapons.containers.{EclairM_33}" +ENUMS.Storage.weapons.containers.EclairM_33 = "weapons.containers.{EclairM_33}" ENUMS.Storage.weapons.bombs.ART_2S9_NONA_Air_19140lb = "weapons.bombs.ART 2S9 NONA Air [19140lb]" ENUMS.Storage.weapons.bombs.BR_250 = "weapons.bombs.BR_250" ENUMS.Storage.weapons.bombs.IAB_500 = "weapons.bombs.IAB-500" @@ -707,10 +707,10 @@ ENUMS.Storage.weapons.bombs.SD_250_Stg = "weapons.bombs.SD_250_Stg" ENUMS.Storage.weapons.missiles.R_530F_IR = "weapons.missiles.R_530F_IR" ENUMS.Storage.weapons.bombs.British_SAP_500LB_Bomb_Mk5 = "weapons.bombs.British_SAP_500LB_Bomb_Mk5" ENUMS.Storage.weapons.bombs.FAB_250M54 = "weapons.bombs.FAB-250M54" -ENUMS.Storage.weapons.containers._M2KC_AAF = "weapons.containers.{M2KC_AAF}" +ENUMS.Storage.weapons.containers.M2KC_AAF = "weapons.containers.{M2KC_AAF}" ENUMS.Storage.weapons.missiles.CM_802AKG_AI = "weapons.missiles.CM-802AKG_AI" ENUMS.Storage.weapons.bombs.CBU_103 = "weapons.bombs.CBU_103" -ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_RED = "weapons.containers.{US_M10_SMOKE_TANK_RED}" +ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_RED = "weapons.containers.{US_M10_SMOKE_TANK_RED}" ENUMS.Storage.weapons.missiles.X_29T = "weapons.missiles.X_29T" ENUMS.Storage.weapons.bombs.HEMTT_TFFT_34400lb = "weapons.bombs.HEMTT TFFT [34400lb]" ENUMS.Storage.weapons.missiles.C_701IR = "weapons.missiles.C-701IR" @@ -727,9 +727,9 @@ ENUMS.Storage.weapons.bombs.HEBOMB = "weapons.bombs.HEBOMB" ENUMS.Storage.weapons.bombs.CBU_97 = "weapons.bombs.CBU_97" ENUMS.Storage.weapons.bombs.MK_81SE = "weapons.bombs.MK-81SE" ENUMS.Storage.weapons.nurs.Zuni_127 = "weapons.nurs.Zuni_127" -ENUMS.Storage.weapons.containers._M2KC_AGF = "weapons.containers.{M2KC_AGF}" +ENUMS.Storage.weapons.containers.M2KC_AGF = "weapons.containers.{M2KC_AGF}" ENUMS.Storage.weapons.droptanks.Hercules_ExtFuelTank = "weapons.droptanks.Hercules_ExtFuelTank" -ENUMS.Storage.weapons.containers._SMOKE_WHITE = "weapons.containers.{SMOKE_WHITE}" +ENUMS.Storage.weapons.containers.SMOKE_WHITE = "weapons.containers.{SMOKE_WHITE}" ENUMS.Storage.weapons.droptanks.droptank_150_gal = "weapons.droptanks.droptank_150_gal" ENUMS.Storage.weapons.nurs.HYDRA_70_WTU1B = "weapons.nurs.HYDRA_70_WTU1B" ENUMS.Storage.weapons.missiles.GB_6_SFW = "weapons.missiles.GB-6-SFW" @@ -827,7 +827,7 @@ ENUMS.Storage.weapons.bombs.ARV_BRDM_2_Air_12320lb = "weapons.bombs.ARV BRDM-2 A ENUMS.Storage.weapons.bombs.ARV_BRDM_2_Skid_12210lb = "weapons.bombs.ARV BRDM-2 Skid [12210lb]" ENUMS.Storage.weapons.nurs.SNEB_TYPE251_F1B = "weapons.nurs.SNEB_TYPE251_F1B" ENUMS.Storage.weapons.missiles.X_41 = "weapons.missiles.X_41" -ENUMS.Storage.weapons.containers._MIG21_SMOKE_WHITE = "weapons.containers.{MIG21_SMOKE_WHITE}" +ENUMS.Storage.weapons.containers.MIG21_SMOKE_WHITE = "weapons.containers.{MIG21_SMOKE_WHITE}" ENUMS.Storage.weapons.bombs.MK_82AIR = "weapons.bombs.MK_82AIR" ENUMS.Storage.weapons.missiles.R_530F_EM = "weapons.missiles.R_530F_EM" ENUMS.Storage.weapons.bombs.SAMP400LD = "weapons.bombs.SAMP400LD" @@ -867,7 +867,7 @@ ENUMS.Storage.weapons.missiles.X_25ML = "weapons.missiles.X_25ML" ENUMS.Storage.weapons.droptanks.FPU_8A = "weapons.droptanks.FPU_8A" ENUMS.Storage.weapons.bombs.BLG66 = "weapons.bombs.BLG66" ENUMS.Storage.weapons.nurs.C_8CM_RD = "weapons.nurs.C_8CM_RD" -ENUMS.Storage.weapons.containers._EclairM_06 = "weapons.containers.{EclairM_06}" +ENUMS.Storage.weapons.containers.EclairM_06 = "weapons.containers.{EclairM_06}" ENUMS.Storage.weapons.bombs.RBK_500AO = "weapons.bombs.RBK_500AO" ENUMS.Storage.weapons.missiles.AIM_9P = "weapons.missiles.AIM-9P" ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk4_Short = "weapons.bombs.British_GP_500LB_Bomb_Mk4_Short" @@ -876,7 +876,7 @@ ENUMS.Storage.weapons.missiles.Rb_15F = "weapons.missiles.Rb 15F" ENUMS.Storage.weapons.nurs.ARAKM70BHE = "weapons.nurs.ARAKM70BHE" ENUMS.Storage.weapons.bombs.AAA_Vulcan_M163_Air_21666lb = "weapons.bombs.AAA Vulcan M163 Air [21666lb]" ENUMS.Storage.weapons.missiles.X_29L = "weapons.missiles.X_29L" -ENUMS.Storage.weapons.containers._F14_LANTIRN_TP = "weapons.containers.{F14-LANTIRN-TP}" +ENUMS.Storage.weapons.containers.F14_LANTIRN_TP = "weapons.containers.{F14-LANTIRN-TP}" ENUMS.Storage.weapons.bombs.FAB_250_M62 = "weapons.bombs.FAB-250-M62" ENUMS.Storage.weapons.missiles.AIM_120C = "weapons.missiles.AIM_120C" ENUMS.Storage.weapons.bombs.EWR_SBORKA_Air_21624lb = "weapons.bombs.EWR SBORKA Air [21624lb]" @@ -897,7 +897,7 @@ ENUMS.Storage.weapons.bombs.MK77mod1_WPN = "weapons.bombs.MK77mod1-WPN" ENUMS.Storage.weapons.droptanks.PTB400_MIG19 = "weapons.droptanks.PTB400_MIG19" ENUMS.Storage.weapons.torpedoes.Mark_46 = "weapons.torpedoes.Mark_46" ENUMS.Storage.weapons.containers.rightSeat = "weapons.containers.rightSeat" -ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_ORANGE = "weapons.containers.{US_M10_SMOKE_TANK_ORANGE}" +ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_ORANGE = "weapons.containers.{US_M10_SMOKE_TANK_ORANGE}" ENUMS.Storage.weapons.bombs.SAB_100MN = "weapons.bombs.SAB_100MN" ENUMS.Storage.weapons.nurs.FFAR_Mk5_HEAT = "weapons.nurs.FFAR Mk5 HEAT" ENUMS.Storage.weapons.bombs.IFV_TPZ_FUCH_33440lb = "weapons.bombs.IFV TPZ FUCH [33440lb]" @@ -931,7 +931,7 @@ ENUMS.Storage.weapons.missiles.Rb_74 = "weapons.missiles.Rb 74" ENUMS.Storage.weapons.containers.leftSeat = "weapons.containers.leftSeat" ENUMS.Storage.weapons.bombs.LS_6_100 = "weapons.bombs.LS-6-100" ENUMS.Storage.weapons.bombs.Transport_URAL_375_14815lb = "weapons.bombs.Transport URAL-375 [14815lb]" -ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_GREEN = "weapons.containers.{US_M10_SMOKE_TANK_GREEN}" +ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_GREEN = "weapons.containers.{US_M10_SMOKE_TANK_GREEN}" ENUMS.Storage.weapons.missiles.X_22 = "weapons.missiles.X_22" ENUMS.Storage.weapons.containers.FAS = "weapons.containers.FAS" ENUMS.Storage.weapons.nurs.S_25_O = "weapons.nurs.S-25-O" @@ -944,9 +944,9 @@ ENUMS.Storage.weapons.missiles.AIM_54A_Mk47 = "weapons.missiles.AIM_54A_Mk47" ENUMS.Storage.weapons.droptanks.oiltank = "weapons.droptanks.oiltank" ENUMS.Storage.weapons.missiles.AGM_154B = "weapons.missiles.AGM_154B" ENUMS.Storage.weapons.containers.MB339_SMOKE_POD = "weapons.containers.MB339_SMOKE-POD" -ENUMS.Storage.weapons.containers._ECM_POD_L_175V = "weapons.containers.{ECM_POD_L_175V}" +ENUMS.Storage.weapons.containers.ECM_POD_L_175V = "weapons.containers.{ECM_POD_L_175V}" ENUMS.Storage.weapons.droptanks.PTB_580G_F1 = "weapons.droptanks.PTB_580G_F1" -ENUMS.Storage.weapons.containers._EclairM_15 = "weapons.containers.{EclairM_15}" +ENUMS.Storage.weapons.containers.EclairM_15 = "weapons.containers.{EclairM_15}" ENUMS.Storage.weapons.containers.F_15E_AAQ_13_LANTIRN = "weapons.containers.F-15E_AAQ-13_LANTIRN" ENUMS.Storage.weapons.droptanks.Eight00L_Tank_Empty = "weapons.droptanks.800L Tank Empty" ENUMS.Storage.weapons.containers.One6c_hts_pod = "weapons.containers.16c_hts_pod" @@ -956,7 +956,7 @@ ENUMS.Storage.weapons.droptanks.Mosquito_Drop_Tank_50gal = "weapons.droptanks.Mo ENUMS.Storage.weapons.droptanks.DFT_150_GAL_A4E = "weapons.droptanks.DFT_150_GAL_A4E" ENUMS.Storage.weapons.missiles.AIM_9 = "weapons.missiles.AIM_9" ENUMS.Storage.weapons.bombs.IFV_BTR_D_Air_18040lb = "weapons.bombs.IFV BTR-D Air [18040lb]" -ENUMS.Storage.weapons.containers._EclairM_42 = "weapons.containers.{EclairM_42}" +ENUMS.Storage.weapons.containers.EclairM_42 = "weapons.containers.{EclairM_42}" ENUMS.Storage.weapons.bombs.KAB_1500T = "weapons.bombs.KAB_1500T" ENUMS.Storage.weapons.droptanks.PTB_490_MIG21 = "weapons.droptanks.PTB-490-MIG21" ENUMS.Storage.weapons.droptanks.PTB_200_F86F35 = "weapons.droptanks.PTB_200_F86F35" @@ -966,7 +966,7 @@ ENUMS.Storage.weapons.torpedoes.G7A_T1 = "weapons.torpedoes.G7A_T1" ENUMS.Storage.weapons.bombs.IFV_BMD_1_Air_18040lb = "weapons.bombs.IFV BMD-1 Air [18040lb]" ENUMS.Storage.weapons.bombs.SAM_LINEBACKER_34720lb = "weapons.bombs.SAM LINEBACKER [34720lb]" ENUMS.Storage.weapons.containers.ais_pod_t50_r = "weapons.containers.ais-pod-t50_r" -ENUMS.Storage.weapons.containers._CE2_SMOKE_WHITE = "weapons.containers.{CE2_SMOKE_WHITE}" +ENUMS.Storage.weapons.containers.CE2_SMOKE_WHITE = "weapons.containers.{CE2_SMOKE_WHITE}" ENUMS.Storage.weapons.droptanks.fuel_tank_230 = "weapons.droptanks.fuel_tank_230" ENUMS.Storage.weapons.droptanks.M2KC_RPL_522 = "weapons.droptanks.M2KC_RPL_522" ENUMS.Storage.weapons.missiles.AGM_130 = "weapons.missiles.AGM_130" @@ -976,7 +976,7 @@ ENUMS.Storage.weapons.containers.bmp_1 = "weapons.containers.bmp-1" ENUMS.Storage.weapons.bombs.GBU_31 = "weapons.bombs.GBU_31" ENUMS.Storage.weapons.containers.aaq_28LEFT_litening = "weapons.containers.aaq-28LEFT litening" ENUMS.Storage.weapons.missiles.Kh_66_Grom = "weapons.missiles.Kh-66_Grom" -ENUMS.Storage.weapons.containers._MIG21_SMOKE_RED = "weapons.containers.{MIG21_SMOKE_RED}" +ENUMS.Storage.weapons.containers.MIG21_SMOKE_RED = "weapons.containers.{MIG21_SMOKE_RED}" ENUMS.Storage.weapons.containers.U22 = "weapons.containers.U22" ENUMS.Storage.weapons.bombs.IFV_BMD_1_Skid_17930lb = "weapons.bombs.IFV BMD-1 Skid [17930lb]" ENUMS.Storage.weapons.droptanks.Bidon = "weapons.droptanks.Bidon" @@ -985,7 +985,7 @@ ENUMS.Storage.weapons.bombs.Mk_82Y = "weapons.bombs.Mk_82Y" ENUMS.Storage.weapons.containers.pl5eii = "weapons.containers.pl5eii" ENUMS.Storage.weapons.bombs.RBK_500U_OAB_2_5RT = "weapons.bombs.RBK_500U_OAB_2_5RT" ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk5 = "weapons.bombs.British_GP_500LB_Bomb_Mk5" -ENUMS.Storage.weapons.containers._Eclair = "weapons.containers.{Eclair}" +ENUMS.Storage.weapons.containers.Eclair = "weapons.containers.{Eclair}" ENUMS.Storage.weapons.nurs.S5MO_HEFRAG_FFAR = "weapons.nurs.S5MO_HEFRAG_FFAR" ENUMS.Storage.weapons.bombs.BETAB_500M = "weapons.bombs.BETAB-500M" ENUMS.Storage.weapons.bombs.Transport_M818_16000lb = "weapons.bombs.Transport M818 [16000lb]" @@ -1028,7 +1028,7 @@ ENUMS.Storage.weapons.missiles.AGM_65B = "weapons.missiles.AGM_65B" ENUMS.Storage.weapons.bombs.MK_82SNAKEYE = "weapons.bombs.MK_82SNAKEYE" ENUMS.Storage.weapons.nurs.HYDRA_70_MK1 = "weapons.nurs.HYDRA_70_MK1" ENUMS.Storage.weapons.bombs.BLG66_BELOUGA = "weapons.bombs.BLG66_BELOUGA" -ENUMS.Storage.weapons.containers._EclairM_51 = "weapons.containers.{EclairM_51}" +ENUMS.Storage.weapons.containers.EclairM_51 = "weapons.containers.{EclairM_51}" ENUMS.Storage.weapons.missiles.AIM_54A_Mk60 = "weapons.missiles.AIM_54A_Mk60" ENUMS.Storage.weapons.droptanks.DFT_300_GAL_A4E = "weapons.droptanks.DFT_300_GAL_A4E" ENUMS.Storage.weapons.bombs.ATGM_M1134_Stryker_30337lb = "weapons.bombs.ATGM M1134 Stryker [30337lb]" @@ -1073,8 +1073,8 @@ ENUMS.Storage.weapons.bombs.Two50_3 = "weapons.bombs.250-3" ENUMS.Storage.weapons.bombs.SC_500_L2 = "weapons.bombs.SC_500_L2" ENUMS.Storage.weapons.containers.HMMWV_M1045 = "weapons.containers.HMMWV_M1045" ENUMS.Storage.weapons.bombs.FAB_500M54TU = "weapons.bombs.FAB-500M54TU" -ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_YELLOW = "weapons.containers.{US_M10_SMOKE_TANK_YELLOW}" -ENUMS.Storage.weapons.containers._EclairM_60 = "weapons.containers.{EclairM_60}" +ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_YELLOW = "weapons.containers.{US_M10_SMOKE_TANK_YELLOW}" +ENUMS.Storage.weapons.containers.EclairM_60 = "weapons.containers.{EclairM_60}" ENUMS.Storage.weapons.bombs.SAB_250_200 = "weapons.bombs.SAB_250_200" ENUMS.Storage.weapons.bombs.FAB_100 = "weapons.bombs.FAB_100" ENUMS.Storage.weapons.bombs.KAB_500S = "weapons.bombs.KAB_500S" @@ -1096,7 +1096,7 @@ ENUMS.Storage.weapons.nurs.HYDRA_70_M282 = "weapons.nurs.HYDRA_70_M282" ENUMS.Storage.weapons.droptanks.DFT_400_GAL_A4E = "weapons.droptanks.DFT_400_GAL_A4E" ENUMS.Storage.weapons.nurs.HYDRA_70_M257 = "weapons.nurs.HYDRA_70_M257" ENUMS.Storage.weapons.droptanks.AV8BNA_AERO1D = "weapons.droptanks.AV8BNA_AERO1D" -ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_BLUE = "weapons.containers.{US_M10_SMOKE_TANK_BLUE}" +ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_BLUE = "weapons.containers.{US_M10_SMOKE_TANK_BLUE}" ENUMS.Storage.weapons.nurs.ARF8M3HEI = "weapons.nurs.ARF8M3HEI" ENUMS.Storage.weapons.bombs.RN_28 = "weapons.bombs.RN-28" ENUMS.Storage.weapons.bombs.Squad_30_x_Soldier_7950lb = "weapons.bombs.Squad 30 x Soldier [7950lb]" @@ -1119,7 +1119,7 @@ ENUMS.Storage.weapons.nurs.SNEB_TYPE256_H1 = "weapons.nurs.SNEB_TYPE256_H1" ENUMS.Storage.weapons.containers.MATRA_PHIMAT = "weapons.containers.MATRA-PHIMAT" ENUMS.Storage.weapons.containers.smoke_pod = "weapons.containers.smoke_pod" ENUMS.Storage.weapons.containers.F_15E_AAQ_14_LANTIRN = "weapons.containers.F-15E_AAQ-14_LANTIRN" -ENUMS.Storage.weapons.containers._EclairM_24 = "weapons.containers.{EclairM_24}" +ENUMS.Storage.weapons.containers.EclairM_24 = "weapons.containers.{EclairM_24}" ENUMS.Storage.weapons.bombs.GBU_16 = "weapons.bombs.GBU_16" ENUMS.Storage.weapons.nurs.HYDRA_70_M156 = "weapons.nurs.HYDRA_70_M156" ENUMS.Storage.weapons.missiles.R_60 = "weapons.missiles.R-60" @@ -1135,6 +1135,6 @@ ENUMS.Storage.weapons.missiles.S_25L = "weapons.missiles.S_25L" ENUMS.Storage.weapons.nurs.British_AP_25LBNo1_3INCHNo1 = "weapons.nurs.British_AP_25LBNo1_3INCHNo1" ENUMS.Storage.weapons.bombs.BDU_50LD = "weapons.bombs.BDU_50LD" ENUMS.Storage.weapons.bombs.AGM_62 = "weapons.bombs.AGM_62" -ENUMS.Storage.weapons.containers._US_M10_SMOKE_TANK_WHITE = "weapons.containers.{US_M10_SMOKE_TANK_WHITE}" +ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_WHITE = "weapons.containers.{US_M10_SMOKE_TANK_WHITE}" ENUMS.Storage.weapons.missiles.MICA_T = "weapons.missiles.MICA_T" ENUMS.Storage.weapons.containers.HVAR_rocket = "weapons.containers.HVAR_rocket" From fd80339431fa804df67050e5a1489d20b135bdc2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 22 Aug 2023 10:24:19 +0200 Subject: [PATCH 330/603] #Controllable * Added Aerobtics tasks --- .../Moose/Wrapper/Controllable.lua | 1209 +++++++++++++++++ 1 file changed, 1209 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 79f794b48..1f4714e26 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -4056,3 +4056,1212 @@ function CONTROLLABLE:SetAltitude(Altitude, Keep, AltType) end return self end + +--- Return an empty task shell for Aerobatics. +-- @param #CONTROLLABLE self +-- @return DCS#Task +-- @usage +-- local plane = GROUP:FindByName("Aerial-1") +-- -- get a task shell +-- local aerotask = plane:TaskAerobatics() +-- -- add a series of maneuvers +-- aerotask = plane:TaskAerobaticsHorizontalEight(aerotask,1,5000,850,true,false,1,70) +-- aerotask = plane:TaskAerobaticsWingoverFlight(aerotask,1,0,0,true,true,20) +-- aerotask = plane:TaskAerobaticsLoop(aerotask,1,0,0,false,true) +-- -- set the task +-- plane:SetTask(aerotask) +function CONTROLLABLE:TaskAerobatics() + + local DCSTaskAerobatics = { + id = "Aerobatics", + params = { + ["maneuversSequency"] = {}, + }, + ["enabled"] = true, + ["auto"] = false, + } + + return DCSTaskAerobatics +end + +--- Add an aerobatics entry of type "CANDLE" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsCandle(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local CandleTask = { + ["name"] = "CANDLE", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + } + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],CandleTask) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "EDGE_FLIGHT" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number FlightTime (Optional) Time to fly this manoever in seconds, defaults to 10. +-- @param #number Side (Optional) On which side to fly, 0 == left, 1 == right side, defaults to 0. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsEdgeFlight(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,FlightTime,Side) + + local maxrepeats = 10 + local maxflight = 200 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local flighttime = FlightTime or 10 + + if flighttime > 200 then maxflight = flighttime end + + local EdgeTask = { + ["name"] = "EDGE_FLIGHT", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["FlightTime"] = { + ["max_v"] = maxflight, + ["min_v"] = 1, + ["order"] = 6, + ["step"] = 0.1, + ["value"] = flighttime or 10, -- Secs? + }, + ["SIDE"] = { + ["order"] = 7, + ["value"] = Side or 0, --0 == left, 1 == right side + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],EdgeTask) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "WINGOVER_FLIGHT" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number FlightTime (Optional) Time to fly this manoever in seconds, defaults to 10. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsWingoverFlight(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,FlightTime) + + local maxrepeats = 10 + local maxflight = 200 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local flighttime = FlightTime or 10 + + if flighttime > 200 then maxflight = flighttime end + + local WingoverTask = { + ["name"] = "WINGOVER_FLIGHT", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["FlightTime"] = { + ["max_v"] = maxflight, + ["min_v"] = 1, + ["order"] = 6, + ["step"] = 0.1, + ["value"] = flighttime or 10, -- Secs? + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],WingoverTask) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "LOOP" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsLoop(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local LoopTask = { + ["name"] = "LOOP", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + } + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],LoopTask) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "HORIZONTAL_EIGHT" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number Side (Optional) On which side to fly, 0 == left, 1 == right side, defaults to 0. +-- @param #number RollDeg (Optional) Roll degrees for Roll 1 and 2, defaults to 60. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsHorizontalEight(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollDeg) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local LoopTask = { + ["name"] = "HORIZONTAL_EIGHT", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["SIDE"] = { + ["order"] = 6, + ["value"] = Side or 0, + }, + ["ROLL1"] = { + ["order"] = 7, + ["value"] = RollDeg or 60, + }, + ["ROLL2"] = { + ["order"] = 8, + ["value"] = RollDeg or 60, + }, + + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],LoopTask) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "HAMMERHEAD" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number Side (Optional) On which side to fly, 0 == left, 1 == right side, defaults to 0. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsHammerhead(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "HUMMERHEAD", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["SIDE"] = { + ["order"] = 6, + ["value"] = Side or 0, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "SKEWED_LOOP" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number Side (Optional) On which side to fly, 0 == left, 1 == right side, defaults to 0. +-- @param #number RollDeg (Optional) Roll degrees for Roll 1 and 2, defaults to 60. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsSkewedLoop(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollDeg) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "SKEWED_LOOP", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["ROLL"] = { + ["order"] = 6, + ["value"] = RollDeg or 60, + }, + ["SIDE"] = { + ["order"] = 7, + ["value"] = Side or 0, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "TURN" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number Side (Optional) On which side to fly, 0 == left, 1 == right side, defaults to 0. +-- @param #number RollDeg (Optional) Roll degrees for Roll 1 and 2, defaults to 60. +-- @param #number Pull (Optional) How many Gs to pull in this, defaults to 2. +-- @param #number Angle (Optional) How many degrees to turn, defaults to 180. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsTurn(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollDeg,Pull,Angle) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "TURN", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["Ny_req"] = { + ["order"] = 6, + ["value"] = Pull or 2, --amount of G to pull + }, + ["ROLL"] = { + ["order"] = 7, + ["value"] = RollDeg or 60, + }, + ["SECTOR"] = { + ["order"] = 8, + ["value"] = Angle or 180, + }, + ["SIDE"] = { + ["order"] = 9, + ["value"] = Side or 0, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "DIVE" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 5000. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number Angle (Optional) With how many degrees to dive, defaults to 45. Can be 15 to 90 degrees. +-- @param #number FinalAltitude (Optional) Final altitude in meters, defaults to 1000. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsDive(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Angle,FinalAltitude) + + local maxrepeats = 10 + + local angle = Angle + + if angle < 15 then angle = 15 elseif angle > 90 then angle = 90 end + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "DIVE", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 5000, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["Angle"] = { + ["max_v"] = 90, + ["min_v"] = 15, + ["order"] = 6, + ["step"] = 5, + ["value"] = angle or 45, + }, + ["FinalAltitude"] = { + ["order"] = 7, + ["value"] = FinalAltitude or 1000, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "MILITARY_TURN" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsMilitaryTurn(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "MILITARY_TURN", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + } + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "IMMELMAN" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsImmelmann(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "IMMELMAN", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + } + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "STRAIGHT_FLIGHT" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number FlightTime (Optional) Time to fly this manoever in seconds, defaults to 10. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsStraightFlight(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,FlightTime) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local maxflight = 200 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local flighttime = FlightTime or 10 + + if flighttime > 200 then maxflight = flighttime end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "STRAIGHT_FLIGHT", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["FlightTime"] = { + ["max_v"] = maxflight, + ["min_v"] = 1, + ["order"] = 6, + ["step"] = 0.1, + ["value"] = flighttime or 10, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "CLIMB" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number Angle (Optional) Angle to climb. Can be between 15 and 90 degrees. Defaults to 45 degrees. +-- @param #number FinalAltitude (Optional) Altitude to climb to in meters. Defaults to 5000m. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsClimb(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Angle,FinalAltitude) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "CLIMB", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["Angle"] = { + ["max_v"] = 90, + ["min_v"] = 15, + ["order"] = 6, + ["step"] = 5, + ["value"] = Angle or 45, + }, + ["FinalAltitude"] = { + ["order"] = 7, + ["value"] = FinalAltitude or 5000, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "SPIRAL" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number TurnAngle (Optional) Turn angle, defaults to 360 degrees. +-- @param #number Roll (Optional) Roll to take, defaults to 60 degrees. +-- @param #number Side (Optional) On which side to fly, 0 == left, 1 == right side, defaults to 0. +-- @param #number UpDown (Optional) Spiral upwards (1) or downwards (0). Defaults to 0 - downwards. +-- @param #number Angle (Optional) Angle to spiral. Can be between 15 and 90 degrees. Defaults to 45 degrees. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsSpiral(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,TurnAngle,Roll,Side,UpDown,Angle) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + local updown = UpDown and 1 or 0 + local side = Side and 1 or 0 + + local Task = { + ["name"] = "SPIRAL", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["SECTOR"] = { + ["order"] = 6, + ["value"] = TurnAngle or 360, + }, + ["ROLL"] = { + ["order"] = 7, + ["value"] = Roll or 60, + }, + ["SIDE"] = { + ["order"] = 8, + ["value"] = side or 0, + }, + ["UPDOWN"] = { + ["order"] = 9, + ["value"] = updown or 0, + }, + ["Angle"] = { + ["max_v"] = 90, + ["min_v"] = 15, + ["order"] = 10, + ["step"] = 5, + ["value"] = Angle or 45, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "SPLIT_S" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number FinalSpeed (Optional) Final speed to reach in KPH. Defaults to 500 kph. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsSplitS(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,FinalSpeed) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local maxflight = 200 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local finalspeed = FinalSpeed or 500 + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "SPLIT_S", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["FinalSpeed"] = { + ["order"] = 6, + ["value"] = finalspeed, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "AILERON_ROLL" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number Side (Optional) On which side to fly, 0 == left, 1 == right side, defaults to 0. +-- @param #number RollRate (Optional) How many degrees to roll per sec(?), can be between 15 and 450, defaults to 90. +-- @param #number TurnAngle (Optional) Angles to turn overall, defaults to 360. +-- @param #number FixAngle (Optional) No idea what this does, can be between 0 and 180 degrees, defaults to 180. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsAileronRoll(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollRate,TurnAngle,FixAngle) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local maxflight = 200 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "AILERON_ROLL", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["SIDE"] = { + ["order"] = 6, + ["value"] = Side or 0, + }, + ["RollRate"] = { + ["max_v"] = 450, + ["min_v"] = 15, + ["order"] = 7, + ["step"] = 5, + ["value"] = RollRate or 90, + }, + ["SECTOR"] = { + ["order"] = 8, + ["value"] = TurnAngle or 360, + }, + ["FIXSECTOR"] = { + ["max_v"] = 180, + ["min_v"] = 0, + ["order"] = 9, + ["step"] = 5, + ["value"] = FixAngle or 0, -- TODO: Need to find out what this does + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "FORCED_TURN" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number TurnAngle (Optional) Angles to turn, defaults to 360. +-- @param #number Side (Optional) On which side to fly, 0 == left, 1 == right side, defaults to 0. +-- @param #number FlightTime (Optional) Flight time in seconds for thos maneuver. Defaults to 30. +-- @param #number MinSpeed (Optional) Minimum speed to keep in kph, defaults to 250 kph. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsForcedTurn(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,TurnAngle,Side,FlightTime,MinSpeed) + + local maxrepeats = 10 + local flighttime = FlightTime or 30 + local maxtime = 200 + if flighttime > 200 then maxtime = flighttime end + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "FORCED_TURN", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["SECTOR"] = { + ["order"] = 6, + ["value"] = TurnAngle or 360, + }, + ["SIDE"] = { + ["order"] = 7, + ["value"] = Side or 0, + }, + ["FlightTime"] = { + ["max_v"] = maxtime or 200, + ["min_v"] = 0, + ["order"] = 8, + ["step"] = 0.1, + ["value"] = flighttime or 30, + }, + ["MinSpeed"] = { + ["max_v"] = 3000, + ["min_v"] = 30, + ["order"] = 9, + ["step"] = 10, + ["value"] = MinSpeed or 250, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + +--- Add an aerobatics entry of type "BARREL_ROLL" to the Aerobatics Task. +-- @param #CONTROLLABLE self +-- @param DCS#Task TaskAerobatics The Aerobatics Task +-- @param #number Repeats (Optional) The number of repeats, defaults to 1. +-- @param #number InitAltitude (Optional) Starting altitude in meters, defaults to 0. +-- @param #number InitSpeed (Optional) Starting speed in KPH, defaults to 0. +-- @param #boolean UseSmoke (Optional) Using smoke, defaults to false. +-- @param #boolean StartImmediately (Optional) If true, start immediately and ignore InitAltitude and InitSpeed. +-- @param #number Side (Optional) On which side to fly, 0 == left, 1 == right side, defaults to 0. +-- @param #number RollRate (Optional) How many degrees to roll per sec(?), can be between 15 and 450, defaults to 90. +-- @param #number TurnAngle (Optional) Turn angle, defaults to 360 degrees. +-- @return DCS#Task +function CONTROLLABLE:TaskAerobaticsBarrelRoll(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollRate,TurnAngle) + + local maxrepeats = 10 + + if Repeats > maxrepeats then maxrepeats = Repeats end + + local usesmoke = UseSmoke and 1 or 0 + + local startimmediately = StartImmediately and 1 or 0 + + local Task = { + ["name"] = "BARREL_ROLL", + ["params"] = { + ["RepeatQty"] = { + ["max_v"] = maxrepeats, + ["min_v"] = 1, + ["order"] = 1, + ["value"] = Repeats or 1, + }, + ["InitAltitude"] = { + ["order"] = 2, + ["value"] = InitAltitude or 0, + }, + ["InitSpeed"] = { + ["order"] = 3, + ["value"] = InitSpeed or 0, + }, + ["UseSmoke"] = { + ["order"] = 4, + ["value"] = usesmoke, + }, + ["StartImmediatly"] = { + ["order"] = 5, + ["value"] = startimmediately, + }, + ["SIDE"] = { + ["order"] = 6, + ["value"] = Side or 0, + }, + ["RollRate"] = { + ["max_v"] = 450, + ["min_v"] = 15, + ["order"] = 7, + ["step"] = 5, + ["value"] = RollRate or 90, + }, + ["SECTOR"] = { + ["order"] = 8, + ["value"] = TurnAngle or 360, + }, + } + } + + table.insert(TaskAerobatics.params["maneuversSequency"],Task) + + return TaskAerobatics +end + From 48b6b06503678b73bbdea1f79f726ae58d3778e1 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 23 Aug 2023 11:46:08 +0200 Subject: [PATCH 331/603] Update Set.lua Docu (#1995) (#1997) * Update Set.lua Docu Added regex explanation * Update Set.lua --- Moose Development/Moose/Core/Set.lua | 37 ++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 973090bb7..b2b919c55 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -35,6 +35,13 @@ -- * Validate the presence of objects in the SET. -- * Trigger events when objects in the SET change a zone presence. -- +-- ## Notes on `FilterPrefixes()`: +-- +-- This filter always looks for a **partial match** somewhere in the given field. LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. +-- Have a read through the following to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching). +-- For example, setting a filter like so `FilterPrefixes("Huey")` is perfectly all right, whilst `FilterPrefixes("UH-1H Al-Assad")` might not be due to the minus signs. A quick fix here is to use a dot (.) +-- in place of the special character, or escape it with a percentage sign (%), i.e. either `FilterPrefixes("UH.1H Al.Assad")` or `FilterPrefixes("UH%-1H Al%-Assad")` will give you the expected results. +-- -- === -- -- ### Author: **FlightControl** @@ -940,7 +947,8 @@ do -- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). -- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). -- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the groups belonging to the country(ies). - -- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups *containing* the given string in the group name. **Attention!** Bad naming convention, as this not really filtering *prefixes*. + -- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups *containing* the given string in the group name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- * @{#SET_GROUP.FilterActive}: Builds the SET_GROUP with the groups that are only active. Groups that are inactive (late activation) won't be included in the set! -- -- For the Category Filter, extra methods have been added: @@ -2052,7 +2060,8 @@ do -- SET_UNIT -- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies). -- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). -- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). - -- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units sharing the same string(s) in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*. + -- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units sharing the same string(s) in their name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- * @{#SET_UNIT.FilterActive}: Builds the SET_UNIT with the units that are only active. Units that are inactive (late activation) won't be included in the set! -- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Core.Zone#ZONE}. -- @@ -3231,7 +3240,8 @@ do -- SET_STATIC -- * @{#SET_STATIC.FilterCategories}: Builds the SET_STATIC with the units belonging to the category(ies). -- * @{#SET_STATIC.FilterTypes}: Builds the SET_STATIC with the units belonging to the unit type(s). -- * @{#SET_STATIC.FilterCountries}: Builds the SET_STATIC with the units belonging to the country(ies). - -- * @{#SET_STATIC.FilterPrefixes}: Builds the SET_STATIC with the units containing the same string(s) in their name. **ATTENTION** bad naming convention as this *does not** only filter *prefixes*. + -- * @{#SET_STATIC.FilterPrefixes}: Builds the SET_STATIC with the units containing the same string(s) in their name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- * @{#SET_STATIC.FilterZones}: Builds the SET_STATIC with the units within a @{Core.Zone#ZONE}. -- -- Once the filter criteria have been set for the SET_STATIC, you can start filtering using: @@ -3988,7 +3998,8 @@ do -- SET_CLIENT -- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). -- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). -- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). - -- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients containing the same string(s) in their unit/pilot name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*. + -- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients containing the same string(s) in their unit/pilot name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- * @{#SET_CLIENT.FilterActive}: Builds the SET_CLIENT with the units that are only active. Units that are inactive (late activation) won't be included in the set! -- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}. -- @@ -4603,7 +4614,8 @@ do -- SET_PLAYER -- * @{#SET_PLAYER.FilterCategories}: Builds the SET_PLAYER with the clients belonging to the category(ies). -- * @{#SET_PLAYER.FilterTypes}: Builds the SET_PLAYER with the clients belonging to the client type(s). -- * @{#SET_PLAYER.FilterCountries}: Builds the SET_PLAYER with the clients belonging to the country(ies). - -- * @{#SET_PLAYER.FilterPrefixes}: Builds the SET_PLAYER with the clients sharing the same string(s) in their unit/pilot name. **ATTENTION** Bad naming convention as this *does not* only filter prefixes. + -- * @{#SET_PLAYER.FilterPrefixes}: Builds the SET_PLAYER with the clients sharing the same string(s) in their unit/pilot name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- -- Once the filter criteria have been set for the SET_PLAYER, you can start filtering using: -- @@ -5380,7 +5392,8 @@ do -- SET_CARGO -- Filter criteria are defined by: -- -- * @{#SET_CARGO.FilterCoalitions}: Builds the SET_CARGO with the cargos belonging to the coalition(s). - -- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the same string(s). **ATTENTION** Bad naming convention as this *does not* only filter *prefixes*. + -- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the same string(s). **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- * @{#SET_CARGO.FilterTypes}: Builds the SET_CARGO with the cargos belonging to the cargo type(s). -- * @{#SET_CARGO.FilterCountries}: Builds the SET_CARGO with the cargos belonging to the country(ies). -- @@ -5802,7 +5815,8 @@ do -- SET_ZONE -- You can set filter criteria to build the collection of zones in SET_ZONE. -- Filter criteria are defined by: -- - -- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*. + -- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern in their name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- -- Once the filter criteria have been set for the SET_ZONE, you can start filtering using: -- @@ -6171,7 +6185,8 @@ do -- SET_ZONE_GOAL -- You can set filter criteria to build the collection of zones in SET_ZONE_GOAL. -- Filter criteria are defined by: -- - -- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*. + -- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern in their name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- -- Once the filter criteria have been set for the SET_ZONE_GOAL, you can start filtering using: -- @@ -6483,7 +6498,8 @@ do -- SET_OPSZONE -- You can set filter criteria to build the collection of zones in SET_OPSZONE. -- Filter criteria are defined by: -- - -- * @{#SET_OPSZONE.FilterPrefixes}: Builds the SET_OPSZONE with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*. + -- * @{#SET_OPSZONE.FilterPrefixes}: Builds the SET_OPSZONE with the zones having a certain text pattern in their name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- -- Once the filter criteria have been set for the SET_OPSZONE, you can start filtering using: -- @@ -6949,7 +6965,8 @@ do -- SET_OPSGROUP -- * @{#SET_OPSGROUP.FilterCoalitions}: Builds the SET_OPSGROUP with the groups belonging to the coalition(s). -- * @{#SET_OPSGROUP.FilterCategories}: Builds the SET_OPSGROUP with the groups belonging to the category(ies). -- * @{#SET_OPSGROUP.FilterCountries}: Builds the SET_OPSGROUP with the groups belonging to the country(ies). - -- * @{#SET_OPSGROUP.FilterPrefixes}: Builds the SET_OPSGROUP with the groups *containing* the given string in the group name. **Attention!** Bad naming convention, as this not really filtering *prefixes*. + -- * @{#SET_OPSGROUP.FilterPrefixes}: Builds the SET_OPSGROUP with the groups *containing* the given string in the group name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. + -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- * @{#SET_OPSGROUP.FilterActive}: Builds the SET_OPSGROUP with the groups that are only active. Groups that are inactive (late activation) won't be included in the set! -- -- For the Category Filter, extra methods have been added: From 30cadf0d4268c30a02fd146298d7dac8cbbf83d5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 26 Aug 2023 16:08:45 +0200 Subject: [PATCH 332/603] test --- Moose Development/Moose/DCS.lua | 38 ++++++++++++++++--- .../Moose/Wrapper/Controllable.lua | 20 ++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index e26bff6b8..c5c384823 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -1210,14 +1210,17 @@ do -- Unit -- @field #Distance detectionDistanceHRM detection distance for RCS=1m^2 in high-resolution mapping mode, nil if radar has no HRM -- @field #Unit.Radar.detectionDistanceAir detectionDistanceAir detection distance for RCS=1m^2 airborne target, nil if radar doesn't support air search + --- A radar. -- @type Unit.Radar.detectionDistanceAir -- @field #Unit.Radar.detectionDistanceAir.upperHemisphere upperHemisphere -- @field #Unit.Radar.detectionDistanceAir.lowerHemisphere lowerHemisphere + --- A radar. -- @type Unit.Radar.detectionDistanceAir.upperHemisphere -- @field #Distance headOn -- @field #Distance tailOn + --- A radar. -- @type Unit.Radar.detectionDistanceAir.lowerHemisphere -- @field #Distance headOn -- @field #Distance tailOn @@ -1520,6 +1523,7 @@ do -- AI -- @field IR_POINTER -- @field LASER + --- -- @type AI.Task.WaypointType -- @field TAKEOFF -- @field TAKEOFF_PARKING @@ -1527,14 +1531,17 @@ do -- AI -- @field TAKEOFF_PARKING_HOT -- @field LAND + --- -- @type AI.Task.TurnMethod -- @field FLY_OVER_POINT -- @field FIN_POINT + --- -- @type AI.Task.AltitudeType -- @field BARO -- @field RADIO + --- -- @type AI.Task.VehicleFormation -- @field OFF_ROAD -- @field ON_ROAD @@ -1545,15 +1552,18 @@ do -- AI -- @field ECHELON_LEFT -- @field ECHELON_RIGHT + --- -- @type AI.Option -- @field #AI.Option.Air Air -- @field #AI.Option.Ground Ground -- @field #AI.Option.Naval Naval + --- -- @type AI.Option.Air -- @field #AI.Option.Air.id id -- @field #AI.Option.Air.val val + --- -- @type AI.Option.Ground -- @field #AI.Option.Ground.id id -- @field #AI.Option.Ground.val val @@ -1564,7 +1574,7 @@ do -- AI -- @field #AI.Option.Naval.id id -- @field #AI.Option.Naval.val val - + --- -- @type AI.Option.Air.id -- @field NO_OPTION -- @field ROE @@ -1588,6 +1598,7 @@ do -- AI -- @field JETT_TANKS_IF_EMPTY -- @field FORCED_ATTACK + --- -- @type AI.Option.Air.id.FORMATION -- @field LINE_ABREAST -- @field TRAIL @@ -1606,20 +1617,22 @@ do -- AI -- @field COMBAT_BOX -- @field JAVELIN_DOWN - + --- -- @type AI.Option.Air.val -- @field #AI.Option.Air.val.ROE ROE -- @field #AI.Option.Air.val.REACTION_ON_THREAT REACTION_ON_THREAT -- @field #AI.Option.Air.val.RADAR_USING RADAR_USING -- @field #AI.Option.Air.val.FLARE_USING FLARE_USING + --- -- @type AI.Option.Air.val.ROE -- @field WEAPON_FREE -- @field OPEN_FIRE_WEAPON_FREE -- @field OPEN_FIRE -- @field RETURN_FIRE -- @field WEAPON_HOLD - + + --- -- @type AI.Option.Air.val.REACTION_ON_THREAT -- @field NO_REACTION -- @field PASSIVE_DEFENCE @@ -1627,24 +1640,28 @@ do -- AI -- @field BYPASS_AND_ESCAPE -- @field ALLOW_ABORT_MISSION + --- -- @type AI.Option.Air.val.RADAR_USING -- @field NEVER -- @field FOR_ATTACK_ONLY -- @field FOR_SEARCH_IF_REQUIRED -- @field FOR_CONTINUOUS_SEARCH + --- -- @type AI.Option.Air.val.FLARE_USING -- @field NEVER -- @field AGAINST_FIRED_MISSILE -- @field WHEN_FLYING_IN_SAM_WEZ -- @field WHEN_FLYING_NEAR_ENEMIES - + + --- -- @type AI.Option.Air.val.ECM_USING -- @field NEVER_USE -- @field USE_IF_ONLY_LOCK_BY_RADAR -- @field USE_IF_DETECTED_LOCK_BY_RADAR -- @field ALWAYS_USE - + + --- -- @type AI.Option.Air.val.MISSILE_ATTACK -- @field MAX_RANGE -- @field NEZ_RANGE @@ -1652,7 +1669,7 @@ do -- AI -- @field TARGET_THREAT_EST -- @field RANDOM_RANGE - + --- -- @type AI.Option.Ground.id -- @field NO_OPTION -- @field ROE @{#AI.Option.Ground.val.ROE} @@ -1662,41 +1679,50 @@ do -- AI -- @field ENGAGE_AIR_WEAPONS -- @field AC_ENGAGEMENT_RANGE_RESTRICTION + --- -- @type AI.Option.Ground.mid -- Moose added -- @field RESTRICT_AAA_MIN 27 -- @field RESTRICT_AAA_MAX 29 -- @field RESTRICT_TARGETS @{#AI.Option.Ground.mval.ENGAGE_TARGETS} 28 + --- -- @type AI.Option.Ground.val -- @field #AI.Option.Ground.val.ROE ROE -- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE -- @field #AI.Option.Ground.val.ENGAGE_TARGETS RESTRICT_TARGETS + --- -- @type AI.Option.Ground.val.ROE -- @field OPEN_FIRE -- @field RETURN_FIRE -- @field WEAPON_HOLD + --- -- @type AI.Option.Ground.mval -- Moose added -- @field #AI.Option.Ground.mval.ENGAGE_TARGETS ENGAGE_TARGETS + --- -- @type AI.Option.Ground.mval.ENGAGE_TARGETS -- Moose added -- @field ANY_TARGET -- 0 -- @field AIR_UNITS_ONLY -- 1 -- @field GROUND_UNITS_ONLY -- 2 + --- -- @type AI.Option.Ground.val.ALARM_STATE -- @field AUTO -- @field GREEN -- @field RED + --- -- @type AI.Option.Naval.id -- @field NO_OPTION -- @field ROE + --- -- @type AI.Option.Naval.val -- @field #AI.Option.Naval.val.ROE ROE + --- -- @type AI.Option.Naval.val.ROE -- @field OPEN_FIRE -- @field RETURN_FIRE diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 1f4714e26..aaf789652 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -521,6 +521,26 @@ function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index ) return DCSTaskWrappedAction end +--- Return an Empty Task. +-- @param #CONTROLLABLE self +-- @return DCS#Task +function CONTROLLABLE:TaskEmptyTask() + + local DCSTaskWrappedAction = { + ["id"] = "WrappedAction", + ["params"] = { + ["action"] = { + ["id"] = "Script", + ["params"] = { + ["command"] = "", + }, + }, + }, + } + + return DCSTaskWrappedAction +end + --- Set a Task at a Waypoint using a Route list. -- @param #CONTROLLABLE self -- @param #table Waypoint The Waypoint! From 219e46793e1080cc6fbdfc530eafcbeef0829dba Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 30 Aug 2023 16:45:09 +0200 Subject: [PATCH 333/603] #MANTIS * CH assets --- Moose Development/Moose/Functional/Mantis.lua | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 089a5301a..84f682a5c 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -431,24 +431,24 @@ MANTIS.SamDataCH = { -- units from CH (Military Assets by Currenthill) -- https://www.currenthill.com/ -- group name MUST contain CHM to ID launcher type correctly! - ["2S38 CH"] = { Range=8, Blindspot=0.5, Height=6, Type="Short", Radar="2S38" }, - ["PantsirS1 CH"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" }, - ["PantsirS2 CH"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" }, - ["PGL-625 CH"] = { Range=10, Blindspot=0.5, Height=5, Type="Short", Radar="PGL_625" }, - ["HQ-17A CH"] = { Range=20, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" }, - ["M903PAC2 CH"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" }, - ["M903PAC3 CH"] = { Range=120, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" }, - ["TorM2 CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" }, - ["TorM2K CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" }, - ["TorM2M CH"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" }, - ["NASAMS3-AMRAAMER CH"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" }, - ["NASAMS3-AIM9X2 CH"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" }, - ["C-RAM CH"] = { Range=2, Blindspot=0, Height=2, Type="Short", Radar="CH_Centurion_C_RAM" }, - ["PGZ-09 CH"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="CH_PGZ09" }, - ["S350-9M100 CH"] = { Range=15, Blindspot=1.5, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" }, - ["S350-9M96D CH"] = { Range=150, Blindspot=2.5, Height=30, Type="Long", Radar="CH_S350_50P6_9M96D" }, - ["LAV-AD CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_LAVAD" }, - ["HQ-22 CHM"] = { Range=170, Blindspot=5, Height=27, Type="Long", Radar="CH_HQ22_LN" }, + ["2S38 CH"] = { Range=8, Blindspot=0.5, Height=6, Type="Short", Radar="2S38" }, + ["PantsirS1 CH"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" }, + ["PantsirS2 CH"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" }, + ["PGL-625 CH"] = { Range=10, Blindspot=0.5, Height=5, Type="Short", Radar="PGL_625" }, + ["HQ-17A CH"] = { Range=20, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" }, + ["M903PAC2 CH"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" }, + ["M903PAC3 CH"] = { Range=120, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" }, + ["TorM2 CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" }, + ["TorM2K CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" }, + ["TorM2M CH"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" }, + ["NASAMS3-AMRAAMER CH"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" }, + ["NASAMS3-AIM9X2 CH"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" }, + ["C-RAM CH"] = { Range=2, Blindspot=0, Height=2, Type="Short", Radar="CH_Centurion_C_RAM" }, + ["PGZ-09 CH"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="CH_PGZ09" }, + ["S350-9M100 CH"] = { Range=15, Blindspot=1.5, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" }, + ["S350-9M96D CH"] = { Range=150, Blindspot=2.5, Height=30, Type="Long", Radar="CH_S350_50P6_9M96D" }, + ["LAV-AD CH"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_LAVAD" }, + ["HQ-22 CH"] = { Range=170, Blindspot=5, Height=27, Type="Long", Radar="CH_HQ22_LN" }, } ----------------------------------------------------------------------- @@ -614,7 +614,7 @@ do -- TODO Version -- @field #string version - self.version="0.8.12" + self.version="0.8.12a" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) --- FSM Functions --- From 2940eaed63efe0fd99c6cf5d891a6ac2c0ecfe00 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 31 Aug 2023 11:53:13 +0200 Subject: [PATCH 334/603] #AIRBOSS --- Moose Development/Moose/Ops/Airboss.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 96828dc05..3c5cf8bce 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -14844,7 +14844,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p --local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle local text = call.subtitle - self:I(self.lid..text) + self:T(self.lid..text) local srstext = self:_GetNiceSRSText(text) self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, nil, 0.1, nil, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) end @@ -15261,8 +15261,8 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio --sender = "AIRBOSS" end - self:I(self.lid..text) - self:I({sender,frequency,modulation,voice}) + self:T(self.lid..text) + self:T({sender,frequency,modulation,voice}) local srstext = self:_GetNiceSRSText(text) self.SRSQ:NewTransmission(srstext,duration,self.SRS,nil,0.1,nil,nil,nil,frequency,modulation,gender,culture,voice,nil,sender) end From 7769ca8c3f7eb23d248cf6e5ab954457660d71c2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 31 Aug 2023 11:59:03 +0200 Subject: [PATCH 335/603] #MANTIS * Add zone filter option --- Moose Development/Moose/Functional/Mantis.lua | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 84f682a5c..946901102 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -467,6 +467,7 @@ do --@param #string awacs Group name of your Awacs (optional) --@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN (optional) --@param #number Padding For #SEAD - Extra number of seconds to add to radar switch-back-on time (optional) + --@param #table Zones Table of Core.Zone#ZONE Zones Consider SAM groups in this zone(s) only for this MANTIS instance, must be handed as #table of Zone objects --@return #MANTIS self --@usage Start up your MANTIS with a basic setting -- @@ -485,7 +486,7 @@ do -- mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs") -- mybluemantis:Start() -- - function MANTIS:New(name,samprefix,ewrprefix,hq,coalition,dynamic,awacs, EmOnOff, Padding) + function MANTIS:New(name,samprefix,ewrprefix,hq,coalition,dynamic,awacs, EmOnOff, Padding, Zones) -- DONE: Create some user functions for these -- DONE: Make HQ useful @@ -546,6 +547,7 @@ do self.maxclassic = 6 self.autoshorad = true self.ShoradGroupSet = SET_GROUP:New() -- Core.Set#SET_GROUP + self.FilterZones = Zones self.UseEmOnOff = true if EmOnOff == false then @@ -595,16 +597,23 @@ do self:T({self.ewr_templates}) + self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition) + self.EWR_Group = SET_GROUP:New():FilterPrefixes(self.ewr_templates):FilterCoalitions(self.Coalition) + + if self.FilterZones then + self.SAM_Group:FilterZones(self.FilterZones) + end + if self.dynamic then -- Set SAM SET_GROUP - self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart() + self.SAM_Group:FilterStart() -- Set EWR SET_GROUP - self.EWR_Group = SET_GROUP:New():FilterPrefixes(self.ewr_templates):FilterCoalitions(self.Coalition):FilterStart() + self.EWR_Group:FilterStart() else -- Set SAM SET_GROUP - self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce() + self.SAM_Group:FilterOnce() -- Set EWR SET_GROUP - self.EWR_Group = SET_GROUP:New():FilterPrefixes(self.ewr_templates):FilterCoalitions(self.Coalition):FilterOnce() + self.EWR_Group:FilterOnce() end -- set up CC @@ -614,7 +623,7 @@ do -- TODO Version -- @field #string version - self.version="0.8.12a" + self.version="0.8.14" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) --- FSM Functions --- From cb6dd0d6c7bc15ab2a4230002c4ddc3b26d8b92d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Sep 2023 08:51:51 +0200 Subject: [PATCH 336/603] #MANTIS --- Moose Development/Moose/Functional/Mantis.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 946901102..9bd80659b 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -22,7 +22,7 @@ -- @module Functional.Mantis -- @image Functional.Mantis.jpg -- --- Last Update: July 2023 +-- Last Update: Sept 2023 ------------------------------------------------------------------------- --- **MANTIS** class, extends Core.Base#BASE From d9c656f6d211cb75cd2edcc59aaab6f3e65e13cc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Sep 2023 09:42:24 +0200 Subject: [PATCH 337/603] #Airboss --- Moose Development/Moose/Ops/Airboss.lua | 144 ++++++++++++------------ 1 file changed, 73 insertions(+), 71 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 3c5cf8bce..3f7096beb 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -2543,7 +2543,7 @@ function AIRBOSS:AddRecoveryWindow( starttime, stoptime, case, holdingoffset, tu return self end if Tstop <= Tnow then - self:I( string.format( "WARNING: Recovery stop time %s already over. Tnow=%s! Recovery window rejected.", UTILS.SecondsToClock( Tstop ), UTILS.SecondsToClock( Tnow ) ) ) + string.format( "WARNING: Recovery stop time %s already over. Tnow=%s! Recovery window rejected.", UTILS.SecondsToClock( Tstop ), UTILS.SecondsToClock( Tnow ) ) return self end @@ -3258,7 +3258,7 @@ function AIRBOSS:SoundCheckLSO( delay ) end -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) end end @@ -3293,7 +3293,7 @@ function AIRBOSS:SoundCheckMarshal( delay ) end -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) end end @@ -3684,7 +3684,7 @@ function AIRBOSS:onafterStatus( From, Event, To ) if i == 0 then text = text .. " none" end - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Check for collision. if collision then @@ -6960,7 +6960,7 @@ function AIRBOSS:_GetFreeStack( ai, case, empty ) end - self:I( self.lid .. string.format( "Returning free stack %s", tostring( nfree ) ) ) + self:T( self.lid .. string.format( "Returning free stack %s", tostring( nfree ) ) ) return nfree end @@ -7894,7 +7894,7 @@ function AIRBOSS:_RemoveFlight( flight, completely ) -- Remove player from players table. local playerdata = self.players[flight.name] if playerdata then - self:I( self.lid .. string.format( "Removing player %s completely.", flight.name ) ) + self:T( self.lid .. string.format( "Removing player %s completely.", flight.name ) ) self.players[flight.name] = nil end @@ -11592,11 +11592,11 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) -- Get direction the wind is blowing from. This is where we want to go. local windfrom, vwind = self:GetWind( nil, nil, coord ) - --self:I("windfrom="..windfrom.." vwind="..vwind) + --self:T("windfrom="..windfrom.." vwind="..vwind) vwind = vwind + adjustDegreesForWindSpeed(vwind) - --self:I("windfrom="..windfrom.." (c)vwind="..vwind) + --self:T("windfrom="..windfrom.." (c)vwind="..vwind) -- Actually, we want the runway in the wind. local intowind = windfrom - self.carrierparam.rwyangle @@ -14793,60 +14793,62 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p else -- SRS transmission - - local frequency = self.MarshalRadio.frequency - local modulation = self.MarshalRadio.modulation - local voice = nil - local gender = nil - local culture = nil - - if radio.alias == "AIRBOSS" then - frequency = self.AirbossRadio.frequency - modulation = self.AirbossRadio.modulation - voice = self.AirbossRadio.voice - gender = self.AirbossRadio.gender - culture = self.AirbossRadio.culture - end - - if radio.alias == "MARSHAL" then - voice = self.MarshalRadio.voice - gender = self.MarshalRadio.gender - culture = self.MarshalRadio.culture - end - - if radio.alias == "LSO" then - frequency = self.LSORadio.frequency - modulation = self.LSORadio.modulation - voice = self.LSORadio.voice - gender = self.LSORadio.gender - culture = self.LSORadio.culture - end - - if pilotcall then - voice = self.PilotRadio.voice - gender = self.PilotRadio.gender - culture = self.PilotRadio.culture - radio.alias = "PILOT" - end + if call.subtitle ~= nil and string.len(call.subtitle) > 1 then - if not radio.alias then - -- TODO - what freq to use here? - frequency = self.AirbossRadio.frequency - modulation = self.AirbossRadio.modulation - radio.alias = "AIRBOSS" + local frequency = self.MarshalRadio.frequency + local modulation = self.MarshalRadio.modulation + local voice = nil + local gender = nil + local culture = nil + + if radio.alias == "AIRBOSS" then + frequency = self.AirbossRadio.frequency + modulation = self.AirbossRadio.modulation + voice = self.AirbossRadio.voice + gender = self.AirbossRadio.gender + culture = self.AirbossRadio.culture + end + + if radio.alias == "MARSHAL" then + voice = self.MarshalRadio.voice + gender = self.MarshalRadio.gender + culture = self.MarshalRadio.culture + end + + if radio.alias == "LSO" then + frequency = self.LSORadio.frequency + modulation = self.LSORadio.modulation + voice = self.LSORadio.voice + gender = self.LSORadio.gender + culture = self.LSORadio.culture + end + + if pilotcall then + voice = self.PilotRadio.voice + gender = self.PilotRadio.gender + culture = self.PilotRadio.culture + radio.alias = "PILOT" + end + + if not radio.alias then + -- TODO - what freq to use here? + frequency = self.AirbossRadio.frequency + modulation = self.AirbossRadio.modulation + radio.alias = "AIRBOSS" + end + + local volume = nil + + if loud then + volume = 1.0 + end + + --local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle + local text = call.subtitle + self:T(self.lid..text) + local srstext = self:_GetNiceSRSText(text) + self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, nil, 0.1, nil, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) end - - local volume = nil - - if loud then - volume = 1.0 - end - - --local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle - local text = call.subtitle - self:T(self.lid..text) - local srstext = self:_GetNiceSRSText(text) - self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, nil, 0.1, nil, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) end end @@ -15158,7 +15160,7 @@ end -- @param #boolean clear If true, clear screen from previous messages. -- @param #number delay Delay in seconds, before the message is displayed. function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duration, clear, delay ) - self:I({sender,receiver,message}) + self:T({sender,receiver,message}) if playerData and message and message ~= "" then -- Default duration. @@ -15634,7 +15636,7 @@ function AIRBOSS:_LSOCallAircraftBall( modex, nickname, fuelstate ) local text = string.format( "%s Ball, %.1f.", nickname, fuelstate ) -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Nickname UPPERCASE. local NICKNAME = nickname:upper() @@ -15670,7 +15672,7 @@ function AIRBOSS:_MarshalCallGasAtTanker( modex ) local text = string.format( "Bingo fuel! Going for gas at the recovery tanker." ) -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Create new call to display complete subtitle. @@ -15694,7 +15696,7 @@ function AIRBOSS:_MarshalCallGasAtDivert( modex, divertname ) local text = string.format( "Bingo fuel! Going for gas at divert field %s.", divertname ) -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Create new call to display complete subtitle. local call = self:_NewRadioCall( self.PilotCall.BINGOFUEL, modex, text, self.Tmessage, nil, modex ) @@ -15716,7 +15718,7 @@ function AIRBOSS:_MarshalCallRecoveryStopped( case ) local text = string.format( "Case %d recovery ops are stopped. Deck is closed.", case ) -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Create new call to display complete subtitle. local call = self:_NewRadioCall( self.MarshalCall.CASE, "AIRBOSS", text, self.Tmessage, "99" ) @@ -15757,7 +15759,7 @@ function AIRBOSS:_MarshalCallRecoveryPausedResumedAt( clock ) local text = string.format( "aircraft recovery is paused and will be resumed at %s.", clock ) -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Create new call with full subtitle. local call = self:_NewRadioCall( self.MarshalCall.RECOVERYPAUSEDRESUMED, "AIRBOSS", text, self.Tmessage, "99" ) @@ -15784,7 +15786,7 @@ function AIRBOSS:_MarshalCallClearedForRecovery( modex, case ) local text = string.format( "you're cleared for Case %d recovery.", case ) -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Create new call with full subtitle. local call = self:_NewRadioCall( self.MarshalCall.CLEAREDFORRECOVERY, "MARSHAL", text, self.Tmessage, modex ) @@ -15822,7 +15824,7 @@ function AIRBOSS:_MarshalCallNewFinalBearing( FB ) local text = string.format( "new final bearing %03d°.", FB ) -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Create new call with full subtitle. local call = self:_NewRadioCall( self.MarshalCall.NEWFB, "AIRBOSS", text, self.Tmessage, "99" ) @@ -15845,7 +15847,7 @@ function AIRBOSS:_MarshalCallCarrierTurnTo( hdg ) local text = string.format( "carrier is now starting turn to heading %03d°.", hdg ) -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Create new call with full subtitle. local call = self:_NewRadioCall( self.MarshalCall.CARRIERTURNTOHEADING, "AIRBOSS", text, self.Tmessage, "99" ) @@ -15876,7 +15878,7 @@ function AIRBOSS:_MarshalCallStackFull( modex, nwaiting ) end -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Create new call with full subtitle. local call = self:_NewRadioCall( self.MarshalCall.STACKFULL, "AIRBOSS", text, self.Tmessage, modex ) @@ -15947,7 +15949,7 @@ function AIRBOSS:_MarshalCallArrived( modex, case, brc, altitude, charlie, qfe ) local text = string.format( "Case %d, expected BRC %03d°, hold at angels %d. Expected Charlie Time %s. Altimeter %.2f. Report see me.", case, brc, angels, charlie, qfe ) -- Debug message. - self:I( self.lid .. text ) + self:T( self.lid .. text ) -- Create new call to display complete subtitle. local casecall = self:_NewRadioCall( self.MarshalCall.CASE, "MARSHAL", text, self.Tmessage, modex ) From f43924fbb023de14dab178b713ab73eacf00fceb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 5 Sep 2023 10:34:07 +0200 Subject: [PATCH 338/603] #CLIENTMENU --- Moose Development/Moose/Core/ClientMenu.lua | 10 ++++++++-- Moose Development/Moose/Ops/ATIS.lua | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 1f11eebaa..458143c77 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -35,6 +35,7 @@ -- @field #string lid Lid for log entries -- @field #string version Version string -- @field #string name Name +-- @field #string groupname Group name -- @field #table path -- @field #table parentpath -- @field #CLIENTMENU Parent @@ -57,7 +58,7 @@ CLIENTMENU = { ClassName = "CLIENTMENUE", lid = "", - version = "0.1.0", + version = "0.1.1", name = nil, path = nil, group = nil, @@ -68,6 +69,7 @@ CLIENTMENU = { Generic = false, debug = false, Controller = nil, + groupname = nil, } --- @@ -91,6 +93,7 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) self.group = Client:GetGroup() self.client = Client self.GroupID = self.group:GetID() + self.groupname = self.group:GetName() or "Unknown Groupname" else self.Generic = true end @@ -190,7 +193,10 @@ function CLIENTMENU:RemoveF10() self:T(self.lid.."RemoveF10") if self.GroupID then --self:I(self.lid.."Removing "..table.concat(self.path,";")) - missionCommands.removeItemForGroup(self.GroupID , self.path ) + local status, err = pcall(missionCommands.removeItemForGroup(self.GroupID , self.path )) + if not status then + self:I(string.format("**** Error Removing Menu Entry %s for %s!",tostring(self.name),self.groupname)) + end end return self end diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index b90973b05..74a28d9f1 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1840,6 +1840,7 @@ function ATIS:onafterBroadcast( From, Event, To ) subtitle = subtitle .. " Airport" end if not self.useSRS then + --self:I(string.format( "%s/%s.ogg", self.theatre, self.airbasename )) self.radioqueue:NewTransmission( string.format( "%s/%s.ogg", self.theatre, self.airbasename ), 3.0, self.soundpath, nil, nil, subtitle, self.subduration ) end local alltext = subtitle From e6a7416acf064a3fb54eb3e247681b6f76ef88cf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Sep 2023 12:43:23 +0200 Subject: [PATCH 339/603] #PLAYERTASK --- Moose Development/Moose/Core/ClientMenu.lua | 17 ++++++++++------- Moose Development/Moose/Ops/PlayerTask.lua | 15 ++++----------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 458143c77..d5ace7d9e 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -20,7 +20,7 @@ -- -- @module Core.ClientMenu -- @image Core_Menu.JPG --- last change: July 2023 +-- last change: Sept 2023 -- TODO ---------------------------------------------------------------------------------------------------------------- @@ -193,7 +193,10 @@ function CLIENTMENU:RemoveF10() self:T(self.lid.."RemoveF10") if self.GroupID then --self:I(self.lid.."Removing "..table.concat(self.path,";")) - local status, err = pcall(missionCommands.removeItemForGroup(self.GroupID , self.path )) + local function RemoveFunction() + return missionCommands.removeItemForGroup(self.GroupID , self.path ) + end + local status, err = pcall(RemoveFunction) if not status then self:I(string.format("**** Error Removing Menu Entry %s for %s!",tostring(self.name),self.groupname)) end @@ -477,13 +480,13 @@ function CLIENTMENUMANAGER:FindUUIDsByText(Text,Parent) for _uuid,_entry in pairs(self.flattree) do local Entry = _entry -- #CLIENTMENU if Parent then - if Entry and string.find(Entry.name,Text) and string.find(Entry.UUID,Parent.UUID) then + if Entry and string.find(Entry.name,Text,1,true) and string.find(Entry.UUID,Parent.UUID,1,true) then table.insert(matches,_uuid) table.insert(entries,Entry ) n=n+1 end else - if Entry and string.find(Entry.name,Text) then + if Entry and string.find(Entry.name,Text,1,true) then table.insert(matches,_uuid) table.insert(entries,Entry ) n=n+1 @@ -519,7 +522,7 @@ function CLIENTMENUMANAGER:FindUUIDsByParent(Parent) for _uuid,_entry in pairs(self.flattree) do local Entry = _entry -- #CLIENTMENU if Parent then - if Entry and string.find(Entry.UUID,Parent.UUID) then + if Entry and string.find(Entry.UUID,Parent.UUID,1,true) then table.insert(matches,_uuid) table.insert(entries,Entry ) n=n+1 @@ -720,7 +723,7 @@ function CLIENTMENUMANAGER:DeleteGenericEntry(Entry) --self:I("Level = "..i) for _id,_uuid in pairs(tbl[i]) do self:T(_uuid) - if string.find(_uuid,uuid) or _uuid == uuid then + if string.find(_uuid,uuid,1,true) or _uuid == uuid then --self:I("Match for ".._uuid) self.menutree[i][_id] = nil self.flattree[_uuid] = nil @@ -749,7 +752,7 @@ function CLIENTMENUMANAGER:RemoveGenericSubEntries(Entry) self:T("Level = "..i) for _id,_uuid in pairs(tbl[i]) do self:T(_uuid) - if string.find(_uuid,uuid) then + if string.find(_uuid,uuid,1,true) then self:T("Match for ".._uuid) self.menutree[i][_id] = nil self.flattree[_uuid] = nil diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index f91893c7e..190bc8976 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update July 2023 +-- @date Last Update Sept 2023 do @@ -1551,7 +1551,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.60a" +PLAYERTASKCONTROLLER.version="0.1.61" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2503,7 +2503,8 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue() end end local TNow = timer.getAbsTime() - if TNow - task.timestamp > 10 then + if TNow - task.timestamp > 5 then + self:_RemoveMenuEntriesForTask(task) local task = self.TaskQueue:PullByID(_id) -- Ops.PlayerTask#PLAYERTASK task = nil end @@ -3516,13 +3517,6 @@ function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate() if _task.InMenu then self:T("**** Task already in Menu ".._task.Target:GetName()) else - --local pilotcount = _task:CountClients() - --local newtext = "]" - --local tnow = timer.getTime() - -- marker for new tasks - --if tnow - _task.timestamp < 60 then - --newtext = "*]" - --end local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale) --local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext) local text = string.format("%s %03d",menutaskno,_task.PlayerTaskNr) @@ -3533,7 +3527,6 @@ function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate() text = string.format("%s (%03d)",name,_task.PlayerTaskNr) end end - --local taskentry = MENU_GROUP_COMMAND:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task):SetTag(newtag) local parenttable, number = controller:FindEntriesByText(_tasktype,JoinMenu) if number > 0 then local Parent = parenttable[1] From 568f76b0b282dbf9b62205604c5bb48f021bf31d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Sep 2023 18:01:16 +0200 Subject: [PATCH 340/603] #UTILS * Remove utils.routines --- Moose Development/Moose/Core/Base.lua | 16 ++++++++-------- Moose Development/Moose/Core/Database.lua | 4 ++-- Moose Development/Moose/Core/Scheduler.lua | 2 +- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 1 - Moose Development/Moose/DCS.lua | 2 +- Moose Development/Moose/Functional/Escort.lua | 4 ++-- .../Moose/Functional/MissileTrainer.lua | 2 +- Moose Development/Moose/Functional/Movement.lua | 1 - Moose Development/Moose/Modules_local.lua | 1 - Moose Development/Moose/Utilities/Routines.lua | 6 +++--- Moose Development/Moose/Utilities/Utils.lua | 6 +++--- Moose Development/Moose/Wrapper/Controllable.lua | 6 +++--- Moose Development/Moose/Wrapper/Group.lua | 6 +++--- Moose Setup/Moose.files | 1 - 15 files changed, 28 insertions(+), 32 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 74cd76ca1..38b0cbc1d 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -229,7 +229,7 @@ FORMATION = { -- @param #BASE self -- @return #BASE function BASE:New() - --local self = routines.utils.deepCopy( self ) -- Create a new self instance + --local self = UTILS.DeepCopy( self ) -- Create a new self instance local self = UTILS.DeepCopy(self) _ClassID = _ClassID + 1 @@ -252,7 +252,7 @@ end function BASE:Inherit( Child, Parent ) -- Create child. - local Child = routines.utils.deepCopy( Child ) + local Child = UTILS.DeepCopy( Child ) if Child ~= nil then @@ -1167,7 +1167,7 @@ function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) if DebugInfoFrom then LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, UTILS.OneLineSerialize( Arguments ) ) ) end end end @@ -1241,7 +1241,7 @@ function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) if DebugInfoFrom then LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s", LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s", LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, UTILS.OneLineSerialize( Arguments ) ) ) end end end @@ -1311,9 +1311,9 @@ function BASE:E( Arguments ) LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, UTILS.OneLineSerialize( Arguments ) ) ) else - env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) ) + env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, UTILS.OneLineSerialize( Arguments ) ) ) end end @@ -1338,9 +1338,9 @@ function BASE:I( Arguments ) LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.OneLineSerialize( Arguments ) ) ) else - env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) ) + env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.OneLineSerialize( Arguments ) ) ) end end diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 4d46802e9..280682d80 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1854,7 +1854,7 @@ function DATABASE:_RegisterTemplates() self.Navpoints = {} self.UNITS = {} - --Build routines.db.units and self.Navpoints + --Build self.Navpoints for CoalitionName, coa_data in pairs(env.mission.coalition) do self:T({CoalitionName=CoalitionName}) @@ -1876,7 +1876,7 @@ function DATABASE:_RegisterTemplates() for nav_ind, nav_data in pairs(coa_data.nav_points) do if type(nav_data) == 'table' then - self.Navpoints[CoalitionName][nav_ind] = routines.utils.deepCopy(nav_data) + self.Navpoints[CoalitionName][nav_ind] = UTILS.DeepCopy(nav_data) self.Navpoints[CoalitionName][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. self.Navpoints[CoalitionName][nav_ind]['point'] = {} -- point is used by SSE, support it. diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index e228fe628..36804e2ab 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -208,7 +208,7 @@ SCHEDULER = { -- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat. -- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped. -- @return #SCHEDULER self. --- @return #table The ScheduleID of the planned schedule. +-- @return #string The ScheduleID of the planned schedule. function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop ) local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index b2b919c55..60236095d 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -439,7 +439,7 @@ do -- SET_BASE function SET_BASE:SetDatabase( BaseSet ) -- Copy the filter criteria of the BaseSet - local OtherFilter = routines.utils.deepCopy( BaseSet.Filter ) + local OtherFilter = UTILS.DeepCopy( BaseSet.Filter ) self.Filter = OtherFilter -- Now base the new Set on the BaseSet diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 95719aa9e..db5f915a1 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1202,7 +1202,6 @@ function SPAWN:InitCleanUp( SpawnCleanUpInterval ) local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup() self:T( { "CleanUp Scheduler:", SpawnGroup } ) - -- self.CleanUpFunction = routines.scheduleFunction( self._SpawnCleanUpScheduler, { self }, timer.getTime() + 1, SpawnCleanUpInterval ) self.CleanUpScheduler = SCHEDULER:New( self, self._SpawnCleanUpScheduler, {}, 1, SpawnCleanUpInterval, 0.2 ) return self end diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 908cc4c46..b03eff621 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -983,7 +983,7 @@ do -- Spot end -- Spot do -- Controller - --- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C. + --- Controller is an object that performs A.I.-tasks. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C. -- -- This class has 2 types of functions: -- diff --git a/Moose Development/Moose/Functional/Escort.lua b/Moose Development/Moose/Functional/Escort.lua index 90f9f925a..6ae9d694f 100644 --- a/Moose Development/Moose/Functional/Escort.lua +++ b/Moose Development/Moose/Functional/Escort.lua @@ -600,7 +600,7 @@ function ESCORT:MenuReportTargets( Seconds ) self.EscortMenuAttackNearbyTargets = MENU_GROUP:New( self.EscortClient:GetGroup(), "Attack targets", self.EscortMenu ) - self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, Seconds ) + self.ReportTargetsScheduler, self.ReportTargetsSchedulerID = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, Seconds ) return self end @@ -809,7 +809,7 @@ function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 ) end else - routines.removeFunction( self.ReportTargetsScheduler ) + self.ReportTargetsScheduler:Remove(self.ReportTargetsSchedulerID) self.ReportTargetsScheduler = nil end end diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index 386eb8275..8514f6f86 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -560,7 +560,7 @@ function MISSILETRAINER:_AddBearing( Client, TrainerWeapon ) local DirectionVector = { x = PositionMissile.x - TargetVec3.x, y = PositionMissile.y - TargetVec3.y, z = PositionMissile.z - TargetVec3.z } local DirectionRadians = math.atan2( DirectionVector.z, DirectionVector.x ) - --DirectionRadians = DirectionRadians + routines.getNorthCorrection( PositionTarget ) + if DirectionRadians < 0 then DirectionRadians = DirectionRadians + 2 * math.pi end diff --git a/Moose Development/Moose/Functional/Movement.lua b/Moose Development/Moose/Functional/Movement.lua index 3b4031433..b2afe18a8 100644 --- a/Moose Development/Moose/Functional/Movement.lua +++ b/Moose Development/Moose/Functional/Movement.lua @@ -55,7 +55,6 @@ end --- Call this function to start the MOVEMENT scheduling. function MOVEMENT:ScheduleStart() self:F() - --self.MoveFunction = routines.scheduleFunction( self._Scheduler, { self }, timer.getTime() + 1, 120 ) self.MoveFunction = SCHEDULER:New( self, self._Scheduler, {}, 1, 120 ) end diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 959766e8f..73d9e9b05 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -1,5 +1,4 @@ __Moose.Include( 'Utilities\\Enums.lua' ) -__Moose.Include( 'Utilities\\Routines.lua' ) __Moose.Include( 'Utilities\\Utils.lua' ) __Moose.Include( 'Utilities\\Profiler.lua' ) __Moose.Include( 'Utilities\\Templates.lua' ) diff --git a/Moose Development/Moose/Utilities/Routines.lua b/Moose Development/Moose/Utilities/Routines.lua index 8545a1dd7..73aad3def 100644 --- a/Moose Development/Moose/Utilities/Routines.lua +++ b/Moose Development/Moose/Utilities/Routines.lua @@ -25,7 +25,7 @@ routines.utils.round = function( number, decimals ) end -- from http://lua-users.org/wiki/CopyTable -routines.utils.deepCopy = function( object ) +UTILS.DeepCopy = function( object ) local lookup_table = {} local function _copy( object ) if type( object ) ~= "table" then @@ -474,7 +474,7 @@ function routines.getRandPointInCircle( point, radius, innerRadius ) end routines.goRoute = function( group, path ) - local misTask = { id = 'Mission', params = { route = { points = routines.utils.deepCopy( path ) } } } + local misTask = { id = 'Mission', params = { route = { points = UTILS.DeepCopy( path ) } } } if type( group ) == 'string' then group = Group.getByName( group ) end @@ -1671,7 +1671,7 @@ routines.ground.patrolRoute = function( vars ) end if pType and string.lower( pType ) == 'doubleback' then - local curRoute = routines.utils.deepCopy( useRoute ) + local curRoute = UTILS.DeepCopy( useRoute ) for i = #curRoute, 2, -1 do useRoute[#useRoute + 1] = routines.ground.buildWP( curRoute[i], curRoute[i].action, curRoute[i].speed ) end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 27ace5e8c..401fb3c8b 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -324,7 +324,7 @@ UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a s ind_str[#ind_str + 1] = ']=' else --must be a string ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = routines.utils.basicSerialize(ind) + ind_str[#ind_str + 1] = UTILS.BasicSerialize(ind) ind_str[#ind_str + 1] = ']=' end @@ -335,7 +335,7 @@ UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a s tbl_str[#tbl_str + 1] = table.concat(ind_str) tbl_str[#tbl_str + 1] = table.concat(val_str) elseif type(val) == 'string' then - val_str[#val_str + 1] = routines.utils.basicSerialize(val) + val_str[#val_str + 1] = UTILS.BasicSerialize(val) val_str[#val_str + 1] = ',' tbl_str[#tbl_str + 1] = table.concat(ind_str) tbl_str[#tbl_str + 1] = table.concat(val_str) @@ -358,7 +358,7 @@ UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a s tbl_str[#tbl_str + 1] = "f() " .. tostring(ind) tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it else - env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) + env.info('unable to serialize value type ' .. UTILS.BasicSerialize(type(val)) .. ' at index ' .. tostring(ind)) env.info( debug.traceback() ) end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index aaf789652..1ba36453a 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2829,7 +2829,7 @@ end function CONTROLLABLE:GetTaskMission() self:F2( self.ControllableName ) - return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template ) + return UTILS.DeepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template ) end --- Return the mission route of the controllable. @@ -2838,7 +2838,7 @@ end function CONTROLLABLE:GetTaskRoute() self:F2( self.ControllableName ) - return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points ) + return UTILS.DeepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points ) end --- Return the route of a controllable by using the @{Core.Database#DATABASE} class. @@ -2874,7 +2874,7 @@ function CONTROLLABLE:CopyRoute( Begin, End, Randomize, Radius ) for TPointID = Begin + 1, #Template.route.points - End do if Template.route.points[TPointID] then - Points[#Points + 1] = routines.utils.deepCopy( Template.route.points[TPointID] ) + Points[#Points + 1] = UTILS.DeepCopy( Template.route.points[TPointID] ) if Randomize then if not Radius then Radius = 500 diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index bb8ed9408..1783a059d 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2200,7 +2200,7 @@ end function GROUP:GetTaskMission() self:F2( self.GroupName ) - return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template ) + return UTILS.DeepCopy( _DATABASE.Templates.Groups[self.GroupName].Template ) end --- Return the mission route of the group. @@ -2209,7 +2209,7 @@ end function GROUP:GetTaskRoute() self:F2( self.GroupName ) - return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points ) + return UTILS.DeepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points ) end --- Return the route of a group by using the global _DATABASE object (an instance of @{Core.Database#DATABASE}). @@ -2245,7 +2245,7 @@ function GROUP:CopyRoute( Begin, End, Randomize, Radius ) for TPointID = Begin + 1, #Template.route.points - End do if Template.route.points[TPointID] then - Points[#Points+1] = routines.utils.deepCopy( Template.route.points[TPointID] ) + Points[#Points+1] = UTILS.DeepCopy( Template.route.points[TPointID] ) if Randomize then if not Radius then Radius = 500 diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index c116575a9..0d3c690ad 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -1,5 +1,4 @@ Utilities/Enums.lua -Utilities/Routines.lua Utilities/Utils.lua Utilities/Enums.lua Utilities/Profiler.lua From 2afde85ef05d9daf7e4e09e53a6e223e03bcedc4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Sep 2023 18:39:07 +0200 Subject: [PATCH 341/603] #Remove routines --- Moose Development/Moose/Core/Base.lua | 12 ++++++------ Moose Development/Moose/Utilities/Utils.lua | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 38b0cbc1d..868e54c00 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -1167,7 +1167,7 @@ function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) if DebugInfoFrom then LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, UTILS.OneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) ) end end end @@ -1241,7 +1241,7 @@ function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) if DebugInfoFrom then LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s", LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, UTILS.OneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s", LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, UTILS.BasicSerialize( Arguments ) ) ) end end end @@ -1311,9 +1311,9 @@ function BASE:E( Arguments ) LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, UTILS.OneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) ) else - env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, UTILS.OneLineSerialize( Arguments ) ) ) + env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, UTILS.BasicSerialize( Arguments ) ) ) end end @@ -1338,9 +1338,9 @@ function BASE:I( Arguments ) LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.OneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) ) else - env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.OneLineSerialize( Arguments ) ) ) + env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.BasicSerialize( Arguments ) ) ) end end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 401fb3c8b..2e37f9ad0 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -426,10 +426,12 @@ UTILS.BasicSerialize = function(s) if s == nil then return "\"\"" else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then + if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'userdata') ) then return tostring(s) + elseif type(s) == "table" then + return UTILS._OneLineSerialize(s) elseif type(s) == 'string' then - s = string.format('%q', s) + s = string.format('(%s)', s) return s end end From 52764cc900122b0b985937d314b4e31ac59033dc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Sep 2023 13:09:46 +0200 Subject: [PATCH 342/603] #AICSAR * changes from OPSTRANSPORT --- Moose Development/Moose/Functional/AICSAR.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index b71050778..5f158932f 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -22,7 +22,7 @@ -- === -- -- ### Author: **Applevangelist** --- Last Update July 2023 +-- Last Update Sept 2023 -- -- === -- @module Functional.AICSAR @@ -191,7 +191,7 @@ -- @field #AICSAR AICSAR = { ClassName = "AICSAR", - version = "0.1.15", + version = "0.1.16", lid = "", coalition = coalition.side.BLUE, template = "", @@ -889,7 +889,7 @@ function AICSAR:_InitMission(Pilot,Index) -- Cargo transport assignment. local opstransport=OPSTRANSPORT:New(Pilot, pickupzone, self.farpzone) - + --opstransport:SetVerbosity(3) local helo = self:_GetFlight() -- inject reservation @@ -915,8 +915,9 @@ function AICSAR:_InitMission(Pilot,Index) self:__PilotUnloaded(2,Helo,OpsGroup) end - function helo:OnAfterLoadingDone(From,Event,To) - AICPickedUp(helo,helo:GetCargoGroups(),Index) + function helo:OnAfterLoading(From,Event,To) + AICPickedUp(helo,helo:GetCargoGroups(),Index) + helo:__LoadingDone(5) end function helo:OnAfterDead(From,Event,To) @@ -925,6 +926,7 @@ function AICSAR:_InitMission(Pilot,Index) function helo:OnAfterUnloaded(From,Event,To,OpsGroupCargo) AICHeloUnloaded(helo,OpsGroupCargo) + helo:__UnloadingDone(5) end self.helos[Index] = helo From 88741d943e394375000c56010c352c26cad2f5da Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Sep 2023 15:41:16 +0200 Subject: [PATCH 343/603] docu --- Moose Development/Moose/Core/Zone_Detection.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Core/Zone_Detection.lua b/Moose Development/Moose/Core/Zone_Detection.lua index b7f453952..385ac5247 100644 --- a/Moose Development/Moose/Core/Zone_Detection.lua +++ b/Moose Development/Moose/Core/Zone_Detection.lua @@ -2,6 +2,7 @@ -- @module Core.Zone_Detection -- @image MOOSE.JPG +--- -- @type ZONE_DETECTION -- @field DCS#Vec2 Vec2 The current location of the zone. -- @field DCS#Distance Radius The radius of the zone. From 3bbfaad77bc57bea0acacce21c9d224956e76e13 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Sep 2023 15:46:32 +0200 Subject: [PATCH 344/603] Cleanup --- Moose Development/Moose/Modules_local.lua | 2 -- Moose Setup/Moose.files | 2 -- 2 files changed, 4 deletions(-) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 73d9e9b05..20cfd095e 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -1,7 +1,6 @@ __Moose.Include( 'Utilities\\Enums.lua' ) __Moose.Include( 'Utilities\\Utils.lua' ) __Moose.Include( 'Utilities\\Profiler.lua' ) -__Moose.Include( 'Utilities\\Templates.lua' ) __Moose.Include( 'Utilities\\STTS.lua' ) __Moose.Include( 'Utilities\\FiFo.lua' ) __Moose.Include( 'Utilities\\Socket.lua' ) @@ -21,7 +20,6 @@ __Moose.Include( 'Core\\Database.lua' ) __Moose.Include( 'Core\\Set.lua' ) __Moose.Include( 'Core\\Point.lua' ) __Moose.Include( 'Core\\Pathline.lua' ) -__Moose.Include( 'Core\\Velocity.lua' ) __Moose.Include( 'Core\\Message.lua' ) __Moose.Include( 'Core\\Fsm.lua' ) __Moose.Include( 'Core\\Spawn.lua' ) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 0d3c690ad..615619358 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -16,11 +16,9 @@ Core/Event.lua Core/Settings.lua Core/Menu.lua Core/Zone.lua -Core/Zone_Detection.lua Core/Database.lua Core/Set.lua Core/Point.lua -Core/Velocity.lua Core/Message.lua Core/Fsm.lua Core/Spawn.lua From ae7f27ed6f19293896e557d1498a541644ba2c13 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Sep 2023 15:58:35 +0200 Subject: [PATCH 345/603] Cleanup --- Moose Development/Moose/Modules_local.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 20cfd095e..7fc3cfa52 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -15,7 +15,7 @@ __Moose.Include( 'Core\\Event.lua' ) __Moose.Include( 'Core\\Settings.lua' ) __Moose.Include( 'Core\\Menu.lua' ) __Moose.Include( 'Core\\Zone.lua' ) -__Moose.Include( 'Core\\Zone_Detection.lua' ) +__Moose.Include( 'Core\\Velocity.lua' ) __Moose.Include( 'Core\\Database.lua' ) __Moose.Include( 'Core\\Set.lua' ) __Moose.Include( 'Core\\Point.lua' ) From cf34e7f165ae71e095860ca7747fb464bfd30060 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 13 Sep 2023 15:31:07 +0200 Subject: [PATCH 346/603] docu --- Moose Development/Moose/Core/Zone.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 52f4675f0..94f1099d4 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2458,6 +2458,7 @@ function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, C return self end +--- -- @type ZONE_POLYGON -- @extends #ZONE_POLYGON_BASE From 74f87b3f2d7ab2cef86a97ef01ef2747d05e4620 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 13 Sep 2023 16:02:38 +0200 Subject: [PATCH 347/603] Intellisense --- Moose Development/Moose/Core/Base.lua | 1 + Moose Development/Moose/Core/Database.lua | 1 + Moose Development/Moose/Core/Event.lua | 1 + Moose Development/Moose/Core/Report.lua | 1 + Moose Development/Moose/Core/Settings.lua | 3 +++ Moose Development/Moose/Core/Spawn.lua | 1 + Moose Development/Moose/Core/SpawnStatic.lua | 1 + Moose Development/Moose/Core/Zone.lua | 6 +++++- Moose Development/Moose/Functional/ATC_Ground.lua | 8 ++++++-- Moose Development/Moose/Functional/CleanUp.lua | 2 ++ Moose Development/Moose/Tasking/CommandCenter.lua | 1 + Moose Development/Moose/Tasking/Mission.lua | 1 + Moose Development/Moose/Tasking/Task.lua | 1 + Moose Development/Moose/Tasking/TaskInfo.lua | 2 ++ Moose Development/Moose/Wrapper/Identifiable.lua | 1 + Moose Development/Moose/Wrapper/Object.lua | 2 +- Moose Development/Moose/Wrapper/Static.lua | 1 + 17 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 868e54c00..ce3675189 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -34,6 +34,7 @@ local _TraceClassMethod = {} local _ClassID = 0 +--- -- @type BASE -- @field ClassName The name of the class. -- @field ClassID The ID number of the class. diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 280682d80..3409c799f 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -32,6 +32,7 @@ -- @image Core_Database.JPG +--- -- @type DATABASE -- @field #string ClassName Name of the class. -- @field #table Templates Templates: Units, Groups, Statics, ClientsByName, ClientsByID. diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index e88bbc892..25fe050aa 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -173,6 +173,7 @@ -- @image Core_Event.JPG +--- -- @type EVENT -- @field #EVENT.Events Events -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Core/Report.lua b/Moose Development/Moose/Core/Report.lua index 7b592f42d..8d55f05d3 100644 --- a/Moose Development/Moose/Core/Report.lua +++ b/Moose Development/Moose/Core/Report.lua @@ -15,6 +15,7 @@ -- @module Core.Report -- @image Core_Report.JPG +--- -- @type REPORT -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 73cbd2a98..9b72dfd51 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -29,6 +29,8 @@ -- @module Core.Settings -- @image Core_Settings.JPG + +--- -- @type SETTINGS -- @extends Core.Base#BASE @@ -218,6 +220,7 @@ SETTINGS = { SETTINGS.__Enum = {} +--- -- @type SETTINGS.__Enum.Era -- @field #number WWII -- @field #number Korea diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index db5f915a1..f74302e35 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -286,6 +286,7 @@ SPAWN.Takeoff = { Cold = 4, } +--- -- @type SPAWN.SpawnZoneTable -- @list SpawnZone diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 2c38d389e..05ebdb40b 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -34,6 +34,7 @@ -- @module Core.SpawnStatic -- @image Core_Spawnstatic.JPG +--- -- @type SPAWNSTATIC -- @field #string SpawnTemplatePrefix Name of the template group. -- @field #number CountryID Country ID. diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 94f1099d4..5ded3fe7d 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -53,6 +53,7 @@ -- @module Core.Zone -- @image Core_Zones.JPG +--- -- @type ZONE_BASE -- @field #string ZoneName Name of the zone. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. @@ -1496,6 +1497,7 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma end +--- -- @type ZONE -- @extends #ZONE_RADIUS @@ -1580,7 +1582,7 @@ function ZONE:FindByName( ZoneName ) end - +--- -- @type ZONE_UNIT -- @field Wrapper.Unit#UNIT ZoneUNIT -- @extends Core.Zone#ZONE_RADIUS @@ -1723,6 +1725,7 @@ function ZONE_UNIT:GetVec3( Height ) return Vec3 end +--- -- @type ZONE_GROUP -- @extends #ZONE_RADIUS @@ -1809,6 +1812,7 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer ) end +--- -- @type ZONE_POLYGON_BASE -- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}. -- @extends #ZONE_BASE diff --git a/Moose Development/Moose/Functional/ATC_Ground.lua b/Moose Development/Moose/Functional/ATC_Ground.lua index 74cdae7cd..ca7e2ae45 100644 --- a/Moose Development/Moose/Functional/ATC_Ground.lua +++ b/Moose Development/Moose/Functional/ATC_Ground.lua @@ -26,6 +26,7 @@ -- @module Functional.ATC_Ground -- @image Air_Traffic_Control_Ground_Operations.JPG +--- -- @type ATC_GROUND -- @field Core.Set#SET_CLIENT SetClient -- @extends Core.Base#BASE @@ -39,6 +40,7 @@ ATC_GROUND = { AirbaseNames = nil, } +--- -- @type ATC_GROUND.AirbaseNames -- @list <#string> @@ -846,6 +848,7 @@ function ATC_GROUND_UNIVERSAL:Start( RepeatScanSeconds ) return self end +--- -- @type ATC_GROUND_CAUCASUS -- @extends #ATC_GROUND @@ -985,7 +988,7 @@ function ATC_GROUND_CAUCASUS:Start( RepeatScanSeconds ) end - +--- -- @type ATC_GROUND_NEVADA -- @extends #ATC_GROUND @@ -1123,7 +1126,7 @@ function ATC_GROUND_NEVADA:Start( RepeatScanSeconds ) self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) end - +--- -- @type ATC_GROUND_NORMANDY -- @extends #ATC_GROUND @@ -1280,6 +1283,7 @@ function ATC_GROUND_NORMANDY:Start( RepeatScanSeconds ) self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) end +--- -- @type ATC_GROUND_PERSIANGULF -- @extends #ATC_GROUND diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index 4ba1d3e4c..ddff4e4a7 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -52,10 +52,12 @@ -- @module Functional.CleanUp -- @image CleanUp_Airbases.JPG +--- -- @type CLEANUP_AIRBASE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) -- @field #map<#string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases. -- @extends Core.Base#BASE +--- -- @type CLEANUP_AIRBASE -- @extends #CLEANUP_AIRBASE.__ diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 271879aa1..fbd5ded95 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -181,6 +181,7 @@ COMMANDCENTER = { } +--- -- @type COMMANDCENTER.AutoAssignMethods COMMANDCENTER.AutoAssignMethods = { ["Random"] = 1, diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 317b874dd..e839d5d01 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -19,6 +19,7 @@ -- @module Tasking.Mission -- @image Task_Mission.JPG +--- -- @type MISSION -- @field #MISSION.Clients _Clients -- @field Core.Menu#MENU_COALITION MissionMenu diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 9149f0528..7ee93f900 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -220,6 +220,7 @@ -- @module Tasking.Task -- @image MOOSE.JPG +--- -- @type TASK -- @field Core.Scheduler#SCHEDULER TaskScheduler -- @field Tasking.Mission#MISSION Mission diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 22baf4fc4..aeb1e1f86 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -11,6 +11,7 @@ -- @module Tasking.TaskInfo -- @image MOOSE.JPG +--- -- @type TASKINFO -- @extends Core.Base#BASE @@ -29,6 +30,7 @@ TASKINFO = { ClassName = "TASKINFO", } +--- -- @type TASKINFO.Detail #string A string that flags to document which level of detail needs to be shown in the report. -- -- - "M" for Markings on the Map (F10). diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 8786807d5..1dfdda3bf 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -11,6 +11,7 @@ -- @module Wrapper.Identifiable -- @image MOOSE.JPG +--- -- @type IDENTIFIABLE -- @extends Wrapper.Object#OBJECT -- @field #string IdentifiableName The name of the identifiable. diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index 382e13f5e..6bfd36656 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -11,7 +11,7 @@ -- @module Wrapper.Object -- @image MOOSE.JPG - +--- -- @type OBJECT -- @extends Core.Base#BASE -- @field #string ObjectName The name of the Object. diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 5143c698c..e9ca045e8 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -12,6 +12,7 @@ -- @image Wrapper_Static.JPG +--- -- @type STATIC -- @extends Wrapper.Positionable#POSITIONABLE From 97809cdb6568f440f1a0986217e15d210ab2e258 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 14 Sep 2023 12:35:43 +0200 Subject: [PATCH 348/603] #PLAYERTASKCONTROLLER * Fix a problem that sometimes the object event is called prior to the SET_CLIENT event for players joining, leading to false negatives on specifically filtered SET_CLIENT objects --- Moose Development/Moose/Core/Set.lua | 8 +++++--- Moose Development/Moose/Ops/PlayerTask.lua | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 60236095d..056ce9e58 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -860,8 +860,10 @@ do -- SET_BASE self:F3( Object ) local outcome = false local name = Object:GetName() + --self:I("SET_BASE: Objectname = "..name) self:ForEach( function(object) + --self:I("SET_BASE: In set objectname = "..object:GetName()) if object:GetName() == name then outcome = true end @@ -4330,7 +4332,6 @@ do -- SET_CLIENT function SET_CLIENT:FilterStart() if _DATABASE then - self:_FilterStart() self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) @@ -4339,6 +4340,7 @@ do -- SET_CLIENT local timing = self.ZoneTimerInterval or 30 self.ZoneTimer:Start(timing,timing) end + self:_FilterStart() end return self @@ -4574,9 +4576,9 @@ do -- SET_CLIENT if self.Filter.Callsigns then local MClientCallsigns = false local callsign = MClient:GetCallsign() - --self:T(callsign) + --self:I(callsign) for _,_Callsign in pairs(self.Filter.Callsigns) do - if callsign and string.find(callsign,_Callsign) then + if callsign and string.find(callsign,_Callsign,1,true) then MClientCallsigns = true end end diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 190bc8976..15c2581ff 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.19" +PLAYERTASK.version="0.1.20" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -2195,7 +2195,8 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) end elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then if EventData.IniPlayerName and EventData.IniGroup then - if self.IsClientSet and self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then + --if self.IsClientSet and self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then + if self.IsClientSet and (not self.ClientSet:IsIncludeObject(CLIENT:FindByName(EventData.IniUnitName))) then self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName) return self end @@ -4063,7 +4064,8 @@ function PLAYERTASKCONTROLLER:onafterStart(From, Event, To) self:HandleEvent(EVENTS.Ejection, self._EventHandler) self:HandleEvent(EVENTS.Crash, self._EventHandler) self:HandleEvent(EVENTS.PilotDead, self._EventHandler) - self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) + self:SetEventPriority(5) return self end From dd41cd4b4f9a96e958aa983a226a231f3eafc5eb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 15 Sep 2023 09:11:20 +0200 Subject: [PATCH 349/603] #Startup * Re-instate suppression of error box. --- Moose Development/Moose/Utilities/Enums.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index fc4e1d955..e37b1c405 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -26,6 +26,9 @@ -- @field #ENUMS ENUMS = {} +--- Suppress the error box +env.setErrorMessageBoxEnabled( false ) + --- Rules of Engagement. -- @type ENUMS.ROE -- @field #number WeaponFree [AIR] AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target. From c173fe0cb8f12b6b9e4df6576b4780d60efbdb9c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 17 Sep 2023 16:52:45 +0200 Subject: [PATCH 350/603] Quick check on airbase being a string --- Moose Development/Moose/Core/Spawn.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index f74302e35..4295ed097 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -2178,14 +2178,18 @@ end -- @param #table Spots Table of parking spot IDs. Note that these in general are different from the numbering in the mission editor! -- @param #SPAWN.Takeoff Takeoff (Optional) Takeoff type, i.e. either SPAWN.Takeoff.Cold or SPAWN.Takeoff.Hot. Default is Hot. -- @return Wrapper.Group#GROUP The group that was spawned or nil when nothing was spawned. -function SPAWN:SpawnAtParkingSpot( Airbase, Spots, Takeoff ) -- R2.5 +function SPAWN:SpawnAtParkingSpot( Airbase, Spots, Takeoff ) self:F( { Airbase = Airbase, Spots = Spots, Takeoff = Takeoff } ) - + -- Ensure that Spots parameter is a table. if type( Spots ) ~= "table" then Spots = { Spots } end - + + if type(Airbase) == "string" then + Airbase = AIRBASE:FindByName(Airbase) + end + -- Get template group. local group = GROUP:FindByName( self.SpawnTemplatePrefix ) From 5886d154aec90e299554206fa9856980a3498a74 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 17 Sep 2023 17:13:51 +0200 Subject: [PATCH 351/603] #SPAWN * Added option for Modex pre- and postfix --- Moose Development/Moose/Core/Spawn.lua | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 4295ed097..adcafc31f 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -328,6 +328,8 @@ function SPAWN:New( SpawnTemplatePrefix ) self.SpawnInitModu = nil -- No special modulation. self.SpawnInitRadio = nil -- No radio comms setting. self.SpawnInitModex = nil + self.SpawnInitModexPrefix = nil + self.SpawnInitModexPostfix = nil self.SpawnInitAirbase = nil self.TweakedTemplate = false -- Check if the user is using self made template. @@ -382,6 +384,8 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) self.SpawnInitModu = nil -- No special modulation. self.SpawnInitRadio = nil -- No radio communication setting. self.SpawnInitModex = nil + self.SpawnInitModexPrefix = nil + self.SpawnInitModexPostfix = nil self.SpawnInitAirbase = nil self.TweakedTemplate = false -- Check if the user is using self made template. @@ -541,6 +545,8 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr self.SpawnInitModu = nil -- No special modulation. self.SpawnInitRadio = nil -- No radio communication setting. self.SpawnInitModex = nil + self.SpawnInitModexPrefix = nil + self.SpawnInitModexPostfix = nil self.SpawnInitAirbase = nil self.TweakedTemplate = true -- Check if the user is using self made template. self.MooseNameing = true @@ -812,12 +818,17 @@ end --- Sets the modex of the first unit of the group. If more units are in the group, the number is increased by one with every unit. -- @param #SPAWN self -- @param #number modex Modex of the first unit. +-- @param #string prefix (optional) String to prefix to modex, e.g. for French AdA Modex, eg. -L-102 then "-L-" would be the prefix. +-- @param #string postfix (optional) String to postfix to modex, example tbd. -- @return #SPAWN self -function SPAWN:InitModex( modex ) +function SPAWN:InitModex( modex, prefix, postfix ) if modex then self.SpawnInitModex = tonumber( modex ) end + + self.SpawnInitModexPrefix = prefix + self.SpawnInitModexPostfix = postfix return self end @@ -1571,7 +1582,10 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth ) -- Set tail number. if self.SpawnInitModex then for UnitID = 1, #SpawnTemplate.units do - SpawnTemplate.units[UnitID].onboard_num = string.format( "%03d", self.SpawnInitModex + (UnitID - 1) ) + local modexnumber = string.format( "%03d", self.SpawnInitModex + (UnitID - 1) ) + if self.SpawnInitModexPrefix then modexnumber = self.SpawnInitModexPrefix..modexnumber end + if self.SpawnInitModexPostfix then modexnumber = modexnumber..self.SpawnInitModexPostfix end + SpawnTemplate.units[UnitID].onboard_num = modexnumber end end From 75529958f62b444da66e90191c5f591ae4b72a4d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 19 Sep 2023 11:09:55 +0200 Subject: [PATCH 352/603] #SEAD * Better calculation of switch-on again time --- Moose Development/Moose/Functional/Sead.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 0ed37dadb..b80246523 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -1,4 +1,4 @@ ---- **Functional** - Make SAM sites execute evasive and defensive behaviour when being fired upon. +--- **Functional** - Make SAM sites evasive and execute defensive behaviour when being fired upon. -- -- === -- @@ -19,7 +19,7 @@ -- -- ### Authors: **FlightControl**, **applevangelist** -- --- Last Update: Feb 2022 +-- Last Update: September 2023 -- -- === -- @@ -143,7 +143,7 @@ function SEAD:New( SEADGroupPrefixes, Padding ) self:AddTransition("*", "ManageEvasion", "*") self:AddTransition("*", "CalculateHitZone", "*") - self:I("*** SEAD - Started Version 0.4.3") + self:I("*** SEAD - Started Version 0.4.4") return self end @@ -203,7 +203,7 @@ function SEAD:SwitchEmissions(Switch) return self end ---- Add an object to call back when going evasive. +--- Set an object to call back when going evasive. -- @param #SEAD self -- @param #table Object The object to call. Needs to have object functions as follows: -- `:SeadSuppressionPlanned(Group, Name, SuppressionStartTime, SuppressionEndTime)` @@ -405,7 +405,7 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP local function SuppressionStop(args) self:T(string.format("*** SEAD - %s Radar On",args[2])) local grp = args[1] -- Wrapper.Group#GROUP - local name = args[2] -- #string Group Nam + local name = args[2] -- #string Group Name if self.UseEmissionsOnOff then grp:EnableEmission(true) end @@ -424,7 +424,7 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP if _tti > 600 then delay = _tti - 90 end -- shot from afar, 600 is default shorad ontime local SuppressionStartTime = timer.getTime() + delay - local SuppressionEndTime = timer.getTime() + _tti + self.Padding + local SuppressionEndTime = timer.getTime() + delay + _tti + self.Padding + delay local _targetgroupname = _targetgroup:GetName() if not self.SuppressedGroups[_targetgroupname] then self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay)) From 03e1b28c97cae9179c4e74cb4c2de06e06c96a41 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 19 Sep 2023 11:30:27 +0200 Subject: [PATCH 353/603] Typo --- Moose Development/Moose/Core/Database.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 8ab8ed17d..836c840ed 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1368,7 +1368,7 @@ function DATABASE:_EventOnBirth( Event ) if PlayerName then -- Debug info. - self:I(string.format("Player '%s' joint unit '%s' of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName))) + self:I(string.format("Player '%s' joined unit '%s' of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName))) -- Add client in case it does not exist already. if not client then From 3cc6f5337462f0607ed3fa08ae124113ac82f5dc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 19 Sep 2023 12:10:04 +0200 Subject: [PATCH 354/603] Serialize --- Moose Development/Moose/Utilities/Utils.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 7e21a576a..367e85d66 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -406,8 +406,8 @@ function UTILS._OneLineSerialize(tbl) elseif type(val) == 'nil' then -- won't ever happen, right? tbl_str[#tbl_str + 1] = 'nil, ' elseif type(val) == 'table' then - tbl_str[#tbl_str + 1] = UTILS._OneLineSerialize(val) - tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it + --tbl_str[#tbl_str + 1] = UTILS.TableShow(tbl,loc,indent,tableshow_tbls) + --tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it else --log:warn('Unable to serialize value type $1 at index $2', mist.utils.basicSerialize(type(val)), tostring(ind)) end From 1e966ee217c4aa371982654f3bc1e808d1107b03 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 19 Sep 2023 18:06:16 +0200 Subject: [PATCH 355/603] #ATIS * Added SRS localization --- Moose Development/Moose/Ops/ATIS.lua | 591 +++++++++++++++++++++------ 1 file changed, 464 insertions(+), 127 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 74a28d9f1..1c6f4e6bc 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -9,7 +9,7 @@ -- * Visibility -- * Cloud coverage, base and ceiling -- * Temperature --- * Dew point (approximate as there is no relative humidity in DCS yet) +-- * Dew point (approximate as there is no relative humidity in DCS yet) -- * Pressure QNH/QFE -- * Weather phenomena: rain, thunderstorm, fog, dust -- * Active runway based on wind direction @@ -97,6 +97,8 @@ -- @field #boolean TransmitOnlyWithPlayers For SRS - If true, only transmit if there are alive Players. -- @field #string SRSText Text of the complete SRS message (if done at least once, else nil) -- @field #boolean ATISforFARPs Will be set to true if the base given is a FARP/Helipad +-- @field Core.TextAndSound#TEXTANDSOUND gettext Gettext for localization +-- @field #string locale Current locale -- @extends Core.Fsm#FSM --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -130,6 +132,8 @@ -- -- ## Subtitles -- +-- **Note** Subtitles are not displayed when using SRS. The DCS mechanic to show subtitles (top left screen), is via the function call that plays a sound file from a UNIT, hence this doesn't work here. +-- -- Currently, DCS allows for displaying subtitles of radio transmissions only from airborne units, *i.e.* airplanes and helicopters. Therefore, if you want to have subtitles, it is necessary to place an -- additional aircraft on the ATIS airport and set it to uncontrolled. This unit can then function as a radio relay to transmit messages with subtitles. These subtitles will only be displayed, if the -- player has tuned in the correct ATIS frequency. @@ -309,14 +313,86 @@ -- -- atis=ATIS:New("Batumi", 305, radio.modulation.AM) -- atis:SetSRS("D:\\DCS\\_SRS\\", "male", "en-US") --- atis:Start() +-- atis:Start() -- --- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Not that backslashes need to be escaped or simply use slashes (as in linux). +-- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Note that backslashes need to be escaped or simply use slashes (as in linux). -- +-- ### SRS Localization +-- +-- You can localize the SRS output, all you need is to provide a table of translations and set the `locale` of your instance. You need to provide the translations in your script **before you instantiate your ATIS**. +-- The German localization (already provided in the code) e.g. looks like follows: +-- +-- ATIS.Messages.DE = +-- { +-- HOURS = "Uhr", +-- TIME = "Zeit", +-- NOCLOUDINFO = "Informationen über Wolken nicht verfuegbar", +-- OVERCAST = "Geschlossene Wolkendecke", +-- BROKEN = "Stark bewoelkt", +-- SCATTERED = "Bewoelkt", +-- FEWCLOUDS = "Leicht bewoelkt", +-- NOCLOUDS = "Klar", +-- AIRPORT = "Flughafen", +-- INFORMATION ="Information", +-- SUNRISEAT = "Sonnenaufgang um %s lokaler Zeit", +-- SUNSETAT = "Sonnenuntergang um %s lokaler Zeit", +-- WINDFROMMS = "Wind aus %s mit %s m/s", +-- WINDFROMKNOTS = "Wind aus %s mit %s Knoten", +-- GUSTING = "boeig", +-- VISIKM = "Sichtweite %s km", +-- VISISM = "Sichtweite %s Meilen", +-- RAIN = "Regen", +-- TSTORM = "Gewitter", +-- SNOW = "Schnee", +-- SSTROM = "Schneesturm", +-- FOG = "Nebel", +-- DUST = "Staub", +-- PHENOMENA = "Wetter Phaenomene", +-- CLOUDBASEM = "Wolkendecke von %s bis %s Meter", +-- CLOUDBASEFT = "Wolkendecke von %s bis %s Fuß", +-- TEMPERATURE = "Temperatur", +-- DEWPOINT = "Taupunkt", +-- ALTIMETER = "Hoehenmesser", +-- ACTIVERUN = "Aktive Startbahn", +-- LEFT = "Links", +-- RIGHT = "Rechts", +-- RWYLENGTH = "Startbahn", +-- METERS = "Meter", +-- FEET = "Fuß", +-- ELEVATION = "Hoehe", +-- TOWERFREQ = "Kontrollturm Frequenz", +-- ILSFREQ = "ILS Frequenz", +-- OUTERNDB = "Aeussere NDB Frequenz", +-- INNERNDB = "Innere NDB Frequenz", +-- VORFREQ = "VOR Frequenz", +-- VORFREQTTS = "V O R Frequenz", +-- TACANCH = "TACAN Kanal %d Xaver", +-- RSBNCH = "RSBN Kanal", +-- PRMGCH = "PRMG Kanal", +-- ADVISE = "Hinweis bei Erstkontakt, Sie haben Informationen", +-- STATUTE = "englische Meilen", +-- DEGREES = "Grad Celsius", +-- FAHRENHEIT = "Grad Fahrenheit", +-- INCHHG = "Inches H G", +-- MMHG = "Millimeter H G", +-- HECTO = "Hektopascal", +-- METERSPER = "Meter pro Sekunde", +-- TACAN = "Tackan", +-- FARP = "Farp", +-- DELIMITER = "Komma", -- decimal delimiter +-- } +-- +-- Then set up your ATIS and set the locale: +-- +-- atis=ATIS:New("Batumi", 305, radio.modulation.AM) +-- atis:SetSRS("D:\\DCS\\_SRS\\", "female", "de_DE") +-- atis:SetLocale("de") +-- atis:Start() +-- -- ## FARPS --- +-- -- ATIS is working with FARPS, but this requires the usage of SRS. The airbase name for the `New()-method` is the UNIT name of the FARP: --- +-- -- atis = ATIS:New("FARP Gold",119,radio.modulation.AM) -- atis:SetMetricUnits() -- atis:SetTransmitOnlyWithPlayers(true) @@ -367,7 +443,8 @@ ATIS = { relHumidity = nil, ReportmBar = false, TransmitOnlyWithPlayers = false, - ATISforFARPs = false, + ATISforFARPs = false, + locale = "en", } --- NATO alphabet. @@ -412,6 +489,7 @@ ATIS.Alphabet = { -- @field #number TheChannel -10° (West). -- @field #number Syria +5° (East). -- @field #number MarianaIslands +2° (East). +-- @field #number SinaiMao +5° (East). ATIS.RunwayM2T = { Caucasus = 0, Nevada = 12, @@ -421,7 +499,7 @@ ATIS.RunwayM2T = { Syria = 5, MarianaIslands = 2, Falklands = 12, - Sinai = 5, + SinaiMap = 5, } --- Whether ICAO phraseology is used for ATIS broadcasts. @@ -434,7 +512,7 @@ ATIS.RunwayM2T = { -- @field #boolean Syria true. -- @field #boolean MarianaIslands true. -- @field #boolean Falklands true. --- @field #boolean Sinai true. +-- @field #boolean SinaiMap true. ATIS.ICAOPhraseology = { Caucasus = true, Nevada = false, @@ -444,7 +522,7 @@ ATIS.ICAOPhraseology = { Syria = true, MarianaIslands = true, Falklands = true, - Sinai = true, + SinaiMap = true, } --- Nav point data. @@ -610,13 +688,140 @@ ATIS.Sound = { Zulu = { filename = "Zulu.ogg", duration = 0.62 }, } +--- +-- @field Messages +ATIS.Messages = { + EN = + { + HOURS = "hours", + TIME = "hours", + NOCLOUDINFO = "Cloud coverage information not available", + OVERCAST = "Overcast", + BROKEN = "Broken clouds", + SCATTERED = "Scattered clouds", + FEWCLOUDS = "Few clouds", + NOCLOUDS = "No clouds", + AIRPORT = "Airport", + INFORMATION ="Information", + SUNRISEAT = "Sunrise at %s local time", + SUNSETAT = "Sunset at %s local time", + WINDFROMMS = "Wind from %s at %s m/s", + WINDFROMKNOTS = "Wind from %s at %s knots", + GUSTING = "gusting", + VISIKM = "Visibility %s km", + VISISM = "Visibility %s SM", + RAIN = "rain", + TSTORM = "thunderstorm", + SNOW = "snow", + SSTROM = "snowstorm", + FOG = "fog", + DUST = "dust", + PHENOMENA = "Weather phenomena", + CLOUDBASEM = "Cloud base %s, ceiling %s meters", + CLOUDBASEFT = "Cloud base %s, ceiling %s feet", + TEMPERATURE = "Temperature", + DEWPOINT = "Dew point", + ALTIMETER = "Altimeter", + ACTIVERUN = "Active runway", + LEFT = "Left", + RIGHT = "Right", + RWYLENGTH = "Runway length", + METERS = "meters", + FEET = "feet", + ELEVATION = "Elevation", + TOWERFREQ = "Tower frequency", + ILSFREQ = "ILS frequency", + OUTERNDB = "Outer NDB frequency", + INNERNDB = "Inner NDB frequency", + VORFREQ = "VOR frequency", + VORFREQTTS = "V O R frequency", + TACANCH = "TACAN channel %dX Ray", + RSBNCH = "RSBN channel", + PRMGCH = "PRMG channel", + ADVISE = "Advise on initial contact, you have information", + STATUTE = "statute miles", + DEGREES = "degrees Celsius", + FAHRENHEIT = "degrees Fahrenheit", + INCHHG = "inches of Mercury", + MMHG = "millimeters of Mercury", + HECTO = "hectopascals", + METERSPER = "meters per second", + TACAN = "tackan", + FARP = "farp", + DELIMITER = "point", -- decimal delimiter + }, + DE = + { + HOURS = "Uhr", + TIME = "Zeit", + NOCLOUDINFO = "Informationen über Wolken nicht verfuegbar", + OVERCAST = "Geschlossene Wolkendecke", + BROKEN = "Stark bewoelkt", + SCATTERED = "Bewoelkt", + FEWCLOUDS = "Leicht bewoelkt", + NOCLOUDS = "Klar", + AIRPORT = "Flughafen", + INFORMATION ="Information", + SUNRISEAT = "Sonnenaufgang um %s lokaler Zeit", + SUNSETAT = "Sonnenuntergang um %s lokaler Zeit", + WINDFROMMS = "Wind aus %s mit %s m/s", + WINDFROMKNOTS = "Wind aus %s mit %s Knoten", + GUSTING = "boeig", + VISIKM = "Sichtweite %s km", + VISISM = "Sichtweite %s Meilen", + RAIN = "Regen", + TSTORM = "Gewitter", + SNOW = "Schnee", + SSTROM = "Schneesturm", + FOG = "Nebel", + DUST = "Staub", + PHENOMENA = "Wetter Phaenomene", + CLOUDBASEM = "Wolkendecke von %s bis %s Meter", + CLOUDBASEFT = "Wolkendecke von %s bis %s Fuß", + TEMPERATURE = "Temperatur", + DEWPOINT = "Taupunkt", + ALTIMETER = "Hoehenmesser", + ACTIVERUN = "Aktive Startbahn", + LEFT = "Links", + RIGHT = "Rechts", + RWYLENGTH = "Startbahn", + METERS = "Meter", + FEET = "Fuß", + ELEVATION = "Hoehe", + TOWERFREQ = "Kontrollturm Frequenz", + ILSFREQ = "ILS Frequenz", + OUTERNDB = "Aeussere NDB Frequenz", + INNERNDB = "Innere NDB Frequenz", + VORFREQ = "VOR Frequenz", + VORFREQTTS = "V O R Frequenz", + TACANCH = "TACAN Kanal %d Xaver", + RSBNCH = "RSBN Kanal", + PRMGCH = "PRMG Kanal", + ADVISE = "Hinweis bei Erstkontakt, Sie haben Informationen", + STATUTE = "englische Meilen", + DEGREES = "Grad Celsius", + FAHRENHEIT = "Grad Fahrenheit", + INCHHG = "Inches H G", + MMHG = "Millimeter H G", + HECTO = "Hektopascal", + METERSPER = "Meter pro Sekunde", + TACAN = "Tackan", + FARP = "Farp", + DELIMITER = "Komma", -- decimal delimiter + } +} + +--- +-- @field locale +ATIS.locale = "en" + --- ATIS table containing all defined ATISes. -- @field #table _ATIS _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.9.16" +ATIS.version = "0.10.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -637,7 +842,7 @@ ATIS.version = "0.9.16" -- DONE: Set UTC correction. -- DONE: Set magnetic variation. -- DONE: New DCS 2.7 weather presets. --- DONE: whatever +-- DONE: Added TextAndSound localization ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -686,6 +891,7 @@ function ATIS:New(AirbaseName, Frequency, Modulation) self:SetRelativeHumidity() self:SetQueueUpdateTime() self:SetReportmBar(false) + self:_InitLocalization() -- Start State. self:SetStartState( "Stopped" ) @@ -774,6 +980,33 @@ end -- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- [Internal] Init localization +-- @param #ATIS self +-- @return #ATIS self +function ATIS:_InitLocalization() + self:T(self.lid.."_InitLocalization") + self.gettext = TEXTANDSOUND:New("AWACS","en") -- Core.TextAndSound#TEXTANDSOUND + self.locale = "en" + for locale,table in pairs(self.Messages) do + local Locale = string.lower(tostring(locale)) + self:T("**** Adding locale: "..Locale) + for ID,Text in pairs(table) do + self:T(string.format('Adding ID %s',tostring(ID))) + self.gettext:AddEntry(Locale,tostring(ID),Text) + end + end + return self +end + +--- Set locale for localized text-to-sound output via SRS, defaults to "en". +-- @param #ATIS self +-- @param #string locale Locale for localized text-to-sound output via SRS, defaults to "en". +-- @return #ATIS self +function ATIS:SetLocale(locale) + self.locale = string.lower(locale) + return self +end + --- Set sound files folder within miz file. -- @param #ATIS self -- @param #string path Path for sound files. Default "ATIS Soundfiles/". Mind the slash "/" at the end! @@ -801,7 +1034,7 @@ end -- @return #ATIS self function ATIS:SetTowerFrequencies( freqs ) if type( freqs ) == "table" then - -- nothing to do + -- nothing to do else freqs = { freqs } end @@ -829,9 +1062,9 @@ end function ATIS:SetActiveRunway( runway ) self.activerunway = tostring( runway ) local prefer = nil - if string.find(string.lower(runway),"l") then + if string.find(string.lower(runway),"l") then prefer = true - elseif string.find(string.lower(runway),"r") then + elseif string.find(string.lower(runway),"r") then prefer = false end self.airbase:SetActiveRunway(runway,prefer) @@ -920,7 +1153,7 @@ function ATIS:SetRunwayHeadingsMagnetic( headings ) -- First make sure, we have a table. if type( headings ) == "table" then - -- nothing to do + -- nothing to do else headings = { headings } end @@ -1004,9 +1237,9 @@ function ATIS:SetTemperatureFahrenheit() end --- Set relative humidity. This is used to approximately calculate the dew point. --- Note that the dew point is only an artificial information as DCS does not have an atmospheric model that includes humidity (yet). +-- Note that the dew point is only an artificial information as DCS does not have an atmospheric model that includes humidity (yet). -- @param #ATIS self --- @param #number Humidity Relative Humidity, i.e. a number between 0 and 100 %. Default is 50 %. +-- @param #number Humidity Relative Humidity, i.e. a number between 0 and 100 %. Default is 50 %. -- @return #ATIS self function ATIS:SetRelativeHumidity( Humidity ) self.relHumidity = Humidity or 50 @@ -1077,7 +1310,7 @@ end -- * 182° on the Persian Gulf map -- -- Likewise, to convert *true* into *magnetic* heading, one has to substract easterly and add westerly variation. --- +-- -- Or you make your life simple and just include the sign so you don't have to bother about East/West. -- -- @param #ATIS self @@ -1281,20 +1514,20 @@ end function ATIS:onafterStart( From, Event, To ) self:T({From, Event, To}) self:T("Airbase category is "..self.airbase:GetAirbaseCategory()) - + -- Check that this is an airdrome. if self.airbase:GetAirbaseCategory() == Airbase.Category.SHIP then self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT SHIPS.", self.airbasename ) ) return end - + -- Check that if is a Helipad. if self.airbase:GetAirbaseCategory() == Airbase.Category.HELIPAD then self:E( self.lid .. string.format( "EXPERIMENTAL: Starting ATIS for Helipad %s! SRS must be ON", self.airbasename ) ) self.ATISforFARPs = true self.useSRS = true end - + -- Info. self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) ) @@ -1362,7 +1595,7 @@ function ATIS:onafterStatus( From, Event, To ) text = text .. string.format( ", Relay unit=%s (alive=%s)", tostring( self.relayunitname ), relayunitstatus ) end self:T( self.lid .. text ) - + if not self:Is("Stopped") then self:__Status( 60 ) end @@ -1394,9 +1627,9 @@ function ATIS:onafterCheckQueue( From, Event, To ) end end - - -- Check back in 5 seconds. + + -- Check back in 5 seconds. self:__CheckQueue( math.abs( self.dTQueueCheck ) ) end end @@ -1507,11 +1740,11 @@ function ATIS:onafterBroadcast( From, Event, To ) -------------- --- Runway --- -------------- - - + + local runwayLanding, rwyLandingLeft local runwayTakeoff, rwyTakeoffLeft - + if self.airbase:GetAirbaseCategory() == Airbase.Category.HELIPAD then runwayLanding, rwyLandingLeft="PAD 01",false runwayTakeoff, rwyTakeoffLeft="PAD 02",false @@ -1519,7 +1752,7 @@ function ATIS:onafterBroadcast( From, Event, To ) runwayLanding, rwyLandingLeft=self:GetActiveRunway() runwayTakeoff, rwyTakeoffLeft=self:GetActiveRunway(true) end - + ------------ --- Time --- ------------ @@ -1540,8 +1773,9 @@ function ATIS:onafterBroadcast( From, Event, To ) local clock = UTILS.SecondsToClock( time ) local zulu = UTILS.Split( clock, ":" ) local ZULU = string.format( "%s%s", zulu[1], zulu[2] ) + local hours = self.gettext:GetEntry("TIME",self.locale) if self.useSRS then - ZULU = string.format( "%s hours", zulu[1] ) + ZULU = string.format( "%s %s", hours, zulu[1] ) end -- NATO time stamp. 0=Alfa, 1=Bravo, 2=Charlie, etc. @@ -1557,19 +1791,20 @@ function ATIS:onafterBroadcast( From, Event, To ) -------------------------- --- Sunrise and Sunset --- -------------------------- - + + local hours = self.gettext:GetEntry("HOURS",self.locale) local sunrise = coord:GetSunrise() sunrise = UTILS.Split( sunrise, ":" ) local SUNRISE = string.format( "%s%s", sunrise[1], sunrise[2] ) if self.useSRS then - SUNRISE = string.format( "%s %s hours", sunrise[1], sunrise[2] ) + SUNRISE = string.format( "%s %s %s", sunrise[1], sunrise[2], hours ) end local sunset = coord:GetSunset() sunset = UTILS.Split( sunset, ":" ) local SUNSET = string.format( "%s%s", sunset[1], sunset[2] ) if self.useSRS then - SUNSET = string.format( "%s %s hours", sunset[1], sunset[2] ) + SUNSET = string.format( "%s %s %s", sunset[1], sunset[2], hours ) end --------------------------------- @@ -1654,7 +1889,7 @@ function ATIS:onafterBroadcast( From, Event, To ) local cloudceil = clouds.base + clouds.thickness local clouddens = clouds.density - -- Cloud preset (DCS 2.7) + -- Cloud preset (DCS 2.7) local cloudspreset = clouds.preset or "Nothing" -- Precepitation: 0=None, 1=Rain, 2=Thunderstorm, 3=Snow, 4=Snowstorm. @@ -1795,32 +2030,37 @@ function ATIS:onafterBroadcast( From, Event, To ) -- No cloud info for dynamic weather. local CloudCover = {} -- #ATIS.Soundfile CloudCover = ATIS.Sound.CloudsNotAvailable - local CLOUDSsub = "Cloud coverage information not available" - + --local CLOUDSsub = "Cloud coverage information not available" + local CLOUDSsub = self.gettext:GetEntry("NOCLOUDINFO",self.locale) -- Only valid for static weather. if static then if clouddens >= 9 then -- Overcast 9,10 CloudCover = ATIS.Sound.CloudsOvercast - CLOUDSsub = "Overcast" + --CLOUDSsub = "Overcast" + CLOUDSsub = self.gettext:GetEntry("OVERCAST",self.locale) elseif clouddens >= 7 then -- Broken 7,8 CloudCover = ATIS.Sound.CloudsBroken - CLOUDSsub = "Broken clouds" + --CLOUDSsub = "Broken clouds" + CLOUDSsub = self.gettext:GetEntry("BROKEN",self.locale) elseif clouddens >= 4 then -- Scattered 4,5,6 CloudCover = ATIS.Sound.CloudsScattered - CLOUDSsub = "Scattered clouds" + --CLOUDSsub = "Scattered clouds" + CLOUDSsub = self.gettext:GetEntry("SCATTERED",self.locale) elseif clouddens >= 1 then -- Few 1,2,3 CloudCover = ATIS.Sound.CloudsFew - CLOUDSsub = "Few clouds" + --CLOUDSsub = "Few clouds" + CLOUDSsub = self.gettext:GetEntry("FEWCLOUDS",self.locale) else -- No clouds CLOUDBASE = nil CLOUDCEIL = nil CloudCover = ATIS.Sound.CloudsNo - CLOUDSsub = "No clouds" + --CLOUDSsub = "No clouds" + CLOUDSsub = self.gettext:GetEntry("NOCLOUDS",self.locale) end end @@ -1833,11 +2073,12 @@ function ATIS:onafterBroadcast( From, Event, To ) -- Airbase name subtitle = string.format( "%s", self.airbasename ) - if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil - and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil - and self.airbasename:find( "Field" ) == nil - then - subtitle = subtitle .. " Airport" + if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil + and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil + and self.airbasename:find( "Field" ) == nil + then + --subtitle = subtitle .. " Airport" + subtitle = subtitle .. " "..self.gettext:GetEntry("AIRPORT",self.locale) end if not self.useSRS then --self:I(string.format( "%s/%s.ogg", self.theatre, self.airbasename )) @@ -1846,7 +2087,9 @@ function ATIS:onafterBroadcast( From, Event, To ) local alltext = subtitle -- Information tag - subtitle = string.format( "Information %s", NATO ) + local information = self.gettext:GetEntry("INFORMATION",self.locale) + --subtitle = string.format( "Information %s", NATO ) + subtitle = string.format( "%s %s", information, NATO ) local _INFORMATION = subtitle if not self.useSRS then self:Transmission( ATIS.Sound.Information, 0.5, subtitle ) @@ -1865,7 +2108,9 @@ function ATIS:onafterBroadcast( From, Event, To ) if not self.zulutimeonly then -- Sunrise Time - subtitle = string.format( "Sunrise at %s local time", SUNRISE ) + local sunrise = self.gettext:GetEntry("SUNRISEAT",self.locale) + --subtitle = string.format( "Sunrise at %s local time", SUNRISE ) + subtitle = string.format( sunrise, SUNRISE ) if not self.useSRS then self:Transmission( ATIS.Sound.SunriseAt, 0.5, subtitle ) self.radioqueue:Number2Transmission( SUNRISE, nil, 0.2 ) @@ -1874,7 +2119,9 @@ function ATIS:onafterBroadcast( From, Event, To ) alltext = alltext .. ";\n" .. subtitle -- Sunset Time - subtitle = string.format( "Sunset at %s local time", SUNSET ) + local sunset = self.gettext:GetEntry("SUNSETAT",self.locale) + --subtitle = string.format( "Sunset at %s local time", SUNSET ) + subtitle = string.format( sunset, SUNSET ) if not self.useSRS then self:Transmission( ATIS.Sound.SunsetAt, 0.5, subtitle ) self.radioqueue:Number2Transmission( SUNSET, nil, 0.5 ) @@ -1884,17 +2131,22 @@ function ATIS:onafterBroadcast( From, Event, To ) end -- Wind - -- Adding a space after each digit of WINDFROM to convert this to aviation-speak for TTS via SRS + -- Adding a space after each digit of WINDFROM to convert this to aviation-speak for TTS via SRS if self.useSRS then WINDFROM = string.gsub(WINDFROM,".", "%1 ") - end + end if self.metric then - subtitle = string.format( "Wind from %s at %s m/s", WINDFROM, WINDSPEED ) + local windfrom = self.gettext:GetEntry("WINDFROMMS",self.locale) + --subtitle = string.format( "Wind from %s at %s m/s", WINDFROM, WINDSPEED ) + subtitle = string.format( windfrom, WINDFROM, WINDSPEED ) else - subtitle = string.format( "Wind from %s at %s knots", WINDFROM, WINDSPEED ) + local windfrom = self.gettext:GetEntry("WINDFROMKNOTS",self.locale) + --subtitle = string.format( "Wind from %s at %s m/s", WINDFROM, WINDSPEED ) + subtitle = string.format( windfrom, WINDFROM, WINDSPEED ) end if turbulence > 0 then - subtitle = subtitle .. ", gusting" + --subtitle = subtitle .. ", gusting" + subtitle = subtitle .. ", "..self.gettext:GetEntry("GUSTING",self.locale) end local _WIND = subtitle if not self.useSRS then @@ -1912,12 +2164,16 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle - + -- Visibility if self.metric then - subtitle = string.format( "Visibility %s km", VISIBILITY ) + local visi = self.gettext:GetEntry("VISIKM",self.locale) + --subtitle = string.format( "Visibility %s km", VISIBILITY ) + subtitle = string.format( visi, VISIBILITY ) else - subtitle = string.format( "Visibility %s SM", VISIBILITY ) + local visi = self.gettext:GetEntry("VISISM",self.locale) + --subtitle = string.format( "Visibility %s SM", VISIBILITY ) + subtitle = string.format( visi, VISIBILITY ) end if not self.useSRS then self:Transmission( ATIS.Sound.Visibilty, 1.0, subtitle ) @@ -1929,44 +2185,52 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle - + subtitle = "" -- Weather phenomena local wp = false local wpsub = "" if precepitation == 1 then wp = true - wpsub = wpsub .. " rain" + --wpsub = wpsub .. " rain" + wpsub = wpsub .. " "..self.gettext:GetEntry("RAIN",self.locale) elseif precepitation == 2 then if wp then wpsub = wpsub .. "," end - wpsub = wpsub .. " thunderstorm" + --wpsub = wpsub .. " thunderstorm" + wpsub = wpsub .. " "..self.gettext:GetEntry("TSTORM",self.locale) wp = true elseif precepitation == 3 then - wpsub = wpsub .. " snow" + --wpsub = wpsub .. " snow" + wpsub = wpsub .. " "..self.gettext:GetEntry("SNOW",self.locale) wp = true elseif precepitation == 4 then - wpsub = wpsub .. " snowstorm" + --wpsub = wpsub .. " snowstorm" + wpsub = wpsub .. " "..self.gettext:GetEntry("SSTROM",self.locale) wp = true end if fog then if wp then wpsub = wpsub .. "," end - wpsub = wpsub .. " fog" + --wpsub = wpsub .. " fog" + wpsub = wpsub .. " "..self.gettext:GetEntry("FOG",self.locale) wp = true end if dust then if wp then wpsub = wpsub .. "," end - wpsub = wpsub .. " dust" + --wpsub = wpsub .. " dust" + wpsub = wpsub .. " "..self.gettext:GetEntry("DUST",self.locale) wp = true end -- Actual output if wp then - subtitle = string.format( "Weather phenomena:%s", wpsub ) + local phenos = self.gettext:GetEntry("PHENOMENA",self.locale) + --subtitle = string.format( "Weather phenomena: %s", wpsub ) + subtitle = string.format( "%s: %s", phenos, wpsub ) if not self.useSRS then self:Transmission( ATIS.Sound.WeatherPhenomena, 1.0, subtitle ) if precepitation == 1 then @@ -1998,10 +2262,14 @@ function ATIS:onafterBroadcast( From, Event, To ) local cceil = tostring( tonumber( CLOUDCEIL1000 ) * 1000 + tonumber( CLOUDCEIL0100 ) * 100 ) if self.metric then -- subtitle=string.format("Cloud base %s, ceiling %s meters", CLOUDBASE, CLOUDCEIL) - subtitle = string.format( "Cloud base %s, ceiling %s meters", cbase, cceil ) + local cloudbase = self.gettext:GetEntry("CLOUDBASEM",self.locale) + --subtitle = string.format( "Cloud base %s, ceiling %s meters", cbase, cceil ) + subtitle = string.format( cloudbase, cbase, cceil ) else -- subtitle=string.format("Cloud base %s, ceiling %s feet", CLOUDBASE, CLOUDCEIL) - subtitle = string.format( "Cloud base %s, ceiling %s feet", cbase, cceil ) + local cloudbase = self.gettext:GetEntry("CLOUDBASEFT",self.locale) + --subtitle = string.format( "Cloud base %s, ceiling %s feet", cbase, cceil ) + subtitle = string.format( cloudbase, cbase, cceil ) end if not self.useSRS then self:Transmission( ATIS.Sound.CloudBase, 1.0, subtitle ) @@ -2034,17 +2302,22 @@ function ATIS:onafterBroadcast( From, Event, To ) alltext = alltext .. ";\n" .. subtitle subtitle = "" -- Temperature + local temptext = self.gettext:GetEntry("TEMPERATURE",self.locale) if self.TDegF then if temperature < 0 then - subtitle = string.format( "Temperature -%s °F", TEMPERATURE ) + --subtitle = string.format( "Temperature -%s °F", TEMPERATURE ) + subtitle = string.format( "%s -%s °F", temptext, TEMPERATURE ) else - subtitle = string.format( "Temperature %s °F", TEMPERATURE ) + --subtitle = string.format( "Temperature %s °F", TEMPERATURE ) + subtitle = string.format( "%s %s °F", temptext, TEMPERATURE ) end else if temperature < 0 then - subtitle = string.format( "Temperature -%s °C", TEMPERATURE ) + --subtitle = string.format( "Temperature -%s °C", TEMPERATURE ) + subtitle = string.format( "%s -%s °C", temptext, TEMPERATURE ) else - subtitle = string.format( "Temperature %s °C", TEMPERATURE ) + --subtitle = string.format( "Temperature %s °C", TEMPERATURE ) + subtitle = string.format( "%s %s °C", temptext, TEMPERATURE ) end end local _TEMPERATURE = subtitle @@ -2061,19 +2334,24 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle - + -- Dew point + local dewtext = self.gettext:GetEntry("DEWPOINT",self.locale) if self.TDegF then if dewpoint < 0 then - subtitle = string.format( "Dew point -%s °F", DEWPOINT ) + --subtitle = string.format( "Dew point -%s °F", DEWPOINT ) + subtitle = string.format( "%s -%s °F", dewtext, DEWPOINT ) else - subtitle = string.format( "Dew point %s °F", DEWPOINT ) + --subtitle = string.format( "Dew point %s °F", DEWPOINT ) + subtitle = string.format( "%s %s °F", dewtext, DEWPOINT ) end else if dewpoint < 0 then - subtitle = string.format( "Dew point -%s °C", DEWPOINT ) + --subtitle = string.format( "Dew point -%s °C", DEWPOINT ) + subtitle = string.format( "%s -%s °C", dewtext, DEWPOINT ) else - subtitle = string.format( "Dew point %s °C", DEWPOINT ) + --subtitle = string.format( "Dew point %s °C", DEWPOINT ) + subtitle = string.format( "%s %s °C", dewtext, DEWPOINT ) end end local _DEWPOINT = subtitle @@ -2092,36 +2370,45 @@ function ATIS:onafterBroadcast( From, Event, To ) alltext = alltext .. ";\n" .. subtitle -- Altimeter QNH/QFE. + local altim = self.gettext:GetEntry("ALTIMETER",self.locale) if self.PmmHg then if self.qnhonly then - subtitle = string.format( "Altimeter %s.%s mmHg", QNH[1], QNH[2] ) + --subtitle = string.format( "Altimeter %s.%s mmHg", QNH[1], QNH[2] ) + subtitle = string.format( "%s %s.%s mmHg", altim, QNH[1], QNH[2] ) else - subtitle = string.format( "Altimeter: QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2] ) + --subtitle = string.format( "Altimeter: QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2] ) + subtitle = string.format( "%s: QNH %s.%s, QFE %s.%s mmHg", altim, QNH[1], QNH[2], QFE[1], QFE[2] ) end else if self.metric then if self.qnhonly then - subtitle = string.format( "Altimeter %s.%s hPa", QNH[1], QNH[2] ) + --subtitle = string.format( "Altimeter %s.%s hPa", QNH[1], QNH[2] ) + subtitle = string.format( "%s %s.%s hPa", altim, QNH[1], QNH[2] ) else - subtitle = string.format( "Altimeter: QNH %s.%s, QFE %s.%s hPa", QNH[1], QNH[2], QFE[1], QFE[2] ) + --subtitle = string.format( "Altimeter: QNH %s.%s, QFE %s.%s hPa", QNH[1], QNH[2], QFE[1], QFE[2] ) + subtitle = string.format( "%s: QNH %s.%s, QFE %s.%s hPa", altim, QNH[1], QNH[2], QFE[1], QFE[2] ) end else if self.qnhonly then - subtitle = string.format( "Altimeter %s.%s inHg", QNH[1], QNH[2] ) + --subtitle = string.format( "Altimeter %s.%s inHg", QNH[1], QNH[2] ) + subtitle = string.format( "%s %s.%s inHg", altim, QNH[1], QNH[2] ) else - subtitle = string.format( "Altimeter: QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2] ) + --subtitle = string.format( "Altimeter: QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2] ) + subtitle = string.format( "%s: QNH %s.%s, QFE %s.%s inHg", altim, QNH[1], QNH[2], QFE[1], QFE[2] ) end end end - + if self.ReportmBar and not self.metric then if self.qnhonly then - subtitle = string.format( "%s;\nAltimeter %d hPa", subtitle, mBarqnh ) + --subtitle = string.format( "%s;\nAltimeter %d hPa", subtitle, mBarqnh ) + subtitle = string.format( "%s;\n%s %d hPa", subtitle, altim, mBarqnh ) else - subtitle = string.format( "%s;\nAltimeter: QNH %d, QFE %d hPa", subtitle, mBarqnh, mBarqfe) + --subtitle = string.format( "%s;\nAltimeter: QNH %d, QFE %d hPa", subtitle, mBarqnh, mBarqfe) + subtitle = string.format( "%s;\n%s: QNH %d, QFE %d hPa", subtitle, altim, mBarqnh, mBarqfe) end end - + local _ALTIMETER = subtitle if not self.useSRS then self:Transmission( ATIS.Sound.Altimeter, 1.0, subtitle ) @@ -2155,18 +2442,22 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle - + local _RUNACT if not self.ATISforFARPs then -- Active runway. local subtitle if runwayLanding then - subtitle=string.format("Active runway %s", runwayLanding) + local actrun = self.gettext:GetEntry("ACTIVERUN",self.locale) + --subtitle=string.format("Active runway %s", runwayLanding) + subtitle=string.format("%s %s", actrun, runwayLanding) if rwyLandingLeft==true then - subtitle=subtitle.." Left" + --subtitle=subtitle.." Left" + subtitle=subtitle.." "..self.gettext:GetEntry("LEFT",self.locale) elseif rwyLandingLeft==false then - subtitle=subtitle.." Right" + --subtitle=subtitle.." Right" + subtitle=subtitle.." "..self.gettext:GetEntry("RIGHT",self.locale) end end _RUNACT = subtitle @@ -2180,27 +2471,31 @@ function ATIS:onafterBroadcast( From, Event, To ) end end alltext = alltext .. ";\n" .. subtitle - + -- Runway length. if self.rwylength then - + local runact = self.airbase:GetActiveRunway( self.runwaym2t ) local length = runact.length if not self.metric then length = UTILS.MetersToFeet( length ) end - + -- Length in thousands and hundrets of ft/meters. local L1000, L0100 = self:_GetThousandsAndHundreds( length ) - + -- Subtitle. - local subtitle = string.format( "Runway length %d", length ) + local rwyl = self.gettext:GetEntry("RWYLENGTH",self.locale) + local meters = self.gettext:GetEntry("METERS",self.locale) + local feet = self.gettext:GetEntry("FEET",self.locale) + --local subtitle = string.format( "Runway length %d", length ) + local subtitle = string.format( "%s %d", rwyl, length ) if self.metric then - subtitle = subtitle .. " meters" + subtitle = subtitle .. " "..meters else - subtitle = subtitle .. " feet" + subtitle = subtitle .. " "..feet end - + -- Transmit. if not self.useSRS then self:Transmission( ATIS.Sound.RunwayLength, 1.0, subtitle ) @@ -2223,7 +2518,11 @@ function ATIS:onafterBroadcast( From, Event, To ) end -- Airfield elevation if self.elevation then - + + local elev = self.gettext:GetEntry("ELEVATION",self.locale) + local meters = self.gettext:GetEntry("METERS",self.locale) + local feet = self.gettext:GetEntry("FEET",self.locale) + local elevation = self.airbase:GetHeight() if not self.metric then elevation = UTILS.MetersToFeet( elevation ) @@ -2233,11 +2532,12 @@ function ATIS:onafterBroadcast( From, Event, To ) local L1000, L0100 = self:_GetThousandsAndHundreds( elevation ) -- Subtitle. - local subtitle = string.format( "Elevation %d", elevation ) + --local subtitle = string.format( "Elevation %d", elevation ) + local subtitle = string.format( "%s %d", elev, elevation ) if self.metric then - subtitle = subtitle .. " meters" + subtitle = subtitle .. " "..meters else - subtitle = subtitle .. " feet" + subtitle = subtitle .. " "..feet end -- Transmit. @@ -2269,7 +2569,9 @@ function ATIS:onafterBroadcast( From, Event, To ) freqs = freqs .. ", " end end - subtitle = string.format( "Tower frequency %s", freqs ) + local twrfrq = self.gettext:GetEntry("TOWERFREQ",self.locale) + --subtitle = string.format( "Tower frequency %s", freqs ) + subtitle = string.format( "%s %s", twrfrq, freqs ) if not self.useSRS then self:Transmission( ATIS.Sound.TowerFrequency, 1.0, subtitle ) for _, freq in pairs( self.towerfrequency ) do @@ -2289,7 +2591,9 @@ function ATIS:onafterBroadcast( From, Event, To ) -- ILS local ils=self:GetNavPoint(self.ils, runwayLanding, rwyLandingLeft) if ils then - subtitle = string.format( "ILS frequency %.2f MHz", ils.frequency ) + local ilstxt = self.gettext:GetEntry("ILSFREQ",self.locale) + --subtitle = string.format( "ILS frequency %.2f MHz", ils.frequency ) + subtitle = string.format( "%s %.2f MHz", ilstxt, ils.frequency ) if not self.useSRS then self:Transmission( ATIS.Sound.ILSFrequency, 1.0, subtitle ) local f = string.format( "%.2f", ils.frequency ) @@ -2307,7 +2611,9 @@ function ATIS:onafterBroadcast( From, Event, To ) -- Outer NDB local ndb=self:GetNavPoint(self.ndbouter, runwayLanding, rwyLandingLeft) if ndb then - subtitle = string.format( "Outer NDB frequency %.2f MHz", ndb.frequency ) + local ndbtxt = self.gettext:GetEntry("OUTERNDB",self.locale) + --subtitle = string.format( "Outer NDB frequency %.2f MHz", ndb.frequency ) + subtitle = string.format( "%s %.2f MHz", ndbtxt, ndb.frequency ) if not self.useSRS then self:Transmission( ATIS.Sound.OuterNDBFrequency, 1.0, subtitle ) local f = string.format( "%.2f", ndb.frequency ) @@ -2325,7 +2631,9 @@ function ATIS:onafterBroadcast( From, Event, To ) -- Inner NDB local ndb=self:GetNavPoint(self.ndbinner, runwayLanding, rwyLandingLeft) if ndb then - subtitle = string.format( "Inner NDB frequency %.2f MHz", ndb.frequency ) + local ndbtxt = self.gettext:GetEntry("INNERNDB",self.locale) + --subtitle = string.format( "Inner NDB frequency %.2f MHz", ndb.frequency ) + subtitle = string.format( "%s %.2f MHz", ndbtxt, ndb.frequency ) if not self.useSRS then self:Transmission( ATIS.Sound.InnerNDBFrequency, 1.0, subtitle ) local f = string.format( "%.2f", ndb.frequency ) @@ -2342,9 +2650,13 @@ function ATIS:onafterBroadcast( From, Event, To ) -- VOR if self.vor then - subtitle = string.format( "VOR frequency %.2f MHz", self.vor ) + local vortxt = self.gettext:GetEntry("VORFREQ",self.locale) + local vorttstxt = self.gettext:GetEntry("VORFREQTTS",self.locale) + --subtitle = string.format( "VOR frequency %.2f MHz", self.vor ) + subtitle = string.format( "%s %.2f MHz", vortxt, self.vor ) if self.useSRS then - subtitle = string.format( "V O R frequency %.2f MHz", self.vor ) + --subtitle = string.format( "V O R frequency %.2f MHz", self.vor ) + subtitle = string.format( "%s %.2f MHz", vorttstxt, self.vor ) end if not self.useSRS then self:Transmission( ATIS.Sound.VORFrequency, 1.0, subtitle ) @@ -2362,7 +2674,9 @@ function ATIS:onafterBroadcast( From, Event, To ) -- TACAN if self.tacan then - subtitle=string.format("TACAN channel %dX Ray", self.tacan) + local tactxt = self.gettext:GetEntry("TACANCH",self.locale) + --subtitle=string.format("TACAN channel %dX Ray", self.tacan) + subtitle=string.format(tactxt, self.tacan) if not self.useSRS then self:Transmission( ATIS.Sound.TACANChannel, 1.0, subtitle ) self.radioqueue:Number2Transmission( tostring( self.tacan ), nil, 0.2 ) @@ -2373,7 +2687,9 @@ function ATIS:onafterBroadcast( From, Event, To ) -- RSBN if self.rsbn then - subtitle = string.format( "RSBN channel %d", self.rsbn ) + local rsbntxt = self.gettext:GetEntry("RSBNCH",self.locale) + --subtitle = string.format( "RSBN channel %d", self.rsbn ) + subtitle = string.format( "%s %d", rsbntxt, self.rsbn ) if not self.useSRS then self:Transmission( ATIS.Sound.RSBNChannel, 1.0, subtitle ) self.radioqueue:Number2Transmission( tostring( self.rsbn ), nil, 0.2 ) @@ -2384,21 +2700,25 @@ function ATIS:onafterBroadcast( From, Event, To ) -- PRMG local ndb=self:GetNavPoint(self.prmg, runwayLanding, rwyLandingLeft) if ndb then - subtitle = string.format( "PRMG channel %d", ndb.frequency ) + local prmtxt = self.gettext:GetEntry("PRMGCH",self.locale) + --subtitle = string.format( "PRMG channel %d", ndb.frequency ) + subtitle = string.format( "%s %d", prmtxt, ndb.frequency ) if not self.useSRS then self:Transmission( ATIS.Sound.PRMGChannel, 1.0, subtitle ) self.radioqueue:Number2Transmission( tostring( ndb.frequency ), nil, 0.5 ) end alltext = alltext .. ";\n" .. subtitle end - + -- additional info, if any if self.useSRS and self.AdditionalInformation then alltext = alltext .. ";\n"..self.AdditionalInformation end - + -- Advice on initial... - subtitle = string.format( "Advise on initial contact, you have information %s", NATO ) + local advtxt = self.gettext:GetEntry("ADVISE",self.locale) + --subtitle = string.format( "Advise on initial contact, you have information %s", NATO ) + subtitle = string.format( "%s %s", advtxt, NATO ) if not self.useSRS then self:Transmission( ATIS.Sound.AdviceOnInitial, 0.5, subtitle ) self.radioqueue:NewTransmission( string.format( "NATO Alphabet/%s.ogg", NATO ), 0.75, self.soundpath ) @@ -2431,16 +2751,33 @@ function ATIS:onafterReport( From, Event, To, Text ) local text = string.gsub( Text, "[\r\n]", "" ) -- Replace other stuff. - local text = string.gsub( text, "SM", "statute miles" ) - local text = string.gsub( text, "°C", "degrees Celsius" ) - local text = string.gsub( text, "°F", "degrees Fahrenheit" ) - local text = string.gsub( text, "inHg", "inches of Mercury" ) - local text = string.gsub( text, "mmHg", "millimeters of Mercury" ) - local text = string.gsub( text, "hPa", "hectopascals" ) - local text = string.gsub( text, "m/s", "meters per second" ) - local text = string.gsub( text, "TACAN", "tackan" ) - local text = string.gsub( text, "FARP", "farp" ) - + local statute = self.gettext:GetEntry("STATUTE",self.locale) + local degc = self.gettext:GetEntry("DEGREES",self.locale) + local degf = self.gettext:GetEntry("FAHRENHEIT",self.locale) + local inhg = self.gettext:GetEntry("INCHHG",self.locale) + local mmhg = self.gettext:GetEntry("MMHG",self.locale) + local hpa = self.gettext:GetEntry("HECTO",self.locale) + local emes = self.gettext:GetEntry("METERSPER",self.locale) + local tacan = self.gettext:GetEntry("TACAN",self.locale) + local farp = self.gettext:GetEntry("FARP",self.locale) + + + local text = string.gsub( text, "SM", statute ) + text = string.gsub( text, "°C", degc ) + text = string.gsub( text, "°F", degf ) + text = string.gsub( text, "inHg", inhg ) + text = string.gsub( text, "mmHg", mmhg ) + text = string.gsub( text, "hPa", hpa ) + text = string.gsub( text, "m/s", emes ) + text = string.gsub( text, "TACAN", tacan ) + text = string.gsub( text, "FARP", farp ) + + local delimiter = self.gettext:GetEntry("DELIMITER",self.locale) + + if string.lower(self.locale) ~= "en" then + text = string.gsub(text,"(%d+)(%.)(%d+)","%1 "..delimiter.." %3") + end + -- Replace ";" by "." local text = string.gsub( text, ";", " . " ) @@ -2449,10 +2786,10 @@ function ATIS:onafterReport( From, Event, To, Text ) -- Play text-to-speech report. local duration = STTS.getSpeechTime(text,0.95) - self.msrsQ:NewTransmission(text,duration,self.msrs,nil,2) + self.msrsQ:NewTransmission(text,duration,self.msrs,nil,2) --self.msrs:PlayText( text ) self.SRSText = text - + end end @@ -2524,7 +2861,7 @@ end -- @return #string Active runway, e.g. "31" for 310 deg. -- @return #boolean Use Left=true, Right=false, or nil. function ATIS:GetActiveRunway(Takeoff) - + local runway=nil --Wrapper.Airbase#AIRBASE.Runway if Takeoff then runway=self.airbase:GetActiveRunwayTakeoff() @@ -2587,7 +2924,7 @@ function ATIS:GetNavPoint( navpoints, runway, left ) if hdgD <= 15 then -- We allow an error of +-15° here. if navL == nil or (navL == true and left == true) or (navL == false and left == false) then return nav - end + end end end end From f71039e8407c9eeb668f620ed758b8bd7f51a366 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 20 Sep 2023 17:16:52 +0200 Subject: [PATCH 356/603] #ATIS * Added Spanish TTS locale ("es" --- Moose Development/Moose/Ops/ATIS.lua | 66 ++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 1c6f4e6bc..e0aea4bbd 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -386,7 +386,7 @@ -- -- atis=ATIS:New("Batumi", 305, radio.modulation.AM) -- atis:SetSRS("D:\\DCS\\_SRS\\", "female", "de_DE") --- atis:SetLocale("de") +-- atis:SetLocale("de") -- available locales from source are "en", "de" and "es" -- atis:Start() -- -- ## FARPS @@ -808,7 +808,67 @@ ATIS.Messages = { TACAN = "Tackan", FARP = "Farp", DELIMITER = "Komma", -- decimal delimiter - } + }, + -- Set ES Locale translations for ATIS thanks to @Ritu + ES = + { + HOURS = "horas", + TIME = "horas", + NOCLOUDINFO = "Información sobre capa de nubes no disponible", + OVERCAST = "Nublado", + BROKEN = "Nubes rotas", + SCATTERED = "Nubes dispersas", + FEWCLOUDS = "Ligeramente nublado", + NOCLOUDS = "Despejado", + AIRPORT = "Aeropuerto", + INFORMATION ="Informacion", + SUNRISEAT = "Amanecer a las %s hora local", + SUNSETAT = "Puesta de sol a las %s hora local", + WINDFROMMS = "Viento procedente de %s con %s m/s", + WINDFROMKNOTS = "Viento de %s con %s nudos", + GUSTING = "ráfagas", + VISIKM = "Visibilidad %s km", + VISISM = "Visibilidad %s millas", + RAIN = "Lluvia", + TSTORM = "Tormenta", + SNOW = "Nieve", + SSTROM = "Tormenta de nieve", + FOG = "Niebla", + DUST = "Polvo", + PHENOMENA = "Fenómenos meteorológicos", + CLOUDBASEM = "Capa de nubes de %s a %s metros", + CLOUDBASEFT = "Capa de nubes de %s a %s pies", + TEMPERATURE = "Temperatura", + DEWPOINT = "Punto de rocio", + ALTIMETER = "Altímetro", + ACTIVERUN = "Pista activa", + LEFT = "Izquierda", + RIGHT = "Derecha", + RWYLENGTH = "Longitud de pista", + METERS = "Metro", + FEET = "Pie", + ELEVATION = "Elevación", + TOWERFREQ = "Frecuencias de la torre de control", + ILSFREQ = "Fecuencia ILS", + OUTERNDB = "Frecuencia NDB externa", + INNERNDB = "Frecuencia NDB interior", + VORFREQ = "Frecuencia VOR", + VORFREQTTS = "Frecuencia V O R", + TACANCH = "Canal TACAN %d Xaver", + RSBNCH = "Canal RSBN", + PRMGCH = "Canal PRMG", + ADVISE = "Avise en el contacto inicial a torre de que tiene la informacion", + STATUTE = "Millas inglesas", + DEGREES = "Grados Celsius", + FAHRENHEIT = "Grados Fahrenheit", + INCHHG = "Pulgadas de mercurio", + MMHG = "Milímeteros de Mercurio", + HECTO = "Hectopascales", + METERSPER = "Metros por segundo", + TACAN = "Tacan", + FARP = "Farp", + DELIMITER = "Punto", -- decimal delimiter + }, } --- @@ -821,7 +881,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.10.1" +ATIS.version = "0.10.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list From baf8edfc9c043656f976883660573d6f42d3421b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Sep 2023 08:42:49 +0200 Subject: [PATCH 357/603] chief typo --- Moose Development/Moose/Ops/Chief.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 77477e0cc..f96a70d45 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -163,7 +163,7 @@ -- -- Will at a strategic zone with importance 2. -- --- 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: +-- 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 launched: -- -- * A mission of type `AUFTRAG.Type.CASENHANCED` is started if assets are available that can carry out this mission type. -- * A mission of type `AUFTRAG.Type.ARTY` is started provided assets are available. From c1c7279e0172b4bb50df6a819aa8cf9851d0d3a0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Sep 2023 08:43:00 +0200 Subject: [PATCH 358/603] mods --- Moose Development/Moose/Modules_local.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 7fc3cfa52..46265b420 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -110,6 +110,7 @@ __Moose.Include( 'Ops\\PlayerTask.lua' ) __Moose.Include( 'Ops\\Operation.lua' ) __Moose.Include( 'Ops\\FlightControl.lua' ) __Moose.Include( 'Ops\\PlayerRecce.lua' ) +__Moose.Include( 'Ops\\EasyGCICAP.lua' ) __Moose.Include( 'AI\\AI_Balancer.lua' ) __Moose.Include( 'AI\\AI_Air.lua' ) From 3c9bf1de770c941055fc6ce222f547b5199599c3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 26 Sep 2023 13:07:10 +0200 Subject: [PATCH 359/603] #EasyGCICAP * Initial Release --- Moose Development/Moose/Ops/EasyGCICAP.lua | 929 +++++++++++++++++++++ 1 file changed, 929 insertions(+) create mode 100644 Moose Development/Moose/Ops/EasyGCICAP.lua diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua new file mode 100644 index 000000000..935e517a3 --- /dev/null +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -0,0 +1,929 @@ +------------------------------------------------------------------------- +-- Easy CAP/GCI Class, based on OPS classes +------------------------------------------------------------------------- +-- Documentation +-- +-- https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Ops.EasyGCICAP.html +-- +------------------------------------------------------------------------- +-- Date: September 2023 +------------------------------------------------------------------------- +---@diagnostic disable: cast-local-type +--- **Ops** - Easy GCI & CAP Manager +-- +-- === +-- +-- **Main Features:** +-- +-- * Automatically create and manage A2A CAP/GCI defenses using an @{Ops.AirWing#AIRWING} and Squadrons for one coalition +-- * Easy set-up +-- * Add additional AirWings on other airbases +-- * Each wing can have more than one Squadron - tasking to Squadrons is done on a random basis per AirWing +-- * Create borders and zones of engagement +-- * Detection can be ground based and/or via AWACS +-- +-- === +-- +-- ### AUTHOR: **applevangelist** +-- +-- @module Ops.EasyGCICAP +-- @image AI_Combat_Air_Patrol.JPG + + +--- EASYGCICAP Class +-- @type EASYGCICAP +-- @field #string ClassName +-- @field #number overhead +-- @field #number engagerange +-- @field #number capgrouping +-- @field #string airbasename +-- @field Wrapper.Airbase#AIRBASE airbase +-- @field #number coalition +-- @field #string alias +-- @field #table wings +-- @field Ops.Intelligence#INTEL Intel +-- @field #number resurrection +-- @field #number capspeed +-- @field #number capalt +-- @field #number capdir +-- @field #number capleg +-- @field #number capgrouping +-- @field #number maxinterceptsize +-- @field #number missionrange +-- @field #number noaltert5 +-- @field #table ManagedAW +-- @field #table ManagedSQ +-- @field #table ManagedCP +-- @field #table ManagedTK +-- @field #number MaxAliveMissions +-- @field #boolean debug +-- @extends Core.Fsm#FSM + +--- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown. +-- +-- === +-- +-- # The EasyGCICAP Concept +-- +-- The idea of this class is partially to make the OPS classes easier operational for an A2A CAP/GCI defense network, and to replace the legacy AI_A2A_Dispatcher system - not to it's +-- full extent, but make a basic system work very quickly. +-- +-- # Setup +-- +-- ## Basic understanding +-- +-- The basics are, there is **one** and only **one** AirWing per airbase. Each AirWing has **at least** one Squadron, who will do both CAP and GCI tasks. Squadrons will be randomly chosen for the task at hand. +-- Each AirWing has **at least** one CAP Point that it manages. CAP Points will be covered by the AirWing automatically as long as airframes are available. Detected intruders will be assigned to **one** +-- AirWing based on proximity (that is, if you have more than one). +-- +-- ## Assignment of tasks for intruders +-- +-- Either a CAP Plane or a newly spawned GCI plane will take care of the intruders. Standard overhead is 0.75, i.e. a group of 3 intrudes will +-- be managed by 2 planes from the assigned AirWing. There is an maximum missions limitation per AirWing, so we do not spam the skies. +-- +-- ## Basic set-up code +-- +-- ### Prerequisites +-- +-- You have to put a STATIC object on the airbase with the UNIT name according to the name of the airbase. E.g. for Kuitaisi this has to have the name Kutaisi. This object symbolizes the AirWing HQ. +-- Next put a late activated template group for your CAP/GCI Squadron on the map. Last, put a zone on the map for the CAP operations, let's name it "Blue Zone 1". Size of the zone plays no role. +-- Put an EW radar system on the map and name it aptly, like "Blue EWR". +-- +-- ### Code it +-- +-- -- Set up a basic system for the blue side, we'll reside on Kutaisi, and use GROUP objects with "Blue EWR" in the name as EW Radar Systems. +-- local mywing = EASYGCICAP:New("Blue CAP Operations",AIRBASE.Caucasus.Kutaisi,"blue","Blue EWR") +-- +-- -- Add a CAP patrol point belonging to our airbase, we'll be at 30k ft doing 400 kn, initial direction 90 degrees (East), leg 20NM +-- mywing:AddPatrolPointCAP(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone 1"):GetCoordinate(),30000,400,90,20) +-- +-- -- Add a Squadron with template "Blue Sq1 M2000c", 20 airframes, skill good, Modex starting with 102 and skin "Vendee Jeanne" +-- mywing:AddSquadron("Blue Sq1 M2000c","CAP Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.GOOD,102,"ec1.5_Vendee_Jeanne_clean") +-- +-- -- Add a couple of zones +-- -- We'll defend our border +-- mywing:AddAcceptZone(ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) )) +-- -- We'll attack intruders also here +-- mywing:AddAcceptZone(ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" ))) +-- -- We'll leave the reds alone on their turf +-- mywing:AddRejectZone(ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) )) +-- +-- -- Optional - Draw the borders on the map so we see what's going on +-- -- Set up borders on map +-- local BlueBorder = ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) ) +-- BlueBorder:DrawZone(-1,{0,0,1},1,FillColor,FillAlpha,1,true) +-- local BlueNoGoZone = ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" )) +-- BlueNoGoZone:DrawZone(-1,{1,1,0},1,FillColor,FillAlpha,2,true) +-- local BlueNoGoZone2 = ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) ) +-- BlueNoGoZone2:DrawZone(-1,{1,0,0},1,FillColor,FillAlpha,4,true) +-- +-- ### Add a second airwing with squads and own CAP point (optional) +-- +-- -- Set this up at Sukhumi +-- mywing:AddAirwing(AIRBASE.Caucasus.Sukhumi_Babushara,"Blue CAP Sukhumi") +-- -- CAP Point "Blue Zone 2" +-- mywing:AddPatrolPointCAP(AIRBASE.Caucasus.Sukhumi_Babushara,ZONE:FindByName("Blue Zone 2"):GetCoordinate(),30000,400,90,20) +-- +-- -- This one has two squadrons to choose from +-- mywing:AddSquadron("Blue Sq3 F16","CAP Sukhumi II",AIRBASE.Caucasus.Sukhumi_Babushara,20,AI.Skill.GOOD,402,"JASDF 6th TFS 43-8526 Skull Riders") +-- mywing:AddSquadron("Blue Sq2 F15","CAP Sukhumi I",AIRBASE.Caucasus.Sukhumi_Babushara,20,AI.Skill.GOOD,202,"390th Fighter SQN") +-- +-- ### Add a tanker (optional) +-- +-- -- **Note** If you need different tanker types, i.e. Boom and Drogue, set them up at different AirWings! +-- -- Add a tanker point +-- mywing:AddPatrolPointTanker(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone Tanker"):GetCoordinate(),20000,280,270,50) +-- -- Add a tanker squad +-- mywing:AddTankerSquadron("Blue Tanker","Tanker Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.EXCELLENT,602) +-- +-- # Fine-Tuning +-- +-- ## Change Defaults +-- +-- * @{#EASYGCICAP.SetDefaultResurrection}: Set how many seconds the AirWing stays inoperable after the AirWing STATIC HQ ist destroyed, default 900 secs. +-- * @{#EASYGCICAP.SetDefaultCAPSpeed}: Set how many knots the CAP flights should do (will be altitude corrected), default 300 kn. +-- * @{#EASYGCICAP.SetDefaultCAPAlt}: Set at which altitude (ASL) the CAP planes will fly, default 25,000 ft. +-- * @{#EASYGCICAP.SetDefaultCAPDirection}: Set the initial direction from the CAP point the planes will fly in degrees, default is 90°. +-- * @{#EASYGCICAP.SetDefaultCAPLeg}: Set the length of the CAP leg, default is 15 NM. +-- * @{#EASYGCICAP.SetDefaultCAPGrouping}: Set how many planes will be spawned per mission (CVAP/GCI), defaults to 2. +-- * @{#EASYGCICAP.SetDefaultMissionRange}: Set how many NM the planes can go from the home base, defaults to 100. +-- * @{#EASYGCICAP.SetDefaultNumberAlter5Standby}: Set how many planes will be spawned on cold standby (Alert5), default 2. +-- * @{#EASYGCICAP.SetDefaultEngageRange}: Set max engage range for CAP flights if they detect intruders, defaults to 50. +-- * @{#EASYGCICAP.SetMaxAliveMissions}: Set max parallel missions can be done (CAP+GCI+Alert5+Tanker), defaults to 6. +-- +-- +-- @field #EASYGCICAP +EASYGCICAP = { + ClassName = "EASYGCICAP", + overhead = 0.75, + capgrouping = 2, + airbasename = nil, + airbase = nil, + coalition = "blue", + alias = nil, + wings = {}, + Intel = nil, + resurrection = 900, + capspeed = 300, + capalt = 25000, + capdir = 45, + capleg = 15, + capgrouping = 2, + maxinterceptsize = 2, + missionrange = 100, + noaltert5 = 4, + ManagedAW = {}, + ManagedSQ = {}, + ManagedCP = {}, + ManagedTK = {}, + MaxAliveMissions = 6, + debug = true, + engagerange = 50, +} + +--- Internal Squadron data type +-- @type EASYGCICAP.Squad +-- @field #string TemplateName +-- @field #string SquadName +-- @field #string AirbaseName +-- @field #number AirFrames +-- @field #string Skill +-- @field #string Modex +-- @field #string Livery +-- @field #boolean Tanker + +--- Internal Wing data type +-- @type EASYGCICAP.Wing +-- @field #string AirbaseName +-- @field #string Alias +-- @field #string CapZoneName + +--- Internal CapPoint data type +-- @type EASYGCICAP.CapPoint +-- @field #string AirbaseName +-- @field Core.Point#COORDINATE Coordinate +-- @field #number Altitude +-- @field #number Speed +-- @field #number Heading +-- @field #number LegLength + +--- EASYGCICAP class version. +-- @field #string version +EASYGCICAP.version="0.0.4" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: TBD + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new GCICAP Manager +-- @param #EASYGCICAP self +-- @param #string Alias +-- @param #string AirbaseName +-- @param #string Coalition +-- @param #string EWRName +-- @return #EASYGCICAP self +function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName) + -- Inherit everything from FSM class. + local self=BASE:Inherit(self, FSM:New()) -- #EASYGCICAP + + -- defaults + self.alias = Alias or AirbaseName.." CAP Wing" + self.coalitionname = string.lower(Coalition) or "blue" + self.coalition = self.coaltitionname == "blue" and coalition.side.BLUE or coalition.side.RED + self.wings = {} + self.EWRName = EWRName or self.coalitionname.." EWR" + --self.CapZoneName = CapZoneName + self.airbasename = AirbaseName + self.airbase = AIRBASE:FindByName(self.airbasename) + self.BlueGoZoneSet = SET_ZONE:New() + self.BlueNoGoZoneSet = SET_ZONE:New() + self.resurrection = 900 + self.capspeed = 300 + self.capalt = 25000 + self.capdir = 90 + self.capleg = 15 + self.capgrouping = 2 + self.missionrange = 100 + self.noaltert5 = 2 + self.MaxAliveMissions = 6 + self.engagerange = 50 + + -- Set some string id for output to DCS.log file. + self.lid=string.format("EASYGCICAP %s | ", self.alias) + + -- Add FSM transitions. + -- From State --> Event --> To State + self:SetStartState("Stopped") + self:AddTransition("Stopped", "Start", "Running") + self:AddTransition("Running", "Stop", "Stopped") + self:AddTransition("*", "Status", "*") + + self:AddAirwing(self.airbasename,self.alias,self.CapZoneName) + + self:I(self.lid.."Created new instance (v"..self.version..")") + + self:__Start(math.random(6,12)) + + return self +end + +------------------------------------------------------------------------- +-- Functions +------------------------------------------------------------------------- + + +--- Set Maximum of alive missions to stop airplanes spamming the map +-- @param #EASYGCICAP self +-- @param #number Maxiumum Maxmimum number of parallel missions allowed. Count is Cap-Missions + Intercept-Missions + Alert5-Missionsm default is 6 +-- @return #EASYGCICAP self +function EASYGCICAP:SetMaxAliveMissions(Maxiumum) + self:I(self.lid.."SetDefaultResurrection") + self.MaxAliveMissions = Maxiumum or 6 + return self +end + +--- Add default time to resurrect Airwing building if destroyed +-- @param #EASYGCICAP self +-- @param #number Seconds Seconds, defaults to 900 +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultResurrection(Seconds) + self:I(self.lid.."SetDefaultResurrection") + self.resurrection = Seconds or 900 + return self +end + +--- Set default CAP Speed in knots +-- @param #EASYGCICAP self +-- @param #number Speed Speed defaults to 300 +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultCAPSpeed(Speed) + self:I(self.lid.."SetDefaultSpeed") + self.capspeed = Speed or 300 + return self +end + +--- Set default CAP Altitude in feet +-- @param #EASYGCICAP self +-- @param #number Altitude Altitude defaults to 25000 +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultCAPAlt(Altitude) + self:I(self.lid.."SetDefaultAltitude") + self.capalt = Altitude or 25000 + return self +end + +--- Set default CAP lieg initial direction in degrees +-- @param #EASYGCICAP self +-- @param #number Direction Direction defaults to 90 (East) +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultCAPDirection(Direction) + self:I(self.lid.."SetDefaultDirection") + self.capdir = Direction or 90 + return self +end + +--- Set default leg length in NM +-- @param #EASYGCICAP self +-- @param #number Leg Leg defaults to 15 +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultCAPLeg(Leg) + self:I(self.lid.."SetDefaultLeg") + self.capleg = Leg or 15 + return self +end + +--- Set default grouping, i.e. how many airplanes per CAP point +-- @param #EASYGCICAP self +-- @param #number Grouping Grouping defaults to 2 +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultCAPGrouping(Grouping) + self:I(self.lid.."SetDefaultCAPGrouping") + self.capgrouping = Grouping or 2 + return self +end + +--- Set default range planes can fly from their homebase in NM +-- @param #EASYGCICAP self +-- @param #number Range Range defaults to 100 NM +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultMissionRange(Range) + self:I(self.lid.."SetDefaultMissionRange") + self.missionrange = Range or 100 + return self +end + +--- Set default number of airframes standing by for intercept tasks (visible on the airfield) +-- @param #EASYGCICAP self +-- @param #number Airframes defaults to 2 +-- @return #EASYGCICAP selfAirframes +function EASYGCICAP:SetDefaultNumberAlter5Standby(Airframes) + self:I(self.lid.."SetDefaultNumberAlter5Standby") + self.noaltert5 = math.abs(Airframes) or 2 + return self +end + +--- Set default engage range for intruders detected by CAP flights in NM. +-- @param #EASYGCICAP self +-- @param #number Range defaults to 50NM +-- @return #EASYGCICAP selfAirframes +function EASYGCICAP:SetDefaultEngageRange(Range) + self:I(self.lid.."SetDefaultNumberAlter5Standby") + self.engagerange = Range or 50 + return self +end + +--- Add an AirWing to the manager +-- @param #EASYGCICAP self +-- @param #string Airbasename +-- @param #string Alias +-- @return #EASYGCICAP self +function EASYGCICAP:AddAirwing(Airbasename, Alias) + self:I(self.lid.."AddAirwing "..Airbasename) + + -- Create Airwing data entry + local AWEntry = {} -- #EASYGCICAP.Wing + AWEntry.AirbaseName = Airbasename + AWEntry.Alias = Alias + --AWEntry.CapZoneName = CapZoneName + + self.ManagedAW[Airbasename] = AWEntry + + return self +end + +--- (Internal) Create actual AirWings from the list +-- @param #EASYGCICAP self +-- @return #EASYGCICAP self +function EASYGCICAP:_CreateAirwings() + self:I(self.lid.."_CreateAirwings") + for airbase,data in pairs(self.ManagedAW) do + local wing = data -- #EASYGCICAP.Wing + local afb = wing.AirbaseName + local alias = wing.Alias + --local cz = wing.CapZoneName + self:_AddAirwing(airbase,alias) + end + return self +end + +--- (internal) Create and add another AirWing to the manager +-- @param #EASYGCICAP self +-- @param #string Airbasename +-- @param #string Alias +-- @return #EASYGCICAP self +function EASYGCICAP:_AddAirwing(Airbasename, Alias) + self:I(self.lid.."_AddAirwing "..Airbasename) + + -- Create Airwing + local CAP_Wing = AIRWING:New(Airbasename,Alias) + CAP_Wing:SetReportOff() + CAP_Wing:SetMarker(false) + CAP_Wing:SetAirbase(AIRBASE:FindByName(Airbasename)) + CAP_Wing:SetRespawnAfterDestroyed() + CAP_Wing:SetNumberCAP(self.capgrouping) + CAP_Wing:SetNumberTankerBoom(1) + CAP_Wing:SetNumberTankerProbe(1) + --local PatrolCoordinateKutaisi = ZONE:New(CapZoneName):GetCoordinate() + --CAP_Wing:AddPatrolPointCAP(PatrolCoordinateKutaisi,self.capalt,UTILS.KnotsToAltKIAS(self.capspeed,self.capalt),self.capdir,self.capleg) + CAP_Wing:SetTakeoffHot() + CAP_Wing:SetLowFuelThreshold(0.3) + CAP_Wing.RandomAssetScore = math.random(50,100) + CAP_Wing:Start() + + local Intel = self.Intel + + function CAP_Wing:OnAfterFlightOnMission(From, Event, To, Flightgroup, Mission) + local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP + --flightgroup:SetDespawnAfterLanding() + flightgroup:SetDespawnAfterHolding() + flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename)) + flightgroup:GetGroup():CommandEPLRS(true,5) + flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.BlueGoZoneSet,self.BlueNoGoZoneSet) + flightgroup:GetGroup():OptionROTEvadeFire() + flightgroup:SetOutOfAAMRTB() + flightgroup:SetFuelLowRTB(true) + Intel:AddAgent(flightgroup) + --function flightgroup:OnAfterHolding(From,Event,To) + --self:ClearToLand(5) + --end + + end + + if self.noaltert5 > 0 then + local alert = AUFTRAG:NewALERT5(AUFTRAG.Type.INTERCEPT) + alert:SetRequiredAssets(self.noaltert5) + alert:SetRepeat(99) + CAP_Wing:AddMission(alert) + end + + self.wings[Airbasename] = { CAP_Wing, AIRBASE:FindByName(Airbasename):GetZone(), Airbasename } + + return self +end + +--- Add a CAP patrol point to a Wing +-- @param #EASYGCICAP self +-- @param #string AirbaseName Name of the Wing's airbase +-- @param Core.Point#COORDINATE Coordinate. +-- @param #number Altitude Defaults to 25000 feet. +-- @param #number Speed Defaults to 300 knots. +-- @param #number Heading Defaults to 90 degrees (East). +-- @param #number LegLength Defaults to 15 NM. +-- @return #EASYGCICAP self +function EASYGCICAP:AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) + self:I(self.lid.."AddPatrolPointCAP "..Coordinate:ToStringLLDDM()) + local EntryCAP = {} -- #EASYGCICAP.CapPoint + EntryCAP.AirbaseName = AirbaseName + EntryCAP.Coordinate = Coordinate + EntryCAP.Altitude = Altitude or 25000 + EntryCAP.Speed = Speed or 300 + EntryCAP.Heading = Heading or 90 + EntryCAP.LegLength = LegLength or 15 + self.ManagedCP[#self.ManagedCP+1] = EntryCAP + if self.debug then + local mark = MARKER:New(Coordinate,self.lid.."Patrol Point"):ToAll() + end + return self +end + +--- Add a TANKER patrol point to a Wing +-- @param #EASYGCICAP self +-- @param #string AirbaseName Name of the Wing's airbase +-- @param Core.Point#COORDINATE Coordinate. +-- @param #number Altitude Defaults to 25000 feet. +-- @param #number Speed Defaults to 300 knots. +-- @param #number Heading Defaults to 90 degrees (East). +-- @param #number LegLength Defaults to 15 NM. +-- @return #EASYGCICAP self +function EASYGCICAP:AddPatrolPointTanker(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) + self:I(self.lid.."AddPatrolPointTanker "..Coordinate:ToStringLLDDM()) + local EntryCAP = {} -- #EASYGCICAP.CapPoint + EntryCAP.AirbaseName = AirbaseName + EntryCAP.Coordinate = Coordinate + EntryCAP.Altitude = Altitude or 25000 + EntryCAP.Speed = Speed or 300 + EntryCAP.Heading = Heading or 90 + EntryCAP.LegLength = LegLength or 15 + self.ManagedTK[#self.ManagedTK+1] = EntryCAP + if self.debug then + local mark = MARKER:New(Coordinate,self.lid.."Patrol Point Tanker"):ToAll() + end + return self +end + +--- (Internal) Set actual Tanker Points from the list +-- @param #EASYGCICAP self +-- @return #EASYGCICAP self +function EASYGCICAP:_SetTankerPatrolPoints() + self:I(self.lid.."_SetTankerPatrolPoints") + for _,_data in pairs(self.ManagedTK) do + local data = _data --#EASYGCICAP.CapPoint + local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING + local Coordinate = data.Coordinate + local Altitude = data.Altitude + local Speed = data.Speed + local Heading = data.Heading + local LegLength = data.LegLength + Wing:AddPatrolPointTANKER(Coordinate,Altitude,Speed,Heading,LegLength) + end + + return self +end + +--- (Internal) Set actual PatrolPoints from the list +-- @param #EASYGCICAP self +-- @return #EASYGCICAP self +function EASYGCICAP:_SetCAPPatrolPoints() + self:I(self.lid.."_SetCAPPatrolPoints") + for _,_data in pairs(self.ManagedCP) do + local data = _data --#EASYGCICAP.CapPoint + local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING + local Coordinate = data.Coordinate + local Altitude = data.Altitude + local Speed = data.Speed + local Heading = data.Heading + local LegLength = data.LegLength + Wing:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength) + end + + return self +end + +--- (Internal) Add a CAP patrol point to a Wing +-- @param #EASYGCICAP self +-- @param #string AirbaseName Name of the Wing's airbase +-- @param Core.Point#COORDINATE Coordinate. +-- @param #number Altitude Defaults to 25000 feet. +-- @param #number Speed Defaults to 300 knots. +-- @param #number Heading Defaults to 90 degrees (East). +-- @param #number LegLength Defaults to 15 NM. +-- @return #EASYGCICAP self +function EASYGCICAP:_AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) + self:I(self.lid.."_AddPatrolPointCAP") + local airbasename = AirbaseName or self.airbasename + local coordinate = Coordinate + local Altitude = Altitude or 25000 + local Speed = Speed or 300 + local Heading = Heading or 90 + local LegLength = LegLength or 15 + local wing = self.wings[airbasename][1] -- Ops.AirWing#AIRWING + wing:AddPatrolPointCAP(coordinate,Altitude,Speed,Heading,LegLength) + return self +end + +--- (Internal) Create actual Squadrons from the list +-- @param #EASYGCICAP self +-- @return #EASYGCICAP self +function EASYGCICAP:_CreateSquads() + self:I(self.lid.."_CreateSquads") + for name,data in pairs(self.ManagedSQ) do + local squad = data -- #EASYGCICAP.Squad + local SquadName = name + local TemplateName = squad.TemplateName + local AirbaseName = squad.AirbaseName + local AirFrames = squad.AirFrames + local Skill = squad.Skill + local Modex = squad.Modex + local Livery = squad.Livery + if squad.Tanker then + self:_AddTankerSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery) + else + self:_AddSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery) + end + end + return self +end + +--- Add a Squadron to an Airwing of the manager +-- @param #EASYGCICAP self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYGCICAP self +function EASYGCICAP:AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:I(self.lid.."AddSquadron "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYGCICAP.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 402 + EntrySQ.Livery = Livery + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + +--- Add a Tanker Squadron to an Airwing of the manager +-- @param #EASYGCICAP self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYGCICAP self +function EASYGCICAP:AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:I(self.lid.."AddTankerSquadron "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYGCICAP.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 402 + EntrySQ.Livery = Livery + EntrySQ.Tanker = true + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + +--- (Internal) Add a Squadron to an Airwing of the manager +-- @param #EASYGCICAP self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYGCICAP self +function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:I(self.lid.."_AddSquadron "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.ALERT5}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.ALERT5},75) + + return self +end + +--- (Internal) Add a Tanker Squadron to an Airwing of the manager +-- @param #EASYGCICAP self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYGCICAP self +function EASYGCICAP:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:I(self.lid.."_AddTankerSquadron "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.TANKER}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.TANKER},75) + + return self +end + +--- Add a zone to the accepted zones set. +-- @param #EASYGCICAP self +-- @param Core.Zone#ZONE_BASE Zone +-- @return #EASYGCICAP self +function EASYGCICAP:AddAcceptZone(Zone) + self:I(self.lid.."AddAcceptZone0") + self.BlueGoZoneSet:AddZone(Zone) + return self +end + +--- Add a zone to the rejected zones set. +-- @param #EASYGCICAP self +-- @param Core.Zone#ZONE_BASE Zone +-- @return #EASYGCICAP self +function EASYGCICAP:AddRejectZone(Zone) + self:I(self.lid.."AddRejectZone") + self.BlueNoGoZoneSet:AddZone(Zone) + return self +end + +--- (Internal) Start detection. +-- @param #EASYGCICAP self +-- @return #EASYGCICAP self +function EASYGCICAP:_StartIntel() + self:I(self.lid.."_StartIntel") + -- Border GCI Detection + local BlueAir_DetectionSetGroup = SET_GROUP:New() + BlueAir_DetectionSetGroup:FilterPrefixes( { self.EWRName } ) + BlueAir_DetectionSetGroup:FilterStart() + + -- Intel type detection + local BlueIntel = INTEL:New(BlueAir_DetectionSetGroup,self.coalitionname, self.EWRName) + BlueIntel:SetClusterAnalysis(true,false,false) + BlueIntel:SetForgetTime(300) + BlueIntel:SetAcceptZones(self.BlueGoZoneSet) + BlueIntel:SetRejectZones(self.BlueNoGoZoneSet) + BlueIntel:SetVerbosity(0) + BlueIntel:Start() + + if self.debug then + BlueIntel.debug = true + end + + -- Here, we'll decide if we need to launch an intercepting flight, and from where + + local overhead = self.overhead + local capspeed = self.capspeed + 100 + local capalt = self.capalt + local maxsize = self.maxinterceptsize + + local wings = self.wings + local ctlpts = self.ManagedCP + local MaxAliveMissions = self.MaxAliveMissions * self.capgrouping + + function BlueIntel:OnAfterNewCluster(From,Event,To,Cluster) + -- Aircraft? + if Cluster.ctype ~= INTEL.Ctype.AIRCRAFT then return end + -- Threatlevel 0..10 + local contact = self:GetHighestThreatContact(Cluster) + local name = contact.groupname --#string + local threat = contact.threatlevel --#number + local position = self:CalcClusterFuturePosition(Cluster,300) + -- calculate closest zone + local bestdistance = 2000*1000 -- 2000km + local targetairwing = nil -- Ops.AirWing#AIRWING + local targetawname = "" -- #string + local clustersize = self:ClusterCountUnits(Cluster) or 1 + local wingsize = math.abs(overhead * (clustersize+1)) + if wingsize > maxsize then wingsize = maxsize end + if (not Cluster.mission) and (wingsize > 0) then + MESSAGE:New(string.format("**** %s Interceptors need wingsize %d", UTILS.GetCoalitionName(self.coalition), wingsize),15,"CAPGCI"):ToAllIf(self.debug):ToLog() + for _,_data in pairs (wings) do + local airwing = _data[1] -- Ops.AirWing#AIRWING + local zone = _data[2] -- Core.Zone#ZONE + local zonecoord = zone:GetCoordinate() + local name = _data[3] -- #string + local distance = position:DistanceFromPointVec2(zonecoord) + local airframes = airwing:CountAssets(true) + if distance < bestdistance and airframes >= wingsize then + bestdistance = distance + targetairwing = airwing + targetawname = name + end + end + for _,_data in pairs (ctlpts) do + --local airwing = _data[1] -- Ops.AirWing#AIRWING + --local zone = _data[2] -- Core.Zone#ZONE + --local zonecoord = zone:GetCoordinate() + --local name = _data[3] -- #string + + local data = _data -- #EASYGCICAP.CapPoint + local name = data.AirbaseName + local zonecoord = data.Coordinate + local airwing = wings[name][1] + + local distance = position:DistanceFromPointVec2(zonecoord) + local airframes = airwing:CountAssets(true) + if distance < bestdistance and airframes >= wingsize then + bestdistance = distance + targetairwing = airwing + targetawname = name + end + end + local text = string.format("Closest Airwing is %s", targetawname) + local m = MESSAGE:New(text,10,"CAPGCI"):ToAll():ToLog() + -- Do we have a matching airwing? + if targetairwing then + local AssetCount = targetairwing:CountAssetsOnMission(MissionTypes,Cohort) + --local AssetCount = targetairwing:GetAssetsOnMission({AUFTRAG.Type.INTERCEPT}) + -- Enough airframes on mission already? + self:I(self.lid.." Assets on Mission "..AssetCount) + if AssetCount <= MaxAliveMissions then + local repeats = math.random(1,2) + local InterceptAuftrag = AUFTRAG:NewINTERCEPT(contact.group) + :SetMissionRange(150) + :SetPriority(1,true,1) + :SetRequiredAssets(wingsize) + :SetRepeatOnFailure(repeats) + :SetMissionSpeed(UTILS.KnotsToAltKIAS(capspeed,capalt)) + :SetMissionAltitude(capalt) + targetairwing:AddMission(InterceptAuftrag) + Cluster.mission = InterceptAuftrag + end + else + MESSAGE:New("**** Not enough airframes available or max mission limit reached!",15,"CAPGCI"):ToAllIf(self.debug):ToLog() + end + end + end +self.Intel = BlueIntel +return self +end + +------------------------------------------------------------------------- +-- FSM Functions +------------------------------------------------------------------------- + +--- (Internal) FSM Function onafterStart +-- @param #EASYGCICAP self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #EASYGCICAP self +function EASYGCICAP:onafterStart(From,Event,To) + self:I({From,Event,To}) + self:_StartIntel() + self:_CreateAirwings() + self:_CreateSquads() + self:_SetCAPPatrolPoints() + self:_SetTankerPatrolPoints() + self:__Status(-10) + return self +end + +--- (Internal) FSM Function onbeforeStatus +-- @param #EASYGCICAP self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #EASYGCICAP self +function EASYGCICAP:onbeforeStatus(From,Event,To) + self:T({From,Event,To}) + if self:GetState() == "Stopped" then return false end + return self +end + +--- (Internal) FSM Function onafterStatus +-- @param #EASYGCICAP self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #EASYGCICAP self +function EASYGCICAP:onafterStatus(From,Event,To) + self:I({From,Event,To}) + -- Gather Some Stats + local function counttable(tbl) + local count = 0 + for _,_data in pairs(tbl) do + count = count + 1 + end + return count + end + local wings = counttable(self.ManagedAW) + local squads = counttable(self.ManagedSQ) + local caps = counttable(self.ManagedCP) + local assets = 0 + local instock = 0 + for _,_wing in pairs(self.wings) do + local count = _wing[1]:CountAssetsOnMission(MissionTypes,Cohort) + local count2 = _wing[1]:CountAssets(true,MissionTypes,Attributes) + assets = assets + count + instock = instock + count2 + end + if self.debug then + self:I(self.lid.."Wings: "..wings.." | Squads: "..squads.." | CapPoints: "..caps.." | Assets on Mission: "..assets.." | Assets in Stock: "..instock) + end + self:__Status(30) + return self +end + +--- (Internal) FSM Function onafterStop +-- @param #EASYGCICAP self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #EASYGCICAP self +function EASYGCICAP:onafterStop(From,Event,To) + self:I({From,Event,To}) + self.Intel:Stop() + return self +end From 0477ef669a4482f3d6010df12dd0b1df59c5385c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 26 Sep 2023 13:29:35 +0200 Subject: [PATCH 360/603] docu --- Moose Development/Moose/Ops/EasyGCICAP.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 935e517a3..e3cc625d8 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -8,14 +8,14 @@ ------------------------------------------------------------------------- -- Date: September 2023 ------------------------------------------------------------------------- ----@diagnostic disable: cast-local-type +-- --- **Ops** - Easy GCI & CAP Manager -- -- === -- -- **Main Features:** -- --- * Automatically create and manage A2A CAP/GCI defenses using an @{Ops.AirWing#AIRWING} and Squadrons for one coalition +-- * Automatically create and manage A2A CAP/GCI defenses using an AirWing and Squadrons for one coalition -- * Easy set-up -- * Add additional AirWings on other airbases -- * Each wing can have more than one Squadron - tasking to Squadrons is done on a random basis per AirWing From 6f673583ab17d08c303748326fa8b1da26c5cb98 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 27 Sep 2023 15:39:51 +0200 Subject: [PATCH 361/603] pos --- Moose Development/Moose/Wrapper/Positionable.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 57e50c7be..3da4ae318 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -671,7 +671,7 @@ function POSITIONABLE:GetBoundingRadius( MinDist ) return math.max( math.max( CX, CZ ), boxmin ) end - BASE:E( { "Cannot GetBoundingRadius", Positionable = self, Alive = self:IsAlive() } ) + BASE:T( { "Cannot GetBoundingRadius", Positionable = self, Alive = self:IsAlive() } ) return nil end From 258f9f7bb7b1188f0e9069472f79540589fb37b7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 27 Sep 2023 15:40:44 +0200 Subject: [PATCH 362/603] Zones --- Moose Development/Moose/Core/Zone.lua | 52 ++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 5ded3fe7d..2d2ccaecb 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -196,7 +196,7 @@ end --- Returns if a PointVec2 is within the zone. (Name is misleading, actually takes a #COORDINATE) -- @param #ZONE_BASE self --- @param Core.Point#COORDINATE PointVec2 The coordinate to test. +-- @param Core.Point#COORDINATE Coordinate The coordinate to test. -- @return #boolean true if the PointVec2 is within the zone. function ZONE_BASE:IsPointVec2InZone( Coordinate ) local InZone = self:IsVec2InZone( Coordinate:GetVec2() ) @@ -1443,8 +1443,11 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma local T1 = timer.getTime() local buildings = {} + local buildingzones = {} + if self.ScanData and self.ScanData.BuildingCoordinates then buildings = self.ScanData.BuildingCoordinates + buildingzones = self.ScanData.BuildingZones else -- build table of buildings coordinates for _,_object in pairs (objects) do @@ -1456,28 +1459,32 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma MARKER:New(scenery:GetCoordinate(),"Building"):ToAll() end buildings[#buildings+1] = scenery:GetCoordinate() + local bradius = scenery:GetBoundingRadius() or dist + local bzone = ZONE_RADIUS:New("Building-"..math.random(1,100000),scenery:GetVec2(),bradius,false) + buildingzones[#buildingzones+1] = bzone + --bzone:DrawZone(-1,{1,0,0},Alpha,FillColor,FillAlpha,1,ReadOnly) end end end self.ScanData.BuildingCoordinates = buildings + self.ScanData.BuildingZones = buildingzones end -- max 1000 tries local rcoord = nil - local found = false + local found = true local iterations = 0 for i=1,1000 do iterations = iterations + 1 rcoord = self:GetRandomCoordinate(inner,outer) - found = false - for _,_coord in pairs (buildings) do - local coord = _coord -- Core.Point#COORDINATE + found = true + for _,_coord in pairs (buildingzones) do + local zone = _coord -- Core.Zone#ZONE_RADIUS -- keep >50m dist from buildings - if coord:Get3DDistance(rcoord) > dist then - found = true - else + if zone:IsPointVec2InZone(rcoord) then found = false + break end end if found then @@ -1489,9 +1496,36 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma end end + if not found then + -- max 1000 tries + local rcoord = nil + local found = true + local iterations = 0 + + for i=1,1000 do + iterations = iterations + 1 + rcoord = self:GetRandomCoordinate(inner,outer) + found = true + for _,_coord in pairs (buildings) do + local coord = _coord -- Core.Point#COORDINATE + -- keep >50m dist from buildings + if coord:Get3DDistance(rcoord) < dist then + found = false + end + end + if found then + -- we have a winner! + if markfinal then + MARKER:New(rcoord,"FREE"):ToAll() + end + break + end + end + end + T1=timer.getTime() - self:T(string.format("Found a coordinate: %s | Iterations: %d | Time: %d",tostring(found),iterations,T1-T0)) + self:T(string.format("Found a coordinate: %s | Iterations: %d | Time: %.3f",tostring(found),iterations,T1-T0)) if found then return rcoord else return nil end From 3ea0cc64279eb488534c66da183cd45de0c46a79 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 27 Sep 2023 18:07:34 +0200 Subject: [PATCH 363/603] #EasyGCICAP * Added success criteria if intruder leaves monitored zones --- Moose Development/Moose/Core/Zone.lua | 1 + Moose Development/Moose/Ops/EasyGCICAP.lua | 147 +++++++++++++++------ 2 files changed, 105 insertions(+), 43 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 2d2ccaecb..a6c975b79 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -190,6 +190,7 @@ end -- @param Core.Point#COORDINATE Coordinate The coordinate to test. -- @return #boolean true if the coordinate is within the zone. function ZONE_BASE:IsCoordinateInZone( Coordinate ) + if not Coordinate then return false end local InZone = self:IsVec2InZone( Coordinate:GetVec2() ) return InZone end diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index e3cc625d8..5d1d4c0a8 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -57,6 +57,10 @@ -- @field #table ManagedTK -- @field #number MaxAliveMissions -- @field #boolean debug +-- @field #number repeatsonfailure +-- @field Core.Set#SET_ZONE GoZoneSet +-- @field Core.Set#SET_ZONE NoGoZoneSet +-- @field #boolean Monitor -- @extends Core.Fsm#FSM --- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown. @@ -150,6 +154,7 @@ -- * @{#EASYGCICAP.SetDefaultNumberAlter5Standby}: Set how many planes will be spawned on cold standby (Alert5), default 2. -- * @{#EASYGCICAP.SetDefaultEngageRange}: Set max engage range for CAP flights if they detect intruders, defaults to 50. -- * @{#EASYGCICAP.SetMaxAliveMissions}: Set max parallel missions can be done (CAP+GCI+Alert5+Tanker), defaults to 6. +-- * @{#EASYGCICAP.SetDefaultRepeatOnFailure}: Set max repeats on failure for intercepting/killing intruders, defaults to 3. -- -- -- @field #EASYGCICAP @@ -177,8 +182,12 @@ EASYGCICAP = { ManagedCP = {}, ManagedTK = {}, MaxAliveMissions = 6, - debug = true, + debug = false, engagerange = 50, + repeatsonfailure = 3, + GoZoneSet = nil, + NoGoZoneSet = nil, + Monitor = false, } --- Internal Squadron data type @@ -209,7 +218,7 @@ EASYGCICAP = { --- EASYGCICAP class version. -- @field #string version -EASYGCICAP.version="0.0.4" +EASYGCICAP.version="0.0.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -241,8 +250,8 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName) --self.CapZoneName = CapZoneName self.airbasename = AirbaseName self.airbase = AIRBASE:FindByName(self.airbasename) - self.BlueGoZoneSet = SET_ZONE:New() - self.BlueNoGoZoneSet = SET_ZONE:New() + self.GoZoneSet = SET_ZONE:New() + self.NoGoZoneSet = SET_ZONE:New() self.resurrection = 900 self.capspeed = 300 self.capalt = 25000 @@ -253,6 +262,8 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName) self.noaltert5 = 2 self.MaxAliveMissions = 6 self.engagerange = 50 + self.repeatsonfailure = 3 + self.Monitor = false -- Set some string id for output to DCS.log file. self.lid=string.format("EASYGCICAP %s | ", self.alias) @@ -283,7 +294,7 @@ end -- @param #number Maxiumum Maxmimum number of parallel missions allowed. Count is Cap-Missions + Intercept-Missions + Alert5-Missionsm default is 6 -- @return #EASYGCICAP self function EASYGCICAP:SetMaxAliveMissions(Maxiumum) - self:I(self.lid.."SetDefaultResurrection") + self:T(self.lid.."SetDefaultResurrection") self.MaxAliveMissions = Maxiumum or 6 return self end @@ -293,17 +304,27 @@ end -- @param #number Seconds Seconds, defaults to 900 -- @return #EASYGCICAP self function EASYGCICAP:SetDefaultResurrection(Seconds) - self:I(self.lid.."SetDefaultResurrection") + self:T(self.lid.."SetDefaultResurrection") self.resurrection = Seconds or 900 return self end +--- Add default repeat attempts if an Intruder intercepts fails. +-- @param #EASYGCICAP self +-- @param #number Retries Retries, defaults to 3 +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultRepeatOnFailure(Retries) + self:T(self.lid.."SetDefaultRepeatOnFailure") + self.repeatsonfailure = Retries or 3 + return self +end + --- Set default CAP Speed in knots -- @param #EASYGCICAP self -- @param #number Speed Speed defaults to 300 -- @return #EASYGCICAP self function EASYGCICAP:SetDefaultCAPSpeed(Speed) - self:I(self.lid.."SetDefaultSpeed") + self:T(self.lid.."SetDefaultSpeed") self.capspeed = Speed or 300 return self end @@ -313,7 +334,7 @@ end -- @param #number Altitude Altitude defaults to 25000 -- @return #EASYGCICAP self function EASYGCICAP:SetDefaultCAPAlt(Altitude) - self:I(self.lid.."SetDefaultAltitude") + self:T(self.lid.."SetDefaultAltitude") self.capalt = Altitude or 25000 return self end @@ -323,7 +344,7 @@ end -- @param #number Direction Direction defaults to 90 (East) -- @return #EASYGCICAP self function EASYGCICAP:SetDefaultCAPDirection(Direction) - self:I(self.lid.."SetDefaultDirection") + self:T(self.lid.."SetDefaultDirection") self.capdir = Direction or 90 return self end @@ -333,7 +354,7 @@ end -- @param #number Leg Leg defaults to 15 -- @return #EASYGCICAP self function EASYGCICAP:SetDefaultCAPLeg(Leg) - self:I(self.lid.."SetDefaultLeg") + self:T(self.lid.."SetDefaultLeg") self.capleg = Leg or 15 return self end @@ -343,7 +364,7 @@ end -- @param #number Grouping Grouping defaults to 2 -- @return #EASYGCICAP self function EASYGCICAP:SetDefaultCAPGrouping(Grouping) - self:I(self.lid.."SetDefaultCAPGrouping") + self:T(self.lid.."SetDefaultCAPGrouping") self.capgrouping = Grouping or 2 return self end @@ -353,7 +374,7 @@ end -- @param #number Range Range defaults to 100 NM -- @return #EASYGCICAP self function EASYGCICAP:SetDefaultMissionRange(Range) - self:I(self.lid.."SetDefaultMissionRange") + self:T(self.lid.."SetDefaultMissionRange") self.missionrange = Range or 100 return self end @@ -363,7 +384,7 @@ end -- @param #number Airframes defaults to 2 -- @return #EASYGCICAP selfAirframes function EASYGCICAP:SetDefaultNumberAlter5Standby(Airframes) - self:I(self.lid.."SetDefaultNumberAlter5Standby") + self:T(self.lid.."SetDefaultNumberAlter5Standby") self.noaltert5 = math.abs(Airframes) or 2 return self end @@ -373,7 +394,7 @@ end -- @param #number Range defaults to 50NM -- @return #EASYGCICAP selfAirframes function EASYGCICAP:SetDefaultEngageRange(Range) - self:I(self.lid.."SetDefaultNumberAlter5Standby") + self:T(self.lid.."SetDefaultNumberAlter5Standby") self.engagerange = Range or 50 return self end @@ -384,7 +405,7 @@ end -- @param #string Alias -- @return #EASYGCICAP self function EASYGCICAP:AddAirwing(Airbasename, Alias) - self:I(self.lid.."AddAirwing "..Airbasename) + self:T(self.lid.."AddAirwing "..Airbasename) -- Create Airwing data entry local AWEntry = {} -- #EASYGCICAP.Wing @@ -401,7 +422,7 @@ end -- @param #EASYGCICAP self -- @return #EASYGCICAP self function EASYGCICAP:_CreateAirwings() - self:I(self.lid.."_CreateAirwings") + self:T(self.lid.."_CreateAirwings") for airbase,data in pairs(self.ManagedAW) do local wing = data -- #EASYGCICAP.Wing local afb = wing.AirbaseName @@ -418,7 +439,7 @@ end -- @param #string Alias -- @return #EASYGCICAP self function EASYGCICAP:_AddAirwing(Airbasename, Alias) - self:I(self.lid.."_AddAirwing "..Airbasename) + self:T(self.lid.."_AddAirwing "..Airbasename) -- Create Airwing local CAP_Wing = AIRWING:New(Airbasename,Alias) @@ -444,7 +465,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) flightgroup:SetDespawnAfterHolding() flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename)) flightgroup:GetGroup():CommandEPLRS(true,5) - flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.BlueGoZoneSet,self.BlueNoGoZoneSet) + flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet) flightgroup:GetGroup():OptionROTEvadeFire() flightgroup:SetOutOfAAMRTB() flightgroup:SetFuelLowRTB(true) @@ -477,7 +498,7 @@ end -- @param #number LegLength Defaults to 15 NM. -- @return #EASYGCICAP self function EASYGCICAP:AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) - self:I(self.lid.."AddPatrolPointCAP "..Coordinate:ToStringLLDDM()) + self:T(self.lid.."AddPatrolPointCAP "..Coordinate:ToStringLLDDM()) local EntryCAP = {} -- #EASYGCICAP.CapPoint EntryCAP.AirbaseName = AirbaseName EntryCAP.Coordinate = Coordinate @@ -502,7 +523,7 @@ end -- @param #number LegLength Defaults to 15 NM. -- @return #EASYGCICAP self function EASYGCICAP:AddPatrolPointTanker(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) - self:I(self.lid.."AddPatrolPointTanker "..Coordinate:ToStringLLDDM()) + self:T(self.lid.."AddPatrolPointTanker "..Coordinate:ToStringLLDDM()) local EntryCAP = {} -- #EASYGCICAP.CapPoint EntryCAP.AirbaseName = AirbaseName EntryCAP.Coordinate = Coordinate @@ -521,7 +542,7 @@ end -- @param #EASYGCICAP self -- @return #EASYGCICAP self function EASYGCICAP:_SetTankerPatrolPoints() - self:I(self.lid.."_SetTankerPatrolPoints") + self:T(self.lid.."_SetTankerPatrolPoints") for _,_data in pairs(self.ManagedTK) do local data = _data --#EASYGCICAP.CapPoint local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING @@ -540,7 +561,7 @@ end -- @param #EASYGCICAP self -- @return #EASYGCICAP self function EASYGCICAP:_SetCAPPatrolPoints() - self:I(self.lid.."_SetCAPPatrolPoints") + self:T(self.lid.."_SetCAPPatrolPoints") for _,_data in pairs(self.ManagedCP) do local data = _data --#EASYGCICAP.CapPoint local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING @@ -565,7 +586,7 @@ end -- @param #number LegLength Defaults to 15 NM. -- @return #EASYGCICAP self function EASYGCICAP:_AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) - self:I(self.lid.."_AddPatrolPointCAP") + self:T(self.lid.."_AddPatrolPointCAP") local airbasename = AirbaseName or self.airbasename local coordinate = Coordinate local Altitude = Altitude or 25000 @@ -581,7 +602,7 @@ end -- @param #EASYGCICAP self -- @return #EASYGCICAP self function EASYGCICAP:_CreateSquads() - self:I(self.lid.."_CreateSquads") + self:T(self.lid.."_CreateSquads") for name,data in pairs(self.ManagedSQ) do local squad = data -- #EASYGCICAP.Squad local SquadName = name @@ -611,7 +632,7 @@ end -- @param #string Livery (optional) Livery name to be used. -- @return #EASYGCICAP self function EASYGCICAP:AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) - self:I(self.lid.."AddSquadron "..SquadName) + self:T(self.lid.."AddSquadron "..SquadName) -- Add Squadron Data local EntrySQ = {} -- #EASYGCICAP.Squad EntrySQ.TemplateName = TemplateName @@ -638,7 +659,7 @@ end -- @param #string Livery (optional) Livery name to be used. -- @return #EASYGCICAP self function EASYGCICAP:AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) - self:I(self.lid.."AddTankerSquadron "..SquadName) + self:T(self.lid.."AddTankerSquadron "..SquadName) -- Add Squadron Data local EntrySQ = {} -- #EASYGCICAP.Squad EntrySQ.TemplateName = TemplateName @@ -666,7 +687,7 @@ end -- @param #string Livery (optional) Livery name to be used. -- @return #EASYGCICAP self function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) - self:I(self.lid.."_AddSquadron "..SquadName) + self:T(self.lid.."_AddSquadron "..SquadName) -- Add Squadrons local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) Squadron_One:AddMissionCapability({AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.ALERT5}) @@ -697,7 +718,7 @@ end -- @param #string Livery (optional) Livery name to be used. -- @return #EASYGCICAP self function EASYGCICAP:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) - self:I(self.lid.."_AddTankerSquadron "..SquadName) + self:T(self.lid.."_AddTankerSquadron "..SquadName) -- Add Squadrons local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) Squadron_One:AddMissionCapability({AUFTRAG.Type.TANKER}) @@ -722,8 +743,8 @@ end -- @param Core.Zone#ZONE_BASE Zone -- @return #EASYGCICAP self function EASYGCICAP:AddAcceptZone(Zone) - self:I(self.lid.."AddAcceptZone0") - self.BlueGoZoneSet:AddZone(Zone) + self:T(self.lid.."AddAcceptZone0") + self.GoZoneSet:AddZone(Zone) return self end @@ -732,8 +753,8 @@ end -- @param Core.Zone#ZONE_BASE Zone -- @return #EASYGCICAP self function EASYGCICAP:AddRejectZone(Zone) - self:I(self.lid.."AddRejectZone") - self.BlueNoGoZoneSet:AddZone(Zone) + self:T(self.lid.."AddRejectZone") + self.NoGoZoneSet:AddZone(Zone) return self end @@ -741,7 +762,7 @@ end -- @param #EASYGCICAP self -- @return #EASYGCICAP self function EASYGCICAP:_StartIntel() - self:I(self.lid.."_StartIntel") + self:T(self.lid.."_StartIntel") -- Border GCI Detection local BlueAir_DetectionSetGroup = SET_GROUP:New() BlueAir_DetectionSetGroup:FilterPrefixes( { self.EWRName } ) @@ -751,8 +772,8 @@ function EASYGCICAP:_StartIntel() local BlueIntel = INTEL:New(BlueAir_DetectionSetGroup,self.coalitionname, self.EWRName) BlueIntel:SetClusterAnalysis(true,false,false) BlueIntel:SetForgetTime(300) - BlueIntel:SetAcceptZones(self.BlueGoZoneSet) - BlueIntel:SetRejectZones(self.BlueNoGoZoneSet) + BlueIntel:SetAcceptZones(self.GoZoneSet) + BlueIntel:SetRejectZones(self.NoGoZoneSet) BlueIntel:SetVerbosity(0) BlueIntel:Start() @@ -766,10 +787,12 @@ function EASYGCICAP:_StartIntel() local capspeed = self.capspeed + 100 local capalt = self.capalt local maxsize = self.maxinterceptsize + local repeatsonfailure = self.repeatsonfailure local wings = self.wings local ctlpts = self.ManagedCP local MaxAliveMissions = self.MaxAliveMissions * self.capgrouping + local nogozoneset = self.NoGoZoneSet function BlueIntel:OnAfterNewCluster(From,Event,To,Cluster) -- Aircraft? @@ -816,20 +839,33 @@ function EASYGCICAP:_StartIntel() local airframes = airwing:CountAssets(true) if distance < bestdistance and airframes >= wingsize then bestdistance = distance - targetairwing = airwing + targetairwing = airwing -- Ops.AirWing#AIRWING targetawname = name end end local text = string.format("Closest Airwing is %s", targetawname) - local m = MESSAGE:New(text,10,"CAPGCI"):ToAll():ToLog() + local m = MESSAGE:New(text,10,"CAPGCI"):ToAllIf(self.debug):ToLog() -- Do we have a matching airwing? if targetairwing then local AssetCount = targetairwing:CountAssetsOnMission(MissionTypes,Cohort) - --local AssetCount = targetairwing:GetAssetsOnMission({AUFTRAG.Type.INTERCEPT}) + --[[ + local Assets = targetairwing:GetAssetsOnMission(AUFTRAG.Type.GCICAP) + for _,_asset in pairs(Assets) do + local asset = _asset -- Functional.Warehouse#WAREHOUSE.Assetitem + local fg = asset.flightgroup + local name = asset.spawngroupname + local mission = fg:GetMissionCurrent() + local mtype = mission.type + local distance = position:Get3DDistance(fg:GetCoordinate()) or 1000*1000 + distance = distance / 1000 + local text = string.format("FlightGroup %s on mission %s with distance %d km",name,mtype,distance) + local m = MESSAGE:New(text,15,"GCICAP"):ToAllIf(self.debug):ToLog() + end + --]] -- Enough airframes on mission already? - self:I(self.lid.." Assets on Mission "..AssetCount) + self:T(self.lid.." Assets on Mission "..AssetCount) if AssetCount <= MaxAliveMissions then - local repeats = math.random(1,2) + local repeats = repeatsonfailure local InterceptAuftrag = AUFTRAG:NewINTERCEPT(contact.group) :SetMissionRange(150) :SetPriority(1,true,1) @@ -837,6 +873,24 @@ function EASYGCICAP:_StartIntel() :SetRepeatOnFailure(repeats) :SetMissionSpeed(UTILS.KnotsToAltKIAS(capspeed,capalt)) :SetMissionAltitude(capalt) + + if nogozoneset:Count() > 0 then + InterceptAuftrag:AddConditionSuccess( + function(group,zoneset) + local success = false + if group and group:IsAlive() then + local coord = group:GetCoordinate() + if coord and zoneset:IsCoordinateInZone(coord) then + success = true + end + end + return success + end, + contact.group, + nogozoneset + ) + end + targetairwing:AddMission(InterceptAuftrag) Cluster.mission = InterceptAuftrag end @@ -860,7 +914,7 @@ end -- @param #string To -- @return #EASYGCICAP self function EASYGCICAP:onafterStart(From,Event,To) - self:I({From,Event,To}) + self:T({From,Event,To}) self:_StartIntel() self:_CreateAirwings() self:_CreateSquads() @@ -889,7 +943,7 @@ end -- @param #string To -- @return #EASYGCICAP self function EASYGCICAP:onafterStatus(From,Event,To) - self:I({From,Event,To}) + self:T({From,Event,To}) -- Gather Some Stats local function counttable(tbl) local count = 0 @@ -912,6 +966,13 @@ function EASYGCICAP:onafterStatus(From,Event,To) if self.debug then self:I(self.lid.."Wings: "..wings.." | Squads: "..squads.." | CapPoints: "..caps.." | Assets on Mission: "..assets.." | Assets in Stock: "..instock) end + if self.Monitor then + local threatcount = #self.Intel.Clusters or 0 + local text = "GCICAP "..self.alias + text = text.."\nWings: "..wings.."\nSquads: "..squads.."\nCapPoints: "..caps.."\nAssets on Mission: "..assets.."\nAssets in Stock: "..instock + text = text.."\nThreats:"..threatcount + MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug) + end self:__Status(30) return self end @@ -923,7 +984,7 @@ end -- @param #string To -- @return #EASYGCICAP self function EASYGCICAP:onafterStop(From,Event,To) - self:I({From,Event,To}) + self:T({From,Event,To}) self.Intel:Stop() return self end From 98a11a8aad44468cdf07a7562f8813c534cf3e13 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 28 Sep 2023 13:15:27 +0200 Subject: [PATCH 364/603] #EasyGCICAP * Added option for an AWACS patrol point and Squad * Added option to make Tanker and AWACS invisible --- Moose Development/Moose/Ops/EasyGCICAP.lua | 210 +++++++++++++++++++-- 1 file changed, 190 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 5d1d4c0a8..2eac3c3c7 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -55,12 +55,14 @@ -- @field #table ManagedSQ -- @field #table ManagedCP -- @field #table ManagedTK +-- @field #table ManagedEWR -- @field #number MaxAliveMissions -- @field #boolean debug -- @field #number repeatsonfailure -- @field Core.Set#SET_ZONE GoZoneSet -- @field Core.Set#SET_ZONE NoGoZoneSet -- @field #boolean Monitor +-- @field #boolean TankerInvisible -- @extends Core.Fsm#FSM --- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown. @@ -137,8 +139,15 @@ -- -- **Note** If you need different tanker types, i.e. Boom and Drogue, set them up at different AirWings! -- -- Add a tanker point -- mywing:AddPatrolPointTanker(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone Tanker"):GetCoordinate(),20000,280,270,50) --- -- Add a tanker squad --- mywing:AddTankerSquadron("Blue Tanker","Tanker Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.EXCELLENT,602) +-- -- Add an AWACS squad - Radio 251 AM, TACAN 51Y +-- mywing:AddTankerSquadron("Blue Tanker","Tanker Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.EXCELLENT,602,nil,251,radio.modulation.AM,51) +-- +-- ### Add an AWACS (optional) +-- +-- -- Add an AWACS point +-- mywing:AddPatrolPointAwacs(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone AWACS"):GetCoordinate(),25000,300,270,50) +-- -- Add a tanker squad - Radio 251 AM, TACAN 51Y +-- mywing:AddAWACSSquadron("Blue AWACS","AWACS Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.AVERAGE,702,nil,271,radio.modulation.AM) -- -- # Fine-Tuning -- @@ -153,8 +162,9 @@ -- * @{#EASYGCICAP.SetDefaultMissionRange}: Set how many NM the planes can go from the home base, defaults to 100. -- * @{#EASYGCICAP.SetDefaultNumberAlter5Standby}: Set how many planes will be spawned on cold standby (Alert5), default 2. -- * @{#EASYGCICAP.SetDefaultEngageRange}: Set max engage range for CAP flights if they detect intruders, defaults to 50. --- * @{#EASYGCICAP.SetMaxAliveMissions}: Set max parallel missions can be done (CAP+GCI+Alert5+Tanker), defaults to 6. +-- * @{#EASYGCICAP.SetMaxAliveMissions}: Set max parallel missions can be done (CAP+GCI+Alert5+Tanker+AWACS), defaults to 8. -- * @{#EASYGCICAP.SetDefaultRepeatOnFailure}: Set max repeats on failure for intercepting/killing intruders, defaults to 3. +-- * @{#EASYGCICAP.SetTankerAndAWACSInvisible}: Set Tanker and AWACS to be invisible to enemy AI eyes. Is set to `true` by default. -- -- -- @field #EASYGCICAP @@ -181,13 +191,15 @@ EASYGCICAP = { ManagedSQ = {}, ManagedCP = {}, ManagedTK = {}, - MaxAliveMissions = 6, + ManagedEWR = {}, + MaxAliveMissions = 8, debug = false, engagerange = 50, repeatsonfailure = 3, GoZoneSet = nil, NoGoZoneSet = nil, Monitor = false, + TankerInvisible = true, } --- Internal Squadron data type @@ -200,6 +212,10 @@ EASYGCICAP = { -- @field #string Modex -- @field #string Livery -- @field #boolean Tanker +-- @field #boolean AWACS +-- @field #number Frequency +-- @field #number Modulation +-- @field #number TACAN --- Internal Wing data type -- @type EASYGCICAP.Wing @@ -218,7 +234,7 @@ EASYGCICAP = { --- EASYGCICAP class version. -- @field #string version -EASYGCICAP.version="0.0.7" +EASYGCICAP.version="0.0.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -260,10 +276,11 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName) self.capgrouping = 2 self.missionrange = 100 self.noaltert5 = 2 - self.MaxAliveMissions = 6 + self.MaxAliveMissions = 8 self.engagerange = 50 self.repeatsonfailure = 3 self.Monitor = false + self.TankerInvisible = true -- Set some string id for output to DCS.log file. self.lid=string.format("EASYGCICAP %s | ", self.alias) @@ -289,13 +306,23 @@ end ------------------------------------------------------------------------- +--- Set Tanker and AWACS to be invisible to enemy AI eyes +-- @param #EASYGCICAP self +-- @param #boolean Switch Set to true or false, by default this is set to true already +-- @return #EASYGCICAP self +function EASYGCICAP:SetTankerAndAWACSInvisible(Switch) + self:T(self.lid.."SetTankerAndAWACSInvisible") + self.TankerInvisible = Switch + return self +end + --- Set Maximum of alive missions to stop airplanes spamming the map -- @param #EASYGCICAP self -- @param #number Maxiumum Maxmimum number of parallel missions allowed. Count is Cap-Missions + Intercept-Missions + Alert5-Missionsm default is 6 -- @return #EASYGCICAP self function EASYGCICAP:SetMaxAliveMissions(Maxiumum) self:T(self.lid.."SetDefaultResurrection") - self.MaxAliveMissions = Maxiumum or 6 + self.MaxAliveMissions = Maxiumum or 8 return self end @@ -450,6 +477,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) CAP_Wing:SetNumberCAP(self.capgrouping) CAP_Wing:SetNumberTankerBoom(1) CAP_Wing:SetNumberTankerProbe(1) + CAP_Wing:SetNumberAWACS(1) --local PatrolCoordinateKutaisi = ZONE:New(CapZoneName):GetCoordinate() --CAP_Wing:AddPatrolPointCAP(PatrolCoordinateKutaisi,self.capalt,UTILS.KnotsToAltKIAS(self.capspeed,self.capalt),self.capdir,self.capleg) CAP_Wing:SetTakeoffHot() @@ -459,20 +487,29 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) local Intel = self.Intel + local TankerInvisible = self.TankerInvisible + function CAP_Wing:OnAfterFlightOnMission(From, Event, To, Flightgroup, Mission) local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP --flightgroup:SetDespawnAfterLanding() flightgroup:SetDespawnAfterHolding() flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename)) flightgroup:GetGroup():CommandEPLRS(true,5) - flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet) - flightgroup:GetGroup():OptionROTEvadeFire() - flightgroup:SetOutOfAAMRTB() + if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS then + flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet) + flightgroup:SetOutOfAAMRTB() + end + if Mission.type == AUFTRAG.Type.TANKER or Mission.type == AUFTRAG.Type.AWACS then + if TankerInvisible then + flightgroup:GetGroup():SetCommandInvisible(true) + end + end + flightgroup:GetGroup():OptionROTEvadeFire() flightgroup:SetFuelLowRTB(true) Intel:AddAgent(flightgroup) - --function flightgroup:OnAfterHolding(From,Event,To) - --self:ClearToLand(5) - --end + function flightgroup:OnAfterHolding(From,Event,To) + self:ClearToLand(5) + end end @@ -538,6 +575,31 @@ function EASYGCICAP:AddPatrolPointTanker(AirbaseName,Coordinate,Altitude,Speed,H return self end +--- Add an AWACS patrol point to a Wing +-- @param #EASYGCICAP self +-- @param #string AirbaseName Name of the Wing's airbase +-- @param Core.Point#COORDINATE Coordinate. +-- @param #number Altitude Defaults to 25000 feet. +-- @param #number Speed Defaults to 300 knots. +-- @param #number Heading Defaults to 90 degrees (East). +-- @param #number LegLength Defaults to 15 NM. +-- @return #EASYGCICAP self +function EASYGCICAP:AddPatrolPointAwacs(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) + self:T(self.lid.."AddPatrolPointAwacs "..Coordinate:ToStringLLDDM()) + local EntryCAP = {} -- #EASYGCICAP.CapPoint + EntryCAP.AirbaseName = AirbaseName + EntryCAP.Coordinate = Coordinate + EntryCAP.Altitude = Altitude or 25000 + EntryCAP.Speed = Speed or 300 + EntryCAP.Heading = Heading or 90 + EntryCAP.LegLength = LegLength or 15 + self.ManagedEWR[#self.ManagedEWR+1] = EntryCAP + if self.debug then + local mark = MARKER:New(Coordinate,self.lid.."Patrol Point AWACS"):ToAll() + end + return self +end + --- (Internal) Set actual Tanker Points from the list -- @param #EASYGCICAP self -- @return #EASYGCICAP self @@ -557,6 +619,25 @@ function EASYGCICAP:_SetTankerPatrolPoints() return self end +--- (Internal) Set actual Awacs Points from the list +-- @param #EASYGCICAP self +-- @return #EASYGCICAP self +function EASYGCICAP:_SetAwacsPatrolPoints() + self:T(self.lid.."_SetAwacsPatrolPoints") + for _,_data in pairs(self.ManagedEWR) do + local data = _data --#EASYGCICAP.CapPoint + local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING + local Coordinate = data.Coordinate + local Altitude = data.Altitude + local Speed = data.Speed + local Heading = data.Heading + local LegLength = data.LegLength + Wing:AddPatrolPointAWACS(Coordinate,Altitude,Speed,Heading,LegLength) + end + + return self +end + --- (Internal) Set actual PatrolPoints from the list -- @param #EASYGCICAP self -- @return #EASYGCICAP self @@ -612,8 +693,13 @@ function EASYGCICAP:_CreateSquads() local Skill = squad.Skill local Modex = squad.Modex local Livery = squad.Livery + local Frequeny = squad.Frequency + local Modulation = squad.Modulation + local TACAN = squad.TACAN if squad.Tanker then - self:_AddTankerSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery) + self:_AddTankerSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequeny,Modulation,TACAN) + elseif squad.AWACS then + self:_AddAWACSSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequeny,Modulation) else self:_AddSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery) end @@ -657,8 +743,11 @@ end -- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE -- @param #string Modex (optional) Modex to be used,e.g. 402. -- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio Frequency to be used. +-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM +-- @param #number TACAN (optional) TACAN channel, e.g. 71, resulting in Channel 71Y -- @return #EASYGCICAP self -function EASYGCICAP:AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) +function EASYGCICAP:AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation, TACAN) self:T(self.lid.."AddTankerSquadron "..SquadName) -- Add Squadron Data local EntrySQ = {} -- #EASYGCICAP.Squad @@ -667,8 +756,11 @@ function EASYGCICAP:AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirF EntrySQ.AirbaseName = AirbaseName EntrySQ.AirFrames = AirFrames or 20 EntrySQ.Skill = Skill or AI.Skill.AVERAGE - EntrySQ.Modex = Modex or 402 + EntrySQ.Modex = Modex or 602 EntrySQ.Livery = Livery + EntrySQ.Frequency = Frequency + EntrySQ.Modulation = Livery + EntrySQ.TACAN = TACAN EntrySQ.Tanker = true self.ManagedSQ[SquadName] = EntrySQ @@ -676,6 +768,38 @@ function EASYGCICAP:AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirF return self end +--- Add an AWACS Squadron to an Airwing of the manager +-- @param #EASYGCICAP self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio Frequency to be used. +-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM +-- @return #EASYGCICAP self +function EASYGCICAP:AddAWACSSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation) + self:T(self.lid.."AddAWACSSquadron "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYGCICAP.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 702 + EntrySQ.Livery = Livery + EntrySQ.Frequency = Frequency + EntrySQ.Modulation = Livery + EntrySQ.AWACS = true + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + --- (Internal) Add a Squadron to an Airwing of the manager -- @param #EASYGCICAP self -- @param #string TemplateName Name of the group template. @@ -685,8 +809,10 @@ end -- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE -- @param #string Modex (optional) Modex to be used,e.g. 402. -- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio Frequency to be used. +-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM -- @return #EASYGCICAP self -function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) +function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation) self:T(self.lid.."_AddSquadron "..SquadName) -- Add Squadrons local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) @@ -716,8 +842,11 @@ end -- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE -- @param #string Modex (optional) Modex to be used,e.g. 402. -- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio frequency of the Tanker +-- @param #number Modulation (Optional) Radio modulation of the Tanker +-- @param #number TACAN (Optional) TACAN Channel to be used, will always be an "Y" channel -- @return #EASYGCICAP self -function EASYGCICAP:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) +function EASYGCICAP:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation, TACAN) self:T(self.lid.."_AddTankerSquadron "..SquadName) -- Add Squadrons local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) @@ -729,6 +858,8 @@ function EASYGCICAP:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, Air Squadron_One:SetLivery(Livery) Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) Squadron_One:SetMissionRange(self.missionrange) + Squadron_One:SetRadio(Frequency,Modulation) + Squadron_One:AddTacanChannel(TACAN,TACAN) local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING @@ -738,6 +869,39 @@ function EASYGCICAP:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, Air return self end +--- (Internal) Add a AWACS Squadron to an Airwing of the manager +-- @param #EASYGCICAP self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio frequency of the AWACS +-- @param #number Modulation (Optional) Radio modulation of the AWACS +-- @return #EASYGCICAP self +function EASYGCICAP:_AddAWACSSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation) + self:T(self.lid.."_AddAWACSSquadron "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.AWACS}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + Squadron_One:SetRadio(Frequency,Modulation) + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.AWACS},75) + + return self +end + --- Add a zone to the accepted zones set. -- @param #EASYGCICAP self -- @param Core.Zone#ZONE_BASE Zone @@ -809,7 +973,12 @@ function EASYGCICAP:_StartIntel() local clustersize = self:ClusterCountUnits(Cluster) or 1 local wingsize = math.abs(overhead * (clustersize+1)) if wingsize > maxsize then wingsize = maxsize end - if (not Cluster.mission) and (wingsize > 0) then + -- existing mission, and if so - done? + local retrymission = true + if Cluster.mission and (not Cluster.mission:IsOver()) then + retrymission = false + end + if (retrymission) and (wingsize >= 1) then MESSAGE:New(string.format("**** %s Interceptors need wingsize %d", UTILS.GetCoalitionName(self.coalition), wingsize),15,"CAPGCI"):ToAllIf(self.debug):ToLog() for _,_data in pairs (wings) do local airwing = _data[1] -- Ops.AirWing#AIRWING @@ -920,6 +1089,7 @@ function EASYGCICAP:onafterStart(From,Event,To) self:_CreateSquads() self:_SetCAPPatrolPoints() self:_SetTankerPatrolPoints() + self:_SetAwacsPatrolPoints() self:__Status(-10) return self end @@ -970,7 +1140,7 @@ function EASYGCICAP:onafterStatus(From,Event,To) local threatcount = #self.Intel.Clusters or 0 local text = "GCICAP "..self.alias text = text.."\nWings: "..wings.."\nSquads: "..squads.."\nCapPoints: "..caps.."\nAssets on Mission: "..assets.."\nAssets in Stock: "..instock - text = text.."\nThreats:"..threatcount + text = text.."\nThreats: "..threatcount MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug) end self:__Status(30) From d6e3caaa9b6f890351aa227727e011bc271ed0d5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 28 Sep 2023 13:28:49 +0200 Subject: [PATCH 365/603] #EasyGCICAP * Docu additions --- Moose Development/Moose/Ops/EasyGCICAP.lua | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 2eac3c3c7..5891b12de 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -165,6 +165,11 @@ -- * @{#EASYGCICAP.SetMaxAliveMissions}: Set max parallel missions can be done (CAP+GCI+Alert5+Tanker+AWACS), defaults to 8. -- * @{#EASYGCICAP.SetDefaultRepeatOnFailure}: Set max repeats on failure for intercepting/killing intruders, defaults to 3. -- * @{#EASYGCICAP.SetTankerAndAWACSInvisible}: Set Tanker and AWACS to be invisible to enemy AI eyes. Is set to `true` by default. +-- +-- ## Debug and Monitor +-- +-- mywing.debug = true -- log information +-- mywing.Monitor = true -- show some statistics on screen -- -- -- @field #EASYGCICAP @@ -1127,20 +1132,24 @@ function EASYGCICAP:onafterStatus(From,Event,To) local caps = counttable(self.ManagedCP) local assets = 0 local instock = 0 + local capmission = 0 + local interceptmission = 0 for _,_wing in pairs(self.wings) do local count = _wing[1]:CountAssetsOnMission(MissionTypes,Cohort) local count2 = _wing[1]:CountAssets(true,MissionTypes,Attributes) + capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.GCICAP}) + interceptmission = interceptmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.INTERCEPT}) assets = assets + count instock = instock + count2 end - if self.debug then - self:I(self.lid.."Wings: "..wings.." | Squads: "..squads.." | CapPoints: "..caps.." | Assets on Mission: "..assets.." | Assets in Stock: "..instock) - end if self.Monitor then local threatcount = #self.Intel.Clusters or 0 local text = "GCICAP "..self.alias text = text.."\nWings: "..wings.."\nSquads: "..squads.."\nCapPoints: "..caps.."\nAssets on Mission: "..assets.."\nAssets in Stock: "..instock text = text.."\nThreats: "..threatcount + text = text.."\Missions: "..capmission+interceptmission + text = text.."\ - CAP: "..capmission + text = text.."\ - Intercept: "..interceptmission MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug) end self:__Status(30) From 7adf99f82eb5a5f0ead151e0c3bc492d8380a8ad Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 5 Oct 2023 17:39:55 +0200 Subject: [PATCH 366/603] #INTEL * Added Conflict zones --- Moose Development/Moose/Ops/Intelligence.lua | 57 +++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index 0f71a234e..1dbaee076 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -26,6 +26,7 @@ -- @field #table filterCategoryGroup Filter for group categories. -- @field Core.Set#SET_ZONE acceptzoneset Set of accept zones. If defined, only contacts in these zones are considered. -- @field Core.Set#SET_ZONE rejectzoneset Set of reject zones. Contacts in these zones are not considered, even if they are in accept zones. +-- @field Core.Set#SET_ZONE conflictzoneset Set of conflict zones. Contacts in these zones are considered, even if they are not in accept zones or if they are in reject zones. -- @field #table Contacts Table of detected items. -- @field #table ContactsLost Table of lost detected items. -- @field #table ContactsUnknown Table of new detected items. @@ -159,13 +160,12 @@ INTEL.Ctype={ --- INTEL class version. -- @field #string version -INTEL.version="0.3.5" +INTEL.version="0.3.6" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Make forget times user input. Currently these are hard coded. -- TODO: Add min cluster size. Only create new clusters if they have a certain group size. -- TODO: process detected set asynchroniously for better performance. -- DONE: Add statics. @@ -266,6 +266,7 @@ function INTEL:New(DetectionSet, Coalition, Alias) self:SetForgetTime() self:SetAcceptZones() self:SetRejectZones() + self:SetConflictZones() ------------------------ --- Pseudo Functions --- @@ -416,7 +417,7 @@ function INTEL:RemoveAcceptZone(AcceptZone) end --- Set reject zones. Contacts detected in this/these zone(s) are rejected and not reported by the detection. --- Note that reject zones overrule accept zones, i.e. if a unit is inside and accept zone and inside a reject zone, it is rejected. +-- Note that reject zones overrule accept zones, i.e. if a unit is inside an accept zone and inside a reject zone, it is rejected. -- @param #INTEL self -- @param Core.Set#SET_ZONE RejectZoneSet Set of reject zone(s). -- @return #INTEL self @@ -426,7 +427,7 @@ function INTEL:SetRejectZones(RejectZoneSet) end --- Add a reject zone. Contacts detected in this zone are rejected and not reported by the detection. --- Note that reject zones overrule accept zones, i.e. if a unit is inside and accept zone and inside a reject zone, it is rejected. +-- Note that reject zones overrule accept zones, i.e. if a unit is inside an accept zone and inside a reject zone, it is rejected. -- @param #INTEL self -- @param Core.Zone#ZONE RejectZone Add a zone to the reject zone set. -- @return #INTEL self @@ -444,6 +445,36 @@ function INTEL:RemoveRejectZone(RejectZone) return self end +--- Set conflict zones. Contacts detected in this/these zone(s) are reported by the detection. +-- Note that conflict zones overrule all other zones, i.e. if a unit is outside of an accept zone and inside a reject zone, it is still reported if inside a conflict zone. +-- @param #INTEL self +-- @param Core.Set#SET_ZONE ConflictZoneSet Set of conflict zone(s). +-- @return #INTEL self +function INTEL:SetConflictZones(ConflictZoneSet) + self.conflictzoneset=ConflictZoneSet or SET_ZONE:New() + return self +end + +--- Add a conflict zone. Contacts detected in this zone are conflicted and not reported by the detection. +-- Note that conflict zones overrule all other zones, i.e. if a unit is outside of an accept zone and inside a reject zone, it is still reported if inside a conflict zone. +-- @param #INTEL self +-- @param Core.Zone#ZONE ConflictZone Add a zone to the conflict zone set. +-- @return #INTEL self +function INTEL:AddConflictZone(ConflictZone) + self.conflictzoneset:AddZone(ConflictZone) + return self +end + +--- Remove a conflict zone from the conflict zone set. +-- Note that conflict zones overrule all other zones, i.e. if a unit is outside of an accept zone and inside a reject zone, it is still reported if inside a conflict zone. +-- @param #INTEL self +-- @param Core.Zone#ZONE ConflictZone Remove a zone from the conflict zone set. +-- @return #INTEL self +function INTEL:RemoveConflictZone(ConflictZone) + self.conflictzoneset:Remove(ConflictZone:GetName(), true) + return self +end + --- **OBSOLETE, will be removed in next version!** Set forget contacts time interval. -- Previously known contacts that are not detected any more, are "lost" after this time. -- This avoids fast oscillations between a contact being detected and undetected. @@ -780,7 +811,19 @@ function INTEL:UpdateIntel() local remove={} for unitname,_unit in pairs(DetectedUnits) do local unit=_unit --Wrapper.Unit#UNIT - + + local inconflictzone=false + -- Check if unit is in any of the conflict zones. + if self.conflictzoneset:Count()>0 then + for _,_zone in pairs(self.conflictzoneset.Set) do + local zone=_zone --Core.Zone#ZONE + if unit:IsInZone(zone) then + inconflictzone=true + break + end + end + end + -- Check if unit is in any of the accept zones. if self.acceptzoneset:Count()>0 then local inzone=false @@ -793,7 +836,7 @@ function INTEL:UpdateIntel() end -- Unit is not in accept zone ==> remove! - if not inzone then + if (not inzone) and (not inconflictzone) then table.insert(remove, unitname) end end @@ -810,7 +853,7 @@ function INTEL:UpdateIntel() end -- Unit is inside a reject zone ==> remove! - if inzone then + if inzone and (not inconflictzone) then table.insert(remove, unitname) end end From 8f71e6c5c422f274d4889ce26e59686079e89253 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 6 Oct 2023 11:16:30 +0200 Subject: [PATCH 367/603] #EasyGCICAP * Recon Points option added --- Moose Development/Moose/Core/Set.lua | 24 +++- Moose Development/Moose/Ops/AirWing.lua | 88 +++++++++++- Moose Development/Moose/Ops/Auftrag.lua | 10 +- Moose Development/Moose/Ops/EasyGCICAP.lua | 156 +++++++++++++++++---- Moose Development/Moose/Ops/Legion.lua | 8 +- 5 files changed, 252 insertions(+), 34 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 056ce9e58..8db67b214 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -6052,7 +6052,29 @@ do -- SET_ZONE return self end - + + --- Get the average aggregated coordinate of this set of zones. + -- @param #SET_ZONE self + -- @return Core.Point#COORDINATE + function SET_ZONE:GetAverageCoordinate() + local x,y,z = 0,0,0 + local count = 0 + for _,_zone in pairs(self.Set) do + local zone=_zone --Core.Zone#ZONE + local vec3 = zone:GetVec3() + x = x + vec3.x + y = y + vec3.y + z = z + vec3.z + count = count + 1 + end + if count > 1 then + x = x/count + y = y/count + z = z/count + end + local coord = COORDINATE:New(x,y,z) + return coord + end --- Private function. -- @param #SET_ZONE self diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 4cfbe36a7..ed166e23d 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -34,14 +34,17 @@ -- @field Core.Set#SET_ZONE zonesetCAP Set of CAP zones. -- @field Core.Set#SET_ZONE zonesetTANKER Set of TANKER zones. -- @field Core.Set#SET_ZONE zonesetAWACS Set of AWACS zones. +-- @field Core.Set#SET_ZONE zonesetRECON Set of RECON zones. -- @field #number nflightsCAP Number of CAP flights constantly in the air. -- @field #number nflightsAWACS Number of AWACS flights constantly in the air. -- @field #number nflightsTANKERboom Number of TANKER flights with BOOM constantly in the air. -- @field #number nflightsTANKERprobe Number of TANKER flights with PROBE constantly in the air. -- @field #number nflightsRescueHelo Number of Rescue helo flights constantly in the air. +-- @field #number nflightsRecon Number of Recon flights constantly in the air. -- @field #table pointsCAP Table of CAP points. -- @field #table pointsTANKER Table of Tanker points. -- @field #table pointsAWACS Table of AWACS points. +-- @field #table pointsRecon Table of RECON points. -- @field #boolean markpoints Display markers on the F10 map. -- @field Ops.Airboss#AIRBOSS airboss Airboss attached to this wing. -- @@ -123,6 +126,7 @@ AIRWING = { pointsCAP = {}, pointsTANKER = {}, pointsAWACS = {}, + pointsRecon = {}, markpoints = false, } @@ -175,7 +179,7 @@ AIRWING = { --- AIRWING class version. -- @field #string version -AIRWING.version="0.9.2" +AIRWING.version="0.9.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -219,6 +223,7 @@ function AIRWING:New(warehousename, airwingname) -- Defaults: self.nflightsCAP=0 self.nflightsAWACS=0 + self.nflightsRecon=0 self.nflightsTANKERboom=0 self.nflightsTANKERprobe=0 self.nflightsRecoveryTanker=0 @@ -734,6 +739,15 @@ function AIRWING:SetNumberAWACS(n) return self end +--- Set number of RECON flights constantly in the air. +-- @param #AIRWING self +-- @param #number n Number of flights. Default 1. +-- @return #AIRWING self +function AIRWING:SetNumberRecon(n) + self.nflightsRecon=n or 1 + return self +end + --- Set number of Rescue helo flights constantly in the air. -- @param #AIRWING self -- @param #number n Number of flights. Default 1. @@ -819,6 +833,23 @@ function AIRWING:AddPatrolPointCAP(Coordinate, Altitude, Speed, Heading, LegLeng return self end +--- Add a patrol Point for RECON missions. +-- @param #AIRWING self +-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. +-- @param #number Altitude Orbit altitude in feet. +-- @param #number Speed Orbit speed in knots. +-- @param #number Heading Heading in degrees. +-- @param #number LegLength Length of race-track orbit in NM. +-- @return #AIRWING self +function AIRWING:AddPatrolPointRecon(Coordinate, Altitude, Speed, Heading, LegLength) + + local patrolpoint=self:NewPatrolPoint("RECON", Coordinate, Altitude, Speed, Heading, LegLength) + + table.insert(self.pointsRecon, patrolpoint) + + return self +end + --- Add a patrol Point for TANKER missions. -- @param #AIRWING self -- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. @@ -971,6 +1002,9 @@ function AIRWING:onafterStatus(From, Event, To) -- Check Rescue Helo missions. self:CheckRescuhelo() + -- Check Recon missions. + self:CheckRECON() + ---------------- -- Transport --- ---------------- @@ -1111,6 +1145,58 @@ function AIRWING:CheckCAP() return self end +--- Check how many RECON missions are assigned and add number of missing missions. +-- @param #AIRWING self +-- @return #AIRWING self +function AIRWING:CheckRECON() + + local Ncap=0 --self:CountMissionsInQueue({AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT}) + + -- Count CAP missions. + for _,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + + if mission:IsNotOver() and mission.type==AUFTRAG.Type.RECON and mission.patroldata then + Ncap=Ncap+1 + end + + end + + --self:I(self.lid.."Number of active RECON Missions: "..Ncap) + + for i=1,self.nflightsRecon-Ncap do + + --self:I(self.lid.."Creating RECON Missions: "..i) + + local patrol=self:_GetPatrolData(self.pointsRecon) + + local altitude=patrol.altitude --+1000*patrol.noccupied + + local ZoneSet = SET_ZONE:New() + local Zone = ZONE_RADIUS:New(self.alias.." Recon "..math.random(1,10000),patrol.coord:GetVec2(),UTILS.NMToMeters(patrol.leg/2)) + + ZoneSet:AddZone(Zone) + + if self.Debug then + Zone:DrawZone(self.coalition,{0,0,1},Alpha,FillColor,FillAlpha,2,true) + end + + local missionRECON=AUFTRAG:NewRECON(ZoneSet,patrol.speed,patrol.altitude,true) + + missionRECON.patroldata=patrol + missionRECON.categories={AUFTRAG.Category.AIRCRAFT} + + patrol.noccupied=patrol.noccupied+1 + + if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end + + self:AddMission(missionRECON) + + end + + return self +end + --- Check how many TANKER missions are assigned and add number of missing missions. -- @param #AIRWING self -- @return #AIRWING self diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 4c8fb42dd..b501813c8 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -2229,7 +2229,9 @@ function AUFTRAG:NewRECON(ZoneSet, Speed, Altitude, Adinfinitum, Randomly, Forma local mission=AUFTRAG:New(AUFTRAG.Type.RECON) mission:_TargetFromObject(ZoneSet) - + + mission.missionZoneSet = ZoneSet + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.RECON) mission.optionROE=ENUMS.ROE.WeaponHold @@ -2669,7 +2671,7 @@ function AUFTRAG:NewAUTO(EngageGroup) elseif auftrag==AUFTRAG.Type.ORBIT then mission=AUFTRAG:NewORBIT(Coordinate,Altitude,Speed,Heading,Leg) elseif auftrag==AUFTRAG.Type.RECON then - -- Not implemented yet. + mission=AUFTRAG:NewRECON(ZoneSet,Speed,Altitude,Adinfinitum,Randomly,Formation) elseif auftrag==AUFTRAG.Type.RESCUEHELO then mission=AUFTRAG:NewRESCUEHELO(Carrier) elseif auftrag==AUFTRAG.Type.SEAD then @@ -5362,6 +5364,10 @@ function AUFTRAG:GetTargetCoordinate() -- Special case where we defined a return self.transportPickup + + elseif self.missionZoneSet and self.type == AUFTRAG.Type.RECON then + + return self.missionZoneSet:GetAverageCoordinate() elseif self.engageTarget then diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 5891b12de..d72ba190a 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -56,6 +56,7 @@ -- @field #table ManagedCP -- @field #table ManagedTK -- @field #table ManagedEWR +-- @field #table ManagedREC -- @field #number MaxAliveMissions -- @field #boolean debug -- @field #number repeatsonfailure @@ -197,6 +198,7 @@ EASYGCICAP = { ManagedCP = {}, ManagedTK = {}, ManagedEWR = {}, + ManagedREC = {}, MaxAliveMissions = 8, debug = false, engagerange = 50, @@ -218,6 +220,7 @@ EASYGCICAP = { -- @field #string Livery -- @field #boolean Tanker -- @field #boolean AWACS +-- @field #boolean RECON -- @field #number Frequency -- @field #number Modulation -- @field #number TACAN @@ -475,14 +478,22 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) -- Create Airwing local CAP_Wing = AIRWING:New(Airbasename,Alias) + CAP_Wing:SetVerbosityLevel(3) CAP_Wing:SetReportOff() CAP_Wing:SetMarker(false) CAP_Wing:SetAirbase(AIRBASE:FindByName(Airbasename)) CAP_Wing:SetRespawnAfterDestroyed() CAP_Wing:SetNumberCAP(self.capgrouping) - CAP_Wing:SetNumberTankerBoom(1) - CAP_Wing:SetNumberTankerProbe(1) - CAP_Wing:SetNumberAWACS(1) + if #self.ManagedTK > 0 then + CAP_Wing:SetNumberTankerBoom(1) + CAP_Wing:SetNumberTankerProbe(1) + end + if #self.ManagedAW > 0 then + CAP_Wing:SetNumberAWACS(1) + end + if #self.ManagedREC > 0 then + CAP_Wing:SetNumberRecon(1) + end --local PatrolCoordinateKutaisi = ZONE:New(CapZoneName):GetCoordinate() --CAP_Wing:AddPatrolPointCAP(PatrolCoordinateKutaisi,self.capalt,UTILS.KnotsToAltKIAS(self.capspeed,self.capalt),self.capdir,self.capleg) CAP_Wing:SetTakeoffHot() @@ -500,13 +511,17 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) flightgroup:SetDespawnAfterHolding() flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename)) flightgroup:GetGroup():CommandEPLRS(true,5) - if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS then + if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS and Mission.type ~= AUFTRAG.Type.RECON then + flightgroup:SetDetection(true) flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet) flightgroup:SetOutOfAAMRTB() end - if Mission.type == AUFTRAG.Type.TANKER or Mission.type == AUFTRAG.Type.AWACS then + if Mission.type == AUFTRAG.Type.TANKER or Mission.type == AUFTRAG.Type.AWACS or Mission.type == AUFTRAG.Type.RECON then if TankerInvisible then - flightgroup:GetGroup():SetCommandInvisible(true) + flightgroup:GetGroup():SetCommandInvisible(true) + end + if Mission.type == AUFTRAG.Type.RECON then + flightgroup:SetDetection(true) end end flightgroup:GetGroup():OptionROTEvadeFire() @@ -555,6 +570,31 @@ function EASYGCICAP:AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Head return self end +--- Add a RECON patrol point to a Wing +-- @param #EASYGCICAP self +-- @param #string AirbaseName Name of the Wing's airbase +-- @param Core.Point#COORDINATE Coordinate. +-- @param #number Altitude Defaults to 25000 feet. +-- @param #number Speed Defaults to 300 knots. +-- @param #number Heading Defaults to 90 degrees (East). +-- @param #number LegLength Defaults to 15 NM. +-- @return #EASYGCICAP self +function EASYGCICAP:AddPatrolPointRecon(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) + self:T(self.lid.."AddPatrolPointRecon "..Coordinate:ToStringLLDDM()) + local EntryCAP = {} -- #EASYGCICAP.CapPoint + EntryCAP.AirbaseName = AirbaseName + EntryCAP.Coordinate = Coordinate + EntryCAP.Altitude = Altitude or 25000 + EntryCAP.Speed = Speed or 300 + EntryCAP.Heading = Heading or 90 + EntryCAP.LegLength = LegLength or 15 + self.ManagedREC[#self.ManagedREC+1] = EntryCAP + if self.debug then + --local mark = MARKER:New(Coordinate,self.lid.."Patrol Point Recon"):ToAll() + end + return self +end + --- Add a TANKER patrol point to a Wing -- @param #EASYGCICAP self -- @param #string AirbaseName Name of the Wing's airbase @@ -662,25 +702,22 @@ function EASYGCICAP:_SetCAPPatrolPoints() return self end ---- (Internal) Add a CAP patrol point to a Wing +--- (Internal) Set actual PatrolPoints from the list -- @param #EASYGCICAP self --- @param #string AirbaseName Name of the Wing's airbase --- @param Core.Point#COORDINATE Coordinate. --- @param #number Altitude Defaults to 25000 feet. --- @param #number Speed Defaults to 300 knots. --- @param #number Heading Defaults to 90 degrees (East). --- @param #number LegLength Defaults to 15 NM. --- @return #EASYGCICAP self -function EASYGCICAP:_AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) - self:T(self.lid.."_AddPatrolPointCAP") - local airbasename = AirbaseName or self.airbasename - local coordinate = Coordinate - local Altitude = Altitude or 25000 - local Speed = Speed or 300 - local Heading = Heading or 90 - local LegLength = LegLength or 15 - local wing = self.wings[airbasename][1] -- Ops.AirWing#AIRWING - wing:AddPatrolPointCAP(coordinate,Altitude,Speed,Heading,LegLength) +-- @return #EASYGCICAP self +function EASYGCICAP:_SetReconPatrolPoints() + self:T(self.lid.."_SetReconPatrolPoints") + for _,_data in pairs(self.ManagedREC) do + local data = _data --#EASYGCICAP.CapPoint + local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING + local Coordinate = data.Coordinate + local Altitude = data.Altitude + local Speed = data.Speed + local Heading = data.Heading + local LegLength = data.LegLength + Wing:AddPatrolPointRecon(Coordinate,Altitude,Speed,Heading,LegLength) + end + return self end @@ -705,6 +742,8 @@ function EASYGCICAP:_CreateSquads() self:_AddTankerSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequeny,Modulation,TACAN) elseif squad.AWACS then self:_AddAWACSSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequeny,Modulation) + elseif squad.RECON then + self:_AddReconSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery) else self:_AddSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery) end @@ -739,6 +778,34 @@ function EASYGCICAP:AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames, return self end +--- Add a Recon Squadron to an Airwing of the manager +-- @param #EASYGCICAP self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYGCICAP self +function EASYGCICAP:AddReconSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:T(self.lid.."AddReconSquadron "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYGCICAP.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 402 + EntrySQ.Livery = Livery + EntrySQ.RECON = true + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + --- Add a Tanker Squadron to an Airwing of the manager -- @param #EASYGCICAP self -- @param #string TemplateName Name of the group template. @@ -838,6 +905,37 @@ function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames return self end +--- (Internal) Add a Recon Squadron to an Airwing of the manager +-- @param #EASYGCICAP self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYGCICAP self +function EASYGCICAP:_AddReconSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:T(self.lid.."_AddReconSquadron "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.RECON}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.RECON},75) + + return self +end + --- (Internal) Add a Tanker Squadron to an Airwing of the manager -- @param #EASYGCICAP self -- @param #string TemplateName Name of the group template. @@ -1095,6 +1193,7 @@ function EASYGCICAP:onafterStart(From,Event,To) self:_SetCAPPatrolPoints() self:_SetTankerPatrolPoints() self:_SetAwacsPatrolPoints() + self:_SetReconPatrolPoints() self:__Status(-10) return self end @@ -1134,11 +1233,13 @@ function EASYGCICAP:onafterStatus(From,Event,To) local instock = 0 local capmission = 0 local interceptmission = 0 + local reconmission = 0 for _,_wing in pairs(self.wings) do local count = _wing[1]:CountAssetsOnMission(MissionTypes,Cohort) local count2 = _wing[1]:CountAssets(true,MissionTypes,Attributes) capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.GCICAP}) interceptmission = interceptmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.INTERCEPT}) + reconmission = reconmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.RECON}) assets = assets + count instock = instock + count2 end @@ -1147,9 +1248,10 @@ function EASYGCICAP:onafterStatus(From,Event,To) local text = "GCICAP "..self.alias text = text.."\nWings: "..wings.."\nSquads: "..squads.."\nCapPoints: "..caps.."\nAssets on Mission: "..assets.."\nAssets in Stock: "..instock text = text.."\nThreats: "..threatcount - text = text.."\Missions: "..capmission+interceptmission - text = text.."\ - CAP: "..capmission - text = text.."\ - Intercept: "..interceptmission + text = text.."\nMissions: "..capmission+interceptmission + text = text.."\n - CAP: "..capmission + text = text.."\n - Intercept: "..interceptmission + text = text.."\n - Recon: "..reconmission MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug) end self:__Status(30) diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 7405fc02a..5db2ad31f 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -1445,7 +1445,7 @@ end -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned. function LEGION:onafterLegionAssetReturned(From, Event, To, Cohort, Asset) -- Debug message. - self:I(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment))) + self:T(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 @@ -2557,9 +2557,10 @@ function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properti -- Distance to target. local TargetDistance=TargetVec2 and UTILS.VecDist2D(TargetVec2, cohort.legion:GetVec2()) or 0 - + -- Is in range? local Rmax=cohort:GetMissionRange(WeaponTypes) + local RangeMax = RangeMax or 0 local InRange=(RangeMax and math.max(RangeMax, Rmax) or Rmax) >= TargetDistance return InRange @@ -2610,6 +2611,7 @@ function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properti -- Is capable of the mission type? local can=AUFTRAG.CheckMissionCapability(MissionType, Cohort.missiontypes) + if can then can=CheckCategory(Cohort) else @@ -2687,7 +2689,7 @@ function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properti return nil end ---- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else. +--- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cannot be recruited by anyone else. -- @param #table Cohorts Cohorts included. -- @param #string MissionTypeRecruit Mission type for recruiting the cohort assets. -- @param #string MissionTypeOpt Mission type for which the assets are optimized. Default is the same as `MissionTypeRecruit`. From ed7821ad1cb2cabe0d26dbf686a1dee4ebd494f1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 6 Oct 2023 11:30:31 +0200 Subject: [PATCH 368/603] #EasyGCICAP --- Moose Development/Moose/Ops/EasyGCICAP.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index d72ba190a..248318fe7 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -590,7 +590,7 @@ function EASYGCICAP:AddPatrolPointRecon(AirbaseName,Coordinate,Altitude,Speed,He EntryCAP.LegLength = LegLength or 15 self.ManagedREC[#self.ManagedREC+1] = EntryCAP if self.debug then - --local mark = MARKER:New(Coordinate,self.lid.."Patrol Point Recon"):ToAll() + local mark = MARKER:New(Coordinate,self.lid.."Patrol Point Recon"):ToAll() end return self end From 8d37414eac578ab8688c53c4b2228eaa46c81ad9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 6 Oct 2023 13:17:54 +0200 Subject: [PATCH 369/603] #MESSAGE * Added `ToSRS()` --- Moose Development/Moose/Core/Message.lua | 128 ++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index c29212317..467938e86 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -52,7 +52,7 @@ -- === -- -- ### Author: **FlightControl** --- ### Contributions: +-- ### Contributions: **Applevangelist** -- -- === -- @@ -371,7 +371,9 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings ) trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) end end - + + self.CoalitionSide = CoalitionSide + return self end @@ -455,3 +457,125 @@ function MESSAGE:ToLogIf( Condition ) return self end +_MESSAGESRS = {} + +--- Set up MESSAGE generally to allow Text-To-Speech via SRS and TTS functions. +-- @param #string PathToSRS Path to SRS Folder, defaults to "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone". +-- @param #number Port Port number of SRS, defaults to 5002. +-- @param #string PathToCredentials (optional) Path to credentials file for e.g. Google. +-- @param #number Frequency Frequency in MHz. Can also be given as a #table of frequencies. +-- @param #number Modulation Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. +-- @param #string Gender (optional) Gender, i.e. "male" or "female", defaults to "male". +-- @param #strimg Culture (optional) Culture, e.g. "en-US", defaults to "en-GB" +-- @param #string Voice (optional) Voice. Will override gender and culture settings, e.g. MSRS.Voices.Microsoft.Hazel or MSRS.Voices.Google.Standard.de_DE_Standard_D. Hint on Microsoft voices - working voices are limited to Hedda, Hazel, David, Zira and Hortense. **Must** be installed on your Desktop or Server! +-- @param #number Coalition (optional) Coalition, can be coalition.side.RED, coalition.side.BLUE or coalition.side.NEUTRAL. Defaults to coalition.side.NEUTRAL. +-- @param #number Volume (optional) Volume, can be between 0.0 and 1.0 (loudest). +-- @param #string Label (optional) Label, defaults to "MESSAGE" or the Message Category set. +-- @param Core.Point#COORDINATE Coordinate (optional) Coordinate this messages originates from. +-- @usage +-- -- Mind the dot here, not using the colon this time around! +-- -- Needed once only +-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE) +-- -- later on in your code +-- MESSAGE:New("Test message!",15,"SPAWN"):ToAll():ToSRS() +-- +function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate) + local path = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + _MESSAGESRS.MSRS = MSRS:New(path,Frequency,Modulation,Volume) + _MESSAGESRS.MSRS:SetCoalition(Coalition) + _MESSAGESRS.MSRS:SetCoordinate(Coordinate) + _MESSAGESRS.MSRS:SetCulture(Culture) + _MESSAGESRS.MSRS:SetFrequencies(Frequency) + _MESSAGESRS.MSRS:SetGender(Gender) + _MESSAGESRS.MSRS:SetGoogle(PathToCredentials) + _MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE") + _MESSAGESRS.MSRS:SetModulations(Modulation) + _MESSAGESRS.MSRS:SetPath(PathToSRS) + _MESSAGESRS.MSRS:SetPort(Port) + _MESSAGESRS.MSRS:SetVolume(Volume) + _MESSAGESRS.MSRS:SetVoice(Voice) + _MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE") +end + +--- Sends a message via SRS. +-- @param #MESSAGE self +-- @param #number frequency (optional) Frequency in MHz. Can also be given as a #table of frequencies. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. +-- @param #number modulation (optional) Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. +-- @param #string gender (optional) Gender, i.e. "male" or "female". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #strimg culture (optional) Culture, e.g. "en-US. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #string voice (optional) Voice. Will override gender and culture settings. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #number coalition (optional) Coalition, can be coalition.side.RED, coalition.side.BLUE or coalition.side.NEUTRAL. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #number volume (optional) Volume, can be between 0.0 and 1.0 (loudest). Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param Core.Point#COORDINATE coordinate (optional) Coordinate this messages originates from. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @return #MESSAGE self +function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volume,coordinate) + if _MESSAGESRS.SRSQ then + _MESSAGESRS.MSRS:SetLabel(self.MessageCategory or _MESSAGESRS.MSRS.Label or "MESSAGE") + if gender then + _MESSAGESRS.MSRS:SetGender(gender) + end + if coalition then + _MESSAGESRS.MSRS:SetCoalition(coalition) + end + if culture then + _MESSAGESRS.MSRS:SetCulture(culture) + end + if volume then + _MESSAGESRS.MSRS:SetVolume(volume) + end + if coordinate then + _MESSAGESRS.MSRS:SetCoordinate(coordinate) + end + if voice then + _MESSAGESRS.MSRS:SetVoice(voice) + end + _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,1,nil,nil,nil,frequency,modulation) + end + return self +end + +--- Sends a message via SRS on the blue coalition side. +-- @param #MESSAGE self +-- @param #number frequency (optional) Frequency in MHz. Can also be given as a #table of frequencies. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. +-- @param #number modulation (optional) Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. +-- @param #string gender (optional) Gender, i.e. "male" or "female". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #strimg culture (optional) Culture, e.g. "en-US. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #string voice (optional) Voice. Will override gender and culture settings. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #number volume (optional) Volume, can be between 0.0 and 1.0 (loudest). Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param Core.Point#COORDINATE coordinate (optional) Coordinate this messages originates from. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @return #MESSAGE self +function MESSAGE:ToSRSBlue(frequency,modulation,gender,culture,voice,volume,coordinate) + self:ToSRS(frequency,modulation,gender,culture,voice,coalition.side.BLUE,volume,coordinate) + return self +end + +--- Sends a message via SRS on the red coalition side. +-- @param #MESSAGE self +-- @param #number frequency (optional) Frequency in MHz. Can also be given as a #table of frequencies. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. +-- @param #number modulation (optional) Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. +-- @param #string gender (optional) Gender, i.e. "male" or "female". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #strimg culture (optional) Culture, e.g. "en-US. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #string voice (optional) Voice. Will override gender and culture settings. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #number volume (optional) Volume, can be between 0.0 and 1.0 (loudest). Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param Core.Point#COORDINATE coordinate (optional) Coordinate this messages originates from. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @return #MESSAGE self +function MESSAGE:ToSRSRed(frequency,modulation,gender,culture,voice,volume,coordinate) + self:ToSRS(frequency,modulation,gender,culture,voice,coalition.side.RED,volume,coordinate) + return self +end + +--- Sends a message via SRS to all - via the neutral coalition side. +-- @param #MESSAGE self +-- @param #number frequency (optional) Frequency in MHz. Can also be given as a #table of frequencies. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. +-- @param #number modulation (optional) Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. +-- @param #string gender (optional) Gender, i.e. "male" or "female". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #strimg culture (optional) Culture, e.g. "en-US. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #string voice (optional) Voice. Will override gender and culture settings. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #number volume (optional) Volume, can be between 0.0 and 1.0 (loudest). Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param Core.Point#COORDINATE coordinate (optional) Coordinate this messages originates from. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @return #MESSAGE self +function MESSAGE:ToSRSAll(frequency,modulation,gender,culture,voice,volume,coordinate) + self:ToSRS(frequency,modulation,gender,culture,voice,coalition.side.NEUTRAL,volume,coordinate) + return self +end + From 400ae46e03499eff9861820418166cfeeb9b2ab2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 12 Oct 2023 08:52:23 +0200 Subject: [PATCH 370/603] #RAT * DOcu --- Moose Development/Moose/Functional/RAT.lua | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index f5b262277..13db4519a 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -1,4 +1,4 @@ ---- **Functional** - Create random airtraffic in your missions. +--- **Functional** - Create random air traffic in your missions. -- -- === -- @@ -184,8 +184,8 @@ -- * Climb rate is set to a moderate value of ~1500 ft/min. -- * The standard descent rate follows the 3:1 rule, i.e. 1000 ft decent per 3 miles of travel. Hence, angle of descent is ~3.6 degrees. -- * A holding point is randomly selected at a distance between 5 and 10 km away from destination airport. --- * The altitude of theholding point is ~1200 m AGL. Holding patterns might or might not happen with variable duration. --- * If an aircraft is spawned in air, the procedure omitts taxi and take-off and starts with the climb/cruising part. +-- * The altitude of the holding point is ~1200 m AGL. Holding patterns might or might not happen with variable duration. +-- * If an aircraft is spawned in air, the procedure omits taxi and take-off and starts with the climb/cruising part. -- * All values are randomized for each spawned aircraft. -- -- ## Mission Editor Setup @@ -201,13 +201,13 @@ -- Voilà, your already done! -- -- Optionally, you can set a specific livery for the aircraft or give it some weapons. --- However, the aircraft will by default not engage any enemies. Think of them as beeing on a peaceful or ferry mission. +-- However, the aircraft will by default not engage any enemies. Think of them as being on a peaceful or ferry mission. -- -- ## Basic Lua Script -- -- ![Process](..\Presentations\RAT\RAT_Basic_Lua_Script.png) -- --- The basic Lua script for one template group consits of two simple lines as shown in the picture above. +-- The basic Lua script for one template group consists of two simple lines as shown in the picture above. -- -- * **Line 2** creates a new RAT object "yak". The only required parameter for the constructor @{#RAT.New}() is the name of the group as defined in the mission editor. In this example it is "RAT_YAK". -- * **Line 5** trigger the command to spawn the aircraft. The (optional) parameter for the @{#RAT.Spawn}() function is the number of aircraft to be spawned of this object. @@ -221,9 +221,9 @@ -- ## Parking Problems -- -- One big issue in DCS is that not all aircraft can be spawned on every airport or airbase. In particular, bigger aircraft might not have a valid parking spot at smaller airports and --- airstripes. This can lead to multiple problems in DCS. +-- airstrips. This can lead to multiple problems in DCS. -- --- * Landing: When an aircraft tries to land at an airport where it does not have a valid parking spot, it is immidiately despawned the moment its wheels touch the runway, i.e. +-- * Landing: When an aircraft tries to land at an airport where it does not have a valid parking spot, it is immediately despawned the moment its wheels touch the runway, i.e. -- when a landing event is triggered. This leads to the loss of the RAT aircraft. On possible way to circumvent the this problem is to let another RAT aircraft spawn at landing -- and not when it shuts down its engines. See the @{RAT.RespawnAfterLanding}() function. -- * Spawning: When a big aircraft is dynamically spawned on a small airbase a few things can go wrong. For example, it could be spawned at a parking spot with a shelter. @@ -251,9 +251,9 @@ -- c17:Spawn(5) -- -- This would randomly spawn five C-17s but only on airports which have big open air parking spots. Note that also only destination airports are allowed --- which do have this type of parking spot. This should ensure that the aircraft is able to land at the destination without beeing despawned immidiately. +-- which do have this type of parking spot. This should ensure that the aircraft is able to land at the destination without being despawned immediately. -- --- Also, the aircraft are spawned only on the requested parking spot types and not on any other type. If no parking spot of this type is availabe at the +-- Also, the aircraft are spawned only on the requested parking spot types and not on any other type. If no parking spot of this type is available at the -- moment of spawning, the group is automatically spawned in air above the selected airport. -- -- ## Examples @@ -279,7 +279,7 @@ -- -- It is also possible to make aircraft "commute" between two airports, i.e. flying from airport A to B and then back from B to A, etc. -- This can be done by the @{#RAT.Commute}() function. Note that if no departure or destination airports are specified, the first departure and destination are chosen randomly. --- Then the aircraft will fly back and forth between those two airports indefinetly. +-- Then the aircraft will fly back and forth between those two airports indefinitely. -- -- -- ### Spawn in Air @@ -307,7 +307,7 @@ -- * @{#RAT.SetTakeoff}("cold"), which means that all aircraft are spawned with their engines off, -- * @{#RAT.SetTakeoff}("hot"), which means that all aircraft are spawned with their engines on, -- * @{#RAT.SetTakeoff}("runway"), which means that all aircraft are spawned already at the runway ready to takeoff. --- Note that in this case the default spawn intervall is set to 180 seconds in order to avoid aircraft jamms on the runway. Generally, this takeoff at runways should be used with care and problems are to be expected. +-- Note that in this case the default spawn intervall is set to 180 seconds in order to avoid aircraft jams on the runway. Generally, this takeoff at runways should be used with care and problems are to be expected. -- -- -- The options @{#RAT.SetMinDistance}() and @{#RAT.SetMaxDistance}() can be used to restrict the range from departure to destination. For example @@ -330,7 +330,7 @@ -- -- * @{#RAT.SetFLcruise}(300) will cause most planes fly around FL300. -- * @{#RAT.SetFLmin}(100) restricts the cruising alt such that no plane will fly below FL100. Note that this automatically changes the minimum distance from departure to destination. --- That means that only destinations are possible for which the aircraft has had enought time to reach that flight level and descent again. +-- That means that only destinations are possible for which the aircraft has had enough time to reach that flight level and descent again. -- * @{#RAT.SetFLmax}(200) will restrict the cruise alt to maximum FL200, i.e. no aircraft will travel above this height. -- -- @@ -684,10 +684,10 @@ function RAT:Spawn(naircraft) -- Set the coalition table based on choice of self.coalition and self.friendly. self:_SetCoalitionTable() - -- Get all airports of this map beloning to friendly coalition(s). + -- Get all airports of this map belonging to friendly coalition(s). self:_GetAirportsOfCoalition() - -- Set submenuname if it has not been set by user. + -- Set sub-menu name if it has not been set by user. if not self.SubMenuName then self.SubMenuName=self.alias end @@ -1223,9 +1223,9 @@ end --- Set name of destination airports or zones for the AI aircraft. -- @param #RAT self --- @param #string destinationnames Name of the destination airport or table of destination airports. +-- @param #string destinationnames Name of the destination airport or #table of destination airports. -- @return #RAT RAT self object. --- @usage RAT:SetDestination("Krymsk") makes all aircraft of this RAT oject fly to Krymsk airport. +-- @usage RAT:SetDestination("Krymsk") makes all aircraft of this RAT object fly to Krymsk airport. function RAT:SetDestination(destinationnames) self:F2(destinationnames) @@ -1478,7 +1478,7 @@ function RAT:NoRespawn() return self end ---- Number of tries to respawn an aircraft in case it has accitentally been spawned on runway. +--- Number of tries to respawn an aircraft in case it has accidentally been spawned on runway. -- @param #RAT self -- @param #number n Number of retries. Default is 3. -- @return #RAT RAT self object. @@ -1534,7 +1534,7 @@ function RAT:RespawnInAirNotAllowed() return self end ---- Check if aircraft have accidentally been spawned on the runway. If so they will be removed immediatly. +--- Check if aircraft have accidentally been spawned on the runway. If so they will be removed immediately. -- @param #RAT self -- @param #boolean switch If true, check is performed. If false, this check is omitted. -- @param #number radius Distance in meters until a unit is considered to have spawned accidentally on the runway. Default is 75 m. From dca57c97de867ed6b325c7557df46f6dceadc962 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 12 Oct 2023 10:54:00 +0200 Subject: [PATCH 371/603] Syntax --- Moose Development/Moose/Utilities/Utils.lua | 2 +- Moose Development/Moose/Wrapper/Controllable.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 367e85d66..34b316a45 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -224,7 +224,7 @@ UTILS = { -- @return #boolean UTILS.IsInstanceOf = function( object, className ) -- Is className NOT a string ? - if not type( className ) == 'string' then + if type( className ) ~= 'string' then -- Is className a Moose class ? if type( className ) == 'table' and className.IsInstanceOf ~= nil then diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 4012528ca..f7d4b452d 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2167,7 +2167,7 @@ do -- Patrol methods -- @return #CONTROLLABLE function CONTROLLABLE:PatrolZones( ZoneList, Speed, Formation, DelayMin, DelayMax ) - if not type( ZoneList ) == "table" then + if type( ZoneList ) ~= "table" then ZoneList = { ZoneList } end From 0c97bc5b5982db2c74dde7ac54057d83d7a626c3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 12 Oct 2023 17:55:54 +0200 Subject: [PATCH 372/603] #AUFTRAG --- Moose Development/Moose/Ops/Auftrag.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index b501813c8..ce20bebc5 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -2230,7 +2230,13 @@ function AUFTRAG:NewRECON(ZoneSet, Speed, Altitude, Adinfinitum, Randomly, Forma mission:_TargetFromObject(ZoneSet) - mission.missionZoneSet = ZoneSet + if ZoneSet:IsInstanceOf("SET_ZONE") then + mission.missionZoneSet = ZoneSet + else + mission.missionZoneSet = SET_ZONE:New() + mission.missionZoneSet:AddZone(ZoneSet) + end + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.RECON) From 013a6f134e36ede354374a3cc91a3f772972631c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 12 Oct 2023 18:05:23 +0200 Subject: [PATCH 373/603] AUFTRAG --- Moose Development/Moose/Ops/Auftrag.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index b51d52acc..c70ce6377 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -2232,7 +2232,7 @@ function AUFTRAG:NewRECON(ZoneSet, Speed, Altitude, Adinfinitum, Randomly, Forma if ZoneSet:IsInstanceOf("SET_ZONE") then mission.missionZoneSet = ZoneSet - else + elseif ZoneSet:IsInstanceOf("ZONE_BASE") then mission.missionZoneSet = SET_ZONE:New() mission.missionZoneSet:AddZone(ZoneSet) end From c9cad84419ab39aedf9b606bcdb0d823ad17c75b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 13 Oct 2023 16:12:38 +0200 Subject: [PATCH 374/603] #SRS --- Moose Development/Moose/Sound/SRS.lua | 62 ++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 3ace50895..28b871633 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -189,7 +189,7 @@ MSRS = { --- MSRS class version. -- @field #string version -MSRS.version="0.1.2" +MSRS.version="0.1.3" --- Voices -- @type MSRS.Voices @@ -582,6 +582,17 @@ function MSRS:SetVoice(Voice) return self end +--- Set to use a specific voice. Will override gender and culture settings. +-- @param #MSRS self +-- @param #string Voice Voice. +-- @return #MSRS self +function MSRS:SetDefaultVoice(Voice) + + self.defaultVoice=Voice + + return self +end + --- Set the coordinate from which the transmissions will be broadcasted. -- @param #MSRS self -- @param Core.Point#COORDINATE Coordinate Origin of the transmission. @@ -595,11 +606,25 @@ end --- Use google text-to-speech. -- @param #MSRS self --- @param PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". +-- @param #string PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". -- @return #MSRS self function MSRS:SetGoogle(PathToCredentials) self.google=PathToCredentials + self.APIKey=PathToCredentials + self.provider = "gcloud" + + return self +end + +--- Use google text-to-speech. +-- @param #MSRS self +-- @param #string APIKey API Key, usually a string of length 40 with characters and numbers. +-- @return #MSRS self +function MSRS:SetGoogleAPIKey(APIKey) + + self.APIKey=APIKey + self.provider = "gcloud" return self end @@ -1102,6 +1127,24 @@ MSRS_BACKEND_DCSGRPC.Functions.SetGoogle = function (self) return self end +--- Replacement function for @{#MSRS.SetGoogle} to use google text-to-speech - here: Set the API key +-- @param #MSRS self +-- @param #string key +-- @return #MSRS self +MSRS_BACKEND_DCSGRPC.Functions.SetAPIKey = function (self, key) + self.APIKey = key + return self +end + +--- Replacement function for @{#MSRS.SetGoogle} to use google text-to-speech - here: Set the API key +-- @param #MSRS self +-- @param #string voice +-- @return #MSRS self +MSRS_BACKEND_DCSGRPC.Functions.SetDefaultVoice = function (self, voice) + self.defaultVoice = voice + return self +end + --- MSRS:SetAWS() Use AWS text-to-speech. (API key set as part of DCS-gRPC configuration) -- @param #MSRS self -- @return #MSRS self @@ -1285,11 +1328,18 @@ MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Fr options.coalition = 'red' end - options.provider = {} - options.provider[self.provider] = {} - + options[self.provider] = {} + + if self.APIKey then + options[self.provider].key = self.APIKey + end + + if self.defaultVoice then + options[self.provider].voice = self.defaultVoice + end + if self.voice then - options.provider[self.provider].voice = Voice or self.voice + options[self.provider].voice = Voice or self.voice elseif ssml then -- DCS-gRPC doesn't directly support language/gender, but can use SSML -- Only use if a voice isn't explicitly set From 945195691450938f0e80b88021ea1030a2fa75dc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 14 Oct 2023 16:49:55 +0200 Subject: [PATCH 375/603] #MSRS * Added config file loading --- Moose Development/Moose/Core/Message.lua | 37 +-- Moose Development/Moose/Ops/ATIS.lua | 2 +- Moose Development/Moose/Sound/SRS.lua | 270 +++++++++++++++++--- Moose Development/Moose/Utilities/Utils.lua | 2 +- 4 files changed, 241 insertions(+), 70 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 43ef2878a..35d63b276 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -480,21 +480,24 @@ _MESSAGESRS = {} -- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS() -- function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate) - local path = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - _MESSAGESRS.MSRS = MSRS:New(path,Frequency,Modulation,Volume) + _MESSAGESRS.MSRS = MSRS:New(PathToSRS,Frequency,Modulation,Volume) _MESSAGESRS.MSRS:SetCoalition(Coalition) _MESSAGESRS.MSRS:SetCoordinate(Coordinate) _MESSAGESRS.MSRS:SetCulture(Culture) - _MESSAGESRS.MSRS:SetFrequencies(Frequency) + _MESSAGESRS.Culture = Culture + --_MESSAGESRS.MSRS:SetFrequencies(Frequency) _MESSAGESRS.MSRS:SetGender(Gender) + _MESSAGESRS.Gender = Gender _MESSAGESRS.MSRS:SetGoogle(PathToCredentials) _MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE") - _MESSAGESRS.MSRS:SetModulations(Modulation) - _MESSAGESRS.MSRS:SetPath(PathToSRS) + --_MESSAGESRS.MSRS:SetModulations(Modulation) + --_MESSAGESRS.MSRS:SetPath(PathToSRS) _MESSAGESRS.MSRS:SetPort(Port) - _MESSAGESRS.MSRS:SetVolume(Volume) + -- _MESSAGESRS.MSRS:SetVolume(Volume) _MESSAGESRS.MSRS:SetVoice(Voice) + _MESSAGESRS.Voice = Voice _MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE") + env.info(_MESSAGESRS.MSRS.provider,false) end --- Sends a message via SRS. @@ -517,26 +520,8 @@ end -- function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volume,coordinate) if _MESSAGESRS.SRSQ then - _MESSAGESRS.MSRS:SetLabel(self.MessageCategory or _MESSAGESRS.MSRS.Label or "MESSAGE") - if gender then - _MESSAGESRS.MSRS:SetGender(gender) - end - if coalition then - _MESSAGESRS.MSRS:SetCoalition(coalition) - end - if culture then - _MESSAGESRS.MSRS:SetCulture(culture) - end - if volume then - _MESSAGESRS.MSRS:SetVolume(volume) - end - if coordinate then - _MESSAGESRS.MSRS:SetCoordinate(coordinate) - end - if voice then - _MESSAGESRS.MSRS:SetVoice(voice) - end - _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,1,nil,nil,nil,frequency,modulation) + _MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.Voice) + _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency,modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,voice or _MESSAGESRS.Voice,volume,self.MessageCategory) end return self end diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 8f110b07f..dbe0e088d 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1524,7 +1524,7 @@ end -- @param #string GoogleKey Path to Google JSON-Key. -- @return #ATIS self function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) - if PathToSRS then + if PathToSRS or MSRS.path then self.useSRS=true self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation) self.msrs:SetGender(Gender) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 28b871633..c5cb090c9 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -41,12 +41,15 @@ -- @field #number volume Volume between 0 (min) and 1 (max). Default 1. -- @field #string culture Culture. Default "en-GB". -- @field #string gender Gender. Default "female". --- @field #string voice Specifc voce. +-- @field #string voice Specific voce. -- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send. -- @field #string path Path to the SRS exe. This includes the final slash "/". -- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". -- @field #string Label Label showing up on the SRS radio overlay. Default is "ROBOT". No spaces allowed. -- @field #table AltBackend Table containing functions and variables to enable an alternate backend to transmit to SRS. +-- @field #string ConfigFileName Name of the standard config file +-- @field #string ConfigFilePath Path to the standard config file +-- @field #boolean ConfigLoaded -- @extends Core.Base#BASE --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -185,11 +188,14 @@ MSRS = { coordinate = nil, Label = "ROBOT", AltBackend = nil, + ConfigFileName = "Moose_MSRS.lua", + ConfigFilePath = "Config\\", + ConfigLoaded = false, } --- MSRS class version. -- @field #string version -MSRS.version="0.1.3" +MSRS.version="0.1.4" --- Voices -- @type MSRS.Voices @@ -292,6 +298,37 @@ MSRS.Voices = { }, } +--- +-- @type MSRS.ProviderOptions +-- @field #string key +-- @field #string secret +-- @field #string region +-- @field #string defaultVoice +-- @field #string voice + +--- GRPC options +-- @type MSRS.GRPCOptions +-- @field #string plaintext +-- @field #string srsClientName +-- @field #table position +-- @field #string coalition +-- @field #MSRS.ProviderOptions gcloud +-- @field #MSRS.ProviderOptions win +-- @field #MSRS.ProviderOptions azure +-- @field #MSRS.ProviderOptions aws +-- @field #string DefaultProvider + +MSRS.GRPCOptions = {} -- #MSRS.GRPCOptions +MSRS.GRPCOptions.gcloud = {} -- #MSRS.ProviderOptions +MSRS.GRPCOptions.win = {} -- #MSRS.ProviderOptions +MSRS.GRPCOptions.azure = {} -- #MSRS.ProviderOptions +MSRS.GRPCOptions.aws = {} -- #MSRS.ProviderOptions + +MSRS.GRPCOptions.win.defaultVoice = "Hedda" +MSRS.GRPCOptions.win.voice = "Hedda" + +MSRS.GRPCOptions.DefaultProvider = "win" + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -299,6 +336,8 @@ MSRS.Voices = { -- TODO: Add functions to remove freqs and modulations. -- DONE: Add coordinate. -- DONE: Add google. +-- DONE: Add gRPC google options +-- DONE: Add loading default config file ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -328,25 +367,49 @@ function MSRS:New(PathToSRS, Frequency, Modulation, Volume, AltBackend) -- Add parameters to vars so alternate backends can use them if applicable Backend.Vars = Backend.Vars or {} - Backend.Vars.PathToSRS = UTILS.DeepCopy(PathToSRS) -- DeepCopy probably unecessary + Backend.Vars.PathToSRS = PathToSRS Backend.Vars.Frequency = UTILS.DeepCopy(Frequency) Backend.Vars.Modulation = UTILS.DeepCopy(Modulation) - Backend.Vars.Volume = UTILS.DeepCopy(Volume) -- DeepCopy probably unecessary + Backend.Vars.Volume = Volume Backend.Functions = Backend.Functions or {} return self:_NewAltBackend(Backend) end - -- If no AltBackend table, the proceed with default initialization - self:SetPath(PathToSRS) - self:SetPort() - self:SetFrequencies(Frequency) - self:SetModulations(Modulation) - self:SetGender() - self:SetCoalition() - self:SetLabel() - self:SetVolume() + local success = self:LoadConfigFile(nil,nil,self.ConfigLoaded) + + if (not success) and (not self.ConfigLoaded) then + + -- If no AltBackend table, the proceed with default initialisation + self:SetPath(PathToSRS) + self:SetPort() + self:SetFrequencies(Frequency) + self:SetModulations(Modulation) + self:SetGender() + self:SetCoalition() + self:SetLabel() + self:SetVolume(Volume) + + else + + -- there might be some overwrites from :New() + + if PathToSRS then + self:SetPath(PathToSRS) + end + + if Frequency then + self:SetFrequencies(Frequency) + self:SetModulations(Modulation) + end + + if Volume then + self:SetVolume(Volume) + end + + end + self.lid = string.format("%s-%s | ", self.name, self.version) if not io or not os then @@ -366,24 +429,25 @@ end -- @return #MSRS self function MSRS:SetPath(Path) - if Path==nil then + if Path==nil and not self.path then self:E("ERROR: No path to SRS directory specified!") return nil end - -- Set path. - self.path=Path - - -- Remove (back)slashes. - local n=1 ; local nmax=1000 - while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do - self.path=self.path:sub(1,#self.path-1) - n=n+1 + if Path then + -- Set path. + self.path=Path + + -- Remove (back)slashes. + local n=1 ; local nmax=1000 + while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do + self.path=self.path:sub(1,#self.path-1) + n=n+1 + end + + -- Debug output. + self:I(string.format("SRS path=%s", self:GetPath())) end - - -- Debug output. - self:T(string.format("SRS path=%s", self:GetPath())) - return self end @@ -579,6 +643,10 @@ function MSRS:SetVoice(Voice) self.voice=Voice + --local defaultprovider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider or "win" + + --self.GRPCOptions[defaultprovider].voice = Voice + return self end @@ -589,6 +657,8 @@ end function MSRS:SetDefaultVoice(Voice) self.defaultVoice=Voice + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider or "win" + self.GRPCOptions[provider].defaultVoice = Voice return self end @@ -606,13 +676,20 @@ end --- Use google text-to-speech. -- @param #MSRS self --- @param #string PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". +-- @param #string PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". Can also be the Google API key. -- @return #MSRS self function MSRS:SetGoogle(PathToCredentials) - - self.google=PathToCredentials - self.APIKey=PathToCredentials - self.provider = "gcloud" + + if PathToCredentials then + + self.google=PathToCredentials + self.APIKey=PathToCredentials + self.provider = "gcloud" + + self.GRPCOptions.DefaultProvider = "gcloud" + self.GRPCOptions.gcloud.key = PathToCredentials + + end return self end @@ -622,10 +699,12 @@ end -- @param #string APIKey API Key, usually a string of length 40 with characters and numbers. -- @return #MSRS self function MSRS:SetGoogleAPIKey(APIKey) - - self.APIKey=APIKey - self.provider = "gcloud" - + if APIKey then + self.APIKey=APIKey + self.provider = "gcloud" + self.GRPCOptions.DefaultProvider = "gcloud" + self.GRPCOptions.gcloud.key = APIKey + end return self end @@ -1041,6 +1120,111 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp return command end +--- Get SRS command to play sound using the `DCS-SR-ExternalAudio.exe`. +-- @param #MSRS self +-- @param #string Path Path to config file, defaults to "C:\Users\\Saved Games\DCS\Config" +-- @param #string Filename File to load, defaults to "Moose_MSRS.lua" +-- @param #boolean ConfigLoaded - if true, skip the loading +-- @return #boolean success +function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) + + local path = Path or lfs.writedir()..MSRS.ConfigFilePath + local file = Filename or MSRS.ConfigFileName or "Moose_MSRS.lua" + + if UTILS.CheckFileExists(path,file) and not ConfigLoaded then + assert(loadfile(path..file))() + -- now we should have a global var MSRS_Config + if MSRS_Config then + --[[ + -- Moose MSRS default Config + MSRS_Config = { + Path = "D:\\Program Files\\DCS-SimpleRadio-Standalone", + Port = 5002, + Frequency = {127,243}, -- must be a table, 1..n entries! + Modulation = {0,0}, -- must be a table, 1..n entries, one for each frequency! + Volume = 1.0, + Coalition = 0, -- 0 = Neutral, 1 = Red, 2 = Blue + Coordinate = {0,0,0}, -- x,y,alt - optional + Culture = "en-GB", + Gender = "male", + Google = "D:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- path to google json key file - optional + Label = "MSRS", + Voice = "Microsoft Hazel Desktop", + -- gRPC + GRPC = { -- see https://github.com/DCS-gRPC/rust-server + coalition = "blue", -- blue, red, neutral + DefaultProvider = "gcloud", -- win, gcloud, aws, or azure, some of the values below depend on your cloud provider + gcloud = { + key = "", -- for gRPC Google API key + --secret = "", -- needed for aws + --region = "",-- needed for aws + defaultVoice = "MSRS.Voices.Google.Standard.en_GB_Standard_F", + }, + win = { + defaultVoice = "Microsoft Hazel Desktop", + }, + } + } + --]] + if self then + self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + self.port = MSRS_Config.Port or 5002 + self.frequencies = MSRS_Config.Frequency or {127,243} + self.modulations = MSRS_Config.Modulation or {0,0} + self.coalition = MSRS_Config.Coalition or 0 + if MSRS_Config.Coordinate then + self.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1],MSRS_Config.Coordinate[2],MSRS_Config.Coordinate[3]) + end + self.culture = MSRS_Config.Culture or "en-GB" + self.gender = MSRS_Config.Gender or "male" + self.google = MSRS_Config.Google + self.Label = MSRS_Config.Label or "MSRS" + self.voice = MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel + if MSRS_Config.GRPC then + self.provider = MSRS_Config.GRPC.DefaultProvider + if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then + self.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key + self.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice + self.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret + self.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region + end + end + self.ConfigLoaded = true + else + MSRS.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + MSRS.port = MSRS_Config.Port or 5002 + MSRS.frequencies = MSRS_Config.Frequency or {127,243} + MSRS.modulations = MSRS_Config.Modulation or {0,0} + MSRS.coalition = MSRS_Config.Coalition or 0 + if MSRS_Config.Coordinate then + MSRS.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1],MSRS_Config.Coordinate[2],MSRS_Config.Coordinate[3]) + end + MSRS.culture = MSRS_Config.Culture or "en-GB" + MSRS.gender = MSRS_Config.Gender or "male" + MSRS.google = MSRS_Config.Google + MSRS.Label = MSRS_Config.Label or "MSRS" + MSRS.voice = MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel + if MSRS_Config.GRPC then + MSRS.provider = MSRS_Config.GRPC.DefaultProvider + if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then + MSRS.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key + MSRS.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice + MSRS.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret + MSRS.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region + end + end + MSRS.ConfigLoaded = true + end + end + env.info("MSRS - Sucessfully loaded default configuration from disk!",false) + else + env.info("MSRS - Cannot load default configuration from disk!",false) + return false + end + + return true +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- MSRS DCS-gRPC alternate backend ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1303,7 +1487,7 @@ MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Fr BASE:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") BASE:T({Text, Plaintext, Frequencies, Voice, Label}) - local options = {} + local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions local ssml = Text or '' local XmitFrequencies = Frequencies or self.Frequency @@ -1315,7 +1499,7 @@ MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Fr options.srsClientName = Label or self.Label options.position = {} if self.coordinate then - options.position.lat, options.position.lat, options.position.alt = self:_GetLatLongAlt(self.coordinate) + options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) end options.position.lat = options.position.lat or 0.0 @@ -1327,19 +1511,21 @@ MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Fr elseif UTILS.GetCoalitionName(self.coalition) == 'Red' then options.coalition = 'red' end - - options[self.provider] = {} + + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider + + options[provider] = {} if self.APIKey then - options[self.provider].key = self.APIKey + options[provider].key = self.APIKey end if self.defaultVoice then - options[self.provider].voice = self.defaultVoice + --options[provider].defaultVoice = self.defaultVoice end if self.voice then - options[self.provider].voice = Voice or self.voice + options[provider].voice = Voice or self.voice or self.defaultVoice elseif ssml then -- DCS-gRPC doesn't directly support language/gender, but can use SSML -- Only use if a voice isn't explicitly set diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 34b316a45..9fc3dd055 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -2421,7 +2421,7 @@ function UTILS.CheckFileExists(Path,Filename) -- Check io module is available. if not io then - BASE:E("ERROR: io not desanitized. Can't save current state.") + BASE:E("ERROR: io not desanitized.") return false end From ab197699b63c017381cdf0c1a720660117cad87e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 14 Oct 2023 17:23:26 +0200 Subject: [PATCH 376/603] ATIS --- Moose Development/Moose/Ops/ATIS.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index dbe0e088d..94bd002fc 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1555,7 +1555,7 @@ end --- Get the coalition of the associated airbase. -- @param #ATIS self --- @return #number Coalition of the associcated airbase. +-- @return #number Coalition of the associated airbase. function ATIS:GetCoalition() local coal = self.airbase and self.airbase:GetCoalition() or nil return coal From 7558b7ee1de5498f825601f5067feca6a8cc72fe Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 17 Oct 2023 09:28:20 +0200 Subject: [PATCH 377/603] #MSRS * Added loading config file --- Moose Development/Moose/Sound/SRS.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index c5cb090c9..34d2fbe2a 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1158,10 +1158,10 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) key = "", -- for gRPC Google API key --secret = "", -- needed for aws --region = "",-- needed for aws - defaultVoice = "MSRS.Voices.Google.Standard.en_GB_Standard_F", + defaultVoice = MSRS.Voices.Google.Standard.en_GB_Standard_F, }, win = { - defaultVoice = "Microsoft Hazel Desktop", + defaultVoice = "Hazel", }, } } @@ -1514,18 +1514,20 @@ MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Fr local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider - options[provider] = {} + options.provider = {} + + options.provider[provider] = {} if self.APIKey then - options[provider].key = self.APIKey + options.provider[provider].key = self.APIKey end if self.defaultVoice then - --options[provider].defaultVoice = self.defaultVoice + options.provider[provider].defaultVoice = self.defaultVoice end if self.voice then - options[provider].voice = Voice or self.voice or self.defaultVoice + options.provider[provider].voice = Voice or self.voice or self.defaultVoice elseif ssml then -- DCS-gRPC doesn't directly support language/gender, but can use SSML -- Only use if a voice isn't explicitly set @@ -1551,7 +1553,7 @@ MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Fr BASE:T("GRPC.tts") BASE:T(ssml) BASE:T(freq) - BASE:T(options) + BASE:T({options}) GRPC.tts(ssml, freq, options) end From d8bb2fe1f1539a7d2b2a7f1c2b09ff66655cd702 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 17 Oct 2023 11:01:18 +0200 Subject: [PATCH 378/603] #MSRS --- Moose Development/Moose/Sound/SRS.lua | 70 +++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 34d2fbe2a..13550da54 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1120,12 +1120,72 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp return command end ---- Get SRS command to play sound using the `DCS-SR-ExternalAudio.exe`. +--- Get central SRS configuration to be able to play tts over SRS radio using the `DCS-SR-ExternalAudio.exe`. -- @param #MSRS self -- @param #string Path Path to config file, defaults to "C:\Users\\Saved Games\DCS\Config" -- @param #string Filename File to load, defaults to "Moose_MSRS.lua" -- @param #boolean ConfigLoaded - if true, skip the loading -- @return #boolean success +-- @usage +-- 0) Benefits: Centralize configuration of SRS, keep paths and keys out of the mission source code, making it safer and easier to move missions to/between servers, +-- and also make config easier to use in the code. +-- 1) Create a config file named "Moose_MSRS.lua" at this location "C:\Users\\Saved Games\DCS\Config" (or wherever your Saved Games folder resides). +-- 2) The file needs the following structure: +-- +-- -- Moose MSRS default Config +-- MSRS_Config = { +-- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- adjust as needed +-- Port = 5002, -- adjust as needed +-- Frequency = {127,243}, -- must be a table, 1..n entries! +-- Modulation = {0,0}, -- must be a table, 1..n entries, one for each frequency! +-- Volume = 1.0, +-- Coalition = 0, -- 0 = Neutral, 1 = Red, 2 = Blue +-- Coordinate = {0,0,0}, -- x,y,alt - optional +-- Culture = "en-GB", +-- Gender = "male", +-- Google = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- path to google json key file - optional +-- Label = "MSRS", +-- Voice = "Microsoft Hazel Desktop", +-- -- gRPC (optional) +-- GRPC = { -- see https://github.com/DCS-gRPC/rust-server +-- coalition = "blue", -- blue, red, neutral +-- DefaultProvider = "gcloud", -- win, gcloud, aws, or azure, some of the values below depend on your cloud provider +-- gcloud = { +-- key = "", -- for gRPC Google API key +-- --secret = "", -- needed for aws +-- --region = "",-- needed for aws +-- defaultVoice = MSRS.Voices.Google.Standard.en_GB_Standard_F, +-- }, +-- win = { +-- defaultVoice = "Hazel", +-- }, +-- } +-- } +-- +-- 3) Load the config into the MSRS raw class before you do anything else: +-- +-- MSRS.LoadConfigFile() -- Note the "." here +-- +-- This will populate variables for the MSRS raw class and all instances you create with e.g. `mysrs = MSRS:New()` +-- Optionally you can also load this per **single instance** if so needed, i.e. +-- +-- mysrs:LoadConfig(Path,Filename) +-- +-- 4) Use the config in your code like so, variable names are basically the same as in the config file, but all lower case, examples: +-- +-- -- Needed once only +-- MESSAGE.SetMSRS(MSRS.path,nil,MSRS.google,243,radio.modulation.AM,nil,nil, +-- MSRS.Voices.Google.Standard.de_DE_Standard_B,coalition.side.BLUE) +-- +-- -- later on in your code +-- +-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS(243,radio.modulation.AM,nil,nil,MSRS.Voices.Google.Standard.fr_FR_Standard_C) +-- +-- -- Create new ATIS as usual +-- atis=ATIS:New(AIRBASE.Caucasus.Batumi, 123, radio.modulation.AM) +-- atis:SetSRS(nil,nil,nil,MSRS.Voices.Google.Standard.en_US_Standard_H) +-- --Start ATIS +-- atis:Start() function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) local path = Path or lfs.writedir()..MSRS.ConfigFilePath @@ -1138,8 +1198,8 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) --[[ -- Moose MSRS default Config MSRS_Config = { - Path = "D:\\Program Files\\DCS-SimpleRadio-Standalone", - Port = 5002, + Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- adjust as needed + Port = 5002, -- adjust as needed Frequency = {127,243}, -- must be a table, 1..n entries! Modulation = {0,0}, -- must be a table, 1..n entries, one for each frequency! Volume = 1.0, @@ -1147,10 +1207,10 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) Coordinate = {0,0,0}, -- x,y,alt - optional Culture = "en-GB", Gender = "male", - Google = "D:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- path to google json key file - optional + Google = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- path to google json key file - optional Label = "MSRS", Voice = "Microsoft Hazel Desktop", - -- gRPC + -- gRPC (optional) GRPC = { -- see https://github.com/DCS-gRPC/rust-server coalition = "blue", -- blue, red, neutral DefaultProvider = "gcloud", -- win, gcloud, aws, or azure, some of the values below depend on your cloud provider From 443e853d2abeb0406e992b923aa763e253ea9e5a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 17 Oct 2023 16:03:47 +0200 Subject: [PATCH 379/603] #trigger --- Moose Development/Moose/Core/Set.lua | 164 +++++++++++++++++++++++++- Moose Development/Moose/Core/Zone.lua | 150 +++++++++++++++++++++++ 2 files changed, 313 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 8db67b214..a044c2436 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -5842,6 +5842,7 @@ do -- SET_ZONE }, FilterMeta = { }, + Checktime = 5, } --- Creates a new SET_ZONE object, building a set of zones. @@ -6182,7 +6183,168 @@ do -- SET_ZONE return zmin, dmin end - + + --- Set the check time for SET_ZONE:Trigger() + -- @param #SET_ZONE self + -- @param #number seconds Check every seconds for objects entering or leaving the zone. Defaults to 5 secs. + -- @return #SET_ZONE self + function SET_ZONE:SetCheckTime(seconds) + self.Checktime = seconds or 5 + return self + end + + --- Start watching if the Object or Objects move into or out of our set of zones. + -- @param #SET_ZONE self + -- @param Wrappe.Controllable#CONTROLLABLE Objects Object or Objects to watch, can be of type UNIT, GROUP, CLIENT, or SET\_UNIT, SET\_GROUP, SET\_CLIENT + -- @return #SET_ZONE self + -- @usage + -- Create a SET_ZONE and have it look after a SET_GROUP of objects: + -- + -- local groupset = SET_GROUP:New():FilterPrefixes("Aerial"):FilterStart( + -- local zoneset = SET_ZONE:New():FilterPrefixes("Target Zone"):FilterOnce():Trigger(groupset) + -- + -- -- Draw on the map + -- zoneset:ForEachZone( + -- function(zone) + -- zone:DrawZone(-1, {0,1,0}, Alpha, FillColor, FillAlpha, 4, ReadOnly) + -- end + -- ) + -- + -- -- function called when a controllable enters + -- function zoneset:OnAfterEnteredZone(From,Event,To,Controllable,Zone) + -- MESSAGE:New("Group "..Controllable:GetName() .. " entered zone "..Zone:GetName(),10,"Set Trigger"):ToAll() + -- end + -- + -- -- function called when a controllable leaves + -- function zoneset:OnAfterLeftZone(From,Event,To,Controllable,Zone) + -- MESSAGE:New("Group "..Controllable:GetName() .. " left zone "..Zone:GetName(),10,"Set Trigger"):ToAll() + -- end + -- + -- -- Stop after 1 hour + -- zoneset:__TriggerStop(3600) + function SET_ZONE:Trigger(Objects) + --self:I("Added Set_Zone Trigger") + self:AddTransition("*","TriggerStart","TriggerRunning") + self:AddTransition("*","EnteredZone","*") + self:AddTransition("*","LeftZone","*") + self:AddTransition("*","TriggerRunCheck","*") + self:AddTransition("*","TriggerStop","TriggerStopped") + self:TriggerStart() + self.checkobjects = Objects + if UTILS.IsInstanceOf(Objects,"SET_BASE") then + self.objectset = Objects.Set + else + self.objectset = {Objects} + end + self:_TriggerCheck(true) + self:__TriggerRunCheck(self.Checktime) + return self + + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "TriggerStop". Stops the SET_ZONE Trigger. + -- @function [parent=#SET_ZONE] TriggerStop + -- @param #SET_ZONE self + + --- Triggers the FSM event "TriggerStop" after a delay. + -- @function [parent=#SET_ZONE] __TriggerStop + -- @param #SET_ZONE self + -- @param #number delay Delay in seconds. + + --- On After "EnteredZone" event. An observed object has entered the zone. + -- @function [parent=#SET_ZONE] OnAfterEnteredZone + -- @param #SET_ZONE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable entering the zone. + -- @param Core.Zone#ZONE_BASE Zone The zone entered. + + --- On After "LeftZone" event. An observed object has left the zone. + -- @function [parent=#SET_ZONE] OnAfterLeftZone + -- @param #SET_ZONE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable leaving the zone. + -- @param Core.Zone#ZONE_BASE Zone The zone left. + end + + --- (Internal) Check the assigned objects for being in/out of the zone + -- @param #SET_ZONE self + -- @param #boolean fromstart If true, do the init of the objects + -- @return #SET_ZONE self + function SET_ZONE:_TriggerCheck(fromstart) + --self:I("_TriggerCheck | FromStart = "..tostring(fromstart)) + if fromstart then + for _,_object in pairs(self.objectset) do + local obj = _object -- Wrapper.Controllable#CONTROLLABLE + if obj and obj:IsAlive() then + for _,_zone in pairs(self.Set) do + if not obj.TriggerInZone then obj.TriggerInZone = {} end + if _zone:IsCoordinateInZone(obj:GetCoordinate()) then + obj.TriggerInZone[_zone.ZoneName] = true + else + obj.TriggerInZone[_zone.ZoneName] = false + end + --self:I("Object "..obj:GetName().." is in zone = "..tostring(obj.TriggerInZone[_zone.ZoneName])) + end + end + end + else + for _,_object in pairs(self.objectset) do + local obj = _object -- Wrapper.Controllable#CONTROLLABLE + if obj and obj:IsAlive() then + for _,_zone in pairs(self.Set) do + -- Check for pop-up objects + if not obj.TriggerInZone then + -- has not been tagged previously - wasn't in set! + obj.TriggerInZone = {} + end + if not obj.TriggerInZone[_zone.ZoneName] then + -- has not been tagged previously - wasn't in set! + obj.TriggerInZone[_zone.ZoneName] = false + end + -- is obj in zone? + local inzone = _zone:IsCoordinateInZone(obj:GetCoordinate()) + --self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone)) + if inzone and not obj.TriggerInZone[_zone.ZoneName] then + -- wasn't in zone before + --self:I("Newly entered") + self:__EnteredZone(0.5,obj,_zone) + obj.TriggerInZone[_zone.ZoneName] = true + elseif (not inzone) and obj.TriggerInZone[_zone.ZoneName] then + -- has left the zone + --self:I("Newly left") + self:__LeftZone(0.5,obj,_zone) + obj.TriggerInZone[_zone.ZoneName] = false + else + --self:I("Not left or not entered, or something went wrong!") + end + end + end + end + end + return self + end + + --- (Internal) Check the assigned objects for being in/out of the zone + -- @param #SET_ZONE self + -- @param #string From + -- @param #string Event + -- @param #string to + -- @return #SET_ZONE self + function SET_ZONE:onafterTriggerRunCheck(From,Event,To) + --self:I("onafterTriggerRunCheck") + --self:I({From, Event, To}) + if self:GetState() ~= "TriggerStopped" then + self:_TriggerCheck() + self:__TriggerRunCheck(self.Checktime) + end + return self + end end do -- SET_ZONE_GOAL diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index a6c975b79..8d6a26622 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -64,6 +64,7 @@ -- @field #number ZoneID ID of zone. Only zones defined in the ME have an ID! -- @field #table Table of any trigger zone properties from the ME. The key is the Name of the property, and the value is the property's Value. -- @field #number Surface Type of surface. Only determined at the center of the zone! +-- @field #number Checktime Check every Checktime seconds, used for ZONE:Trigger() -- @extends Core.Fsm#FSM @@ -122,6 +123,7 @@ ZONE_BASE = { ZoneID=nil, Properties={}, Surface=nil, + Checktime = 5, } --- The ZONE_BASE.BoundingSquare @@ -557,6 +559,154 @@ function ZONE_BASE:GetZoneMaybe() end end +--- Set the check time for ZONE:Trigger() +-- @param #ZONE_BASE self +-- @param #number seconds Check every seconds for objects entering or leaving the zone. Defaults to 5 secs. +-- @return #ZONE_BASE self +function ZONE_BASE:SetCheckTime(seconds) + self.Checktime = seconds or 5 + return self +end + +--- Start watching if the Object or Objects move into or out of a zone. +-- @param #ZONE_BASE self +-- @param Wrappe.Controllable#CONTROLLABLE Objects Object or Objects to watch, can be of type UNIT, GROUP, CLIENT, or SET\_UNIT, SET\_GROUP, SET\_CLIENT +-- @return #ZONE_BASE self +-- @usage +-- -- Create a new zone and start watching it every 5 secs for a certain GROUP entering or leaving +-- local triggerzone = ZONE:New("ZonetoWatch"):Trigger(GROUP:FindByName("Aerial-1")) +-- +-- -- function to handle FSM event "EnteredZone" +-- function triggerzone:OnAfterEnteredZone(From,Event,To,Group) +-- MESSAGE:New("Group has entered zone!",15):ToAll() +-- end +-- +-- -- function to handle FSM event "LeftZone" +-- function triggerzone:OnAfterLeftZone(From,Event,To,Group) +-- MESSAGE:New("Group has left zone!",15):ToAll() +-- end +-- +-- -- Stop watching the zone after 1 hour +-- triggerzone:__TriggerStop(2600) +function ZONE_BASE:Trigger(Objects) + --self:I("Added Zone Trigger") + self:SetStartState("TriggerStopped") + self:AddTransition("TriggerStopped","TriggerStart","TriggerRunning") + self:AddTransition("*","EnteredZone","*") + self:AddTransition("*","LeftZone","*") + self:AddTransition("*","TriggerRunCheck","*") + self:AddTransition("*","TriggerStop","TriggerStopped") + self:TriggerStart() + self.checkobjects = Objects + if UTILS.IsInstanceOf(Objects,"SET_BASE") then + self.objectset = Objects.Set + else + self.objectset = {Objects} + end + self:_TriggerCheck(true) + self:__TriggerRunCheck(self.Checktime) + return self + + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "TriggerStop". Stops the ZONE_BASE Trigger. + -- @function [parent=#ZONE_BASE] TriggerStop + -- @param #ZONE_BASE self + + --- Triggers the FSM event "TriggerStop" after a delay. + -- @function [parent=#ZONE_BASE] __TriggerStop + -- @param #ZONE_BASE self + -- @param #number delay Delay in seconds. + + --- On After "EnteredZone" event. An observed object has entered the zone. + -- @function [parent=#ZONE_BASE] OnAfterEnteredZone + -- @param #ZONE_BASE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable entering the zone. + + --- On After "LeftZone" event. An observed object has left the zone. + -- @function [parent=#ZONE_BASE] OnAfterLeftZone + -- @param #ZONE_BASE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable leaving the zone. +end + +--- (Internal) Check the assigned objects for being in/out of the zone +-- @param #ZONE_BASE self +-- @param #boolean fromstart If true, do the init of the objects +-- @return #ZONE_BASE self +function ZONE_BASE:_TriggerCheck(fromstart) + --self:I("_TriggerCheck | FromStart = "..tostring(fromstart)) + local objectset = self.objectset or {} + if fromstart then + -- just earmark everyone in/out + for _,_object in pairs(objectset) do + local obj = _object -- Wrapper.Controllable#CONTROLLABLE + if not obj.TriggerInZone then obj.TriggerInZone = {} end + if obj and obj:IsAlive() and self:IsCoordinateInZone(obj:GetCoordinate()) then + obj.TriggerInZone[self.ZoneName] = true + else + obj.TriggerInZone[self.ZoneName] = false + end + --self:I("Object "..obj:GetName().." is in zone = "..tostring(obj.TriggerInZone[self.ZoneName])) + end + else + -- Check for changes + for _,_object in pairs(objectset) do + local obj = _object -- Wrapper.Controllable#CONTROLLABLE + if obj and obj:IsAlive() then + if not obj.TriggerInZone then + -- has not been tagged previously - wasn't in set! + obj.TriggerInZone = {} + end + if not obj.TriggerInZone[self.ZoneName] then + -- has not been tagged previously - wasn't in set! + obj.TriggerInZone[self.ZoneName] = false + end + -- is obj in zone? + local inzone = self:IsCoordinateInZone(obj:GetCoordinate()) + --self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone)) + if inzone and not obj.TriggerInZone[self.ZoneName] then + -- wasn't in zone before + --self:I("Newly entered") + self:__EnteredZone(0.5,obj) + obj.TriggerInZone[self.ZoneName] = true + elseif (not inzone) and obj.TriggerInZone[self.ZoneName] then + -- has left the zone + --self:I("Newly left") + self:__LeftZone(0.5,obj) + obj.TriggerInZone[self.ZoneName] = false + else + --self:I("Not left or not entered, or something went wrong!") + end + end + end + end + return self +end + +--- (Internal) Check the assigned objects for being in/out of the zone +-- @param #ZONE_BASE self +-- @param #string From +-- @param #string Event +-- @param #string to +-- @return #ZONE_BASE self +function ZONE_BASE:onafterTriggerRunCheck(From,Event,To) + if self:GetState() ~= "TriggerStopped" then + self:_TriggerCheck() + self:__TriggerRunCheck(self.Checktime) + end + return self +end + + + --- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists. -- @param #ZONE_BASE self -- @param #string PropertyName The name of a the TriggerZone Property to be retrieved. From 73464a09f1b1d53e64ecebf783a82bd66b42207c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 17 Oct 2023 16:06:11 +0200 Subject: [PATCH 380/603] srs --- Moose Development/Moose/Sound/SRS.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 13550da54..e05d2b437 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -195,7 +195,7 @@ MSRS = { --- MSRS class version. -- @field #string version -MSRS.version="0.1.4" +MSRS.version="0.1.2" --- Voices -- @type MSRS.Voices From d890b3fe927076c2c931846e92094540e7344109 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 20 Oct 2023 18:45:20 +0200 Subject: [PATCH 381/603] #Con --- .../Moose/Wrapper/Controllable.lua | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index f7d4b452d..f4811a615 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -909,6 +909,30 @@ function CONTROLLABLE:CommandEPLRS( SwitchOnOff, Delay ) return self end +--- Set unlimited fuel. See [DCS command Unlimited Fuel](https://wiki.hoggitworld.com/view/DCS_command_setUnlimitedFuel). +-- @param #CONTROLLABLE self +-- @param #boolean OnOff Set unlimited fuel on = true or off = false. +-- @param #number Delay (Optional) Set the option only after x seconds. +-- @return #CONTROLLABLE self +function CONTROLLABLE:CommandSetUnlimitedFuel(OnOff, Delay) + + local CommandSetFuel = { + id = 'SetUnlimitedFuel', + params = { + value = OnOff + } +} + + if Delay and Delay > 0 then + SCHEDULER:New( nil, self.CommandSetUnlimitedFuel, { self, OnOff }, Delay ) + else + self:SetCommand( CommandSetFuel ) + end + + return self +end + + --- Set radio frequency. See [DCS command EPLRS](https://wiki.hoggitworld.com/view/DCS_command_setFrequency) -- @param #CONTROLLABLE self -- @param #number Frequency Radio frequency in MHz. @@ -1073,6 +1097,42 @@ function CONTROLLABLE:TaskBombing( Vec2, GroupAttack, WeaponExpend, AttackQty, D return DCSTask end +--- (AIR) Strafe the point on the ground. +-- @param #CONTROLLABLE self +-- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver strafing at. +-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aircraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aircraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. +-- @param #number Length (optional) Length of the strafing area. +-- @param #number WeaponType (optional) The WeaponType. WeaponType is a number associated with a [corresponding weapons flags](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much ammunition will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion, e.g. AI.Task.WeaponExpend.ALL. +-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. +-- @return DCS#Task The DCS task structure. +-- @usage +-- local attacker = GROUP:FindByName("Aerial-1") +-- local attackVec2 = ZONE:New("Strafe Attack"):GetVec2() +-- -- Attack with any cannons = 805306368, 4 runs, strafe a field of 200 meters +-- local task = attacker:TaskStrafing(attackVec2,4,200,805306368,AI.Task.WeaponExpend.ALL) +-- attacker:SetTask(task,2) +function CONTROLLABLE:TaskStrafing( Vec2, AttackQty, Length, WeaponType, WeaponExpend, Direction, GroupAttack ) + + local DCSTask = { + id = 'Strafing', + params = { + point = Vec2, -- req + weaponType = WeaponType or 1073741822, + expend = WeaponExpend or "Auto", + attackQty = AttackQty or 1, -- req + attackQtyLimit = AttackQty >1 and true or false, + direction = Direction and math.rad(Direction) or 0, + directionEnabled = Direction and true or false, + groupAttack = GroupAttack or false, + length = Length, + } +} + + return DCSTask +end + --- (AIR) Attacking the map object (building, structure, etc). -- @param #CONTROLLABLE self -- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. @@ -1094,7 +1154,6 @@ function CONTROLLABLE:TaskAttackMapObject( Vec2, GroupAttack, WeaponExpend, Atta groupAttack = GroupAttack or false, expend = WeaponExpend or "Auto", attackQtyLimit = AttackQty and true or false, - attackQty = AttackQty, directionEnabled = Direction and true or false, direction = Direction and math.rad(Direction) or 0, altitudeEnabled = Altitude and true or false, From 6035544193cbd534b5f8ae293daf5f4aac0f917d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 21 Oct 2023 12:44:12 +0200 Subject: [PATCH 382/603] #RECOVERYTANKER * Added option to set unlimited fuel --- .../Moose/Ops/RecoveryTanker.lua | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/RecoveryTanker.lua b/Moose Development/Moose/Ops/RecoveryTanker.lua index 0489ccb3d..a4c00cfea 100644 --- a/Moose Development/Moose/Ops/RecoveryTanker.lua +++ b/Moose Development/Moose/Ops/RecoveryTanker.lua @@ -63,6 +63,7 @@ -- @field #boolean eplrs If true, enable data link, e.g. if used as AWACS. -- @field #boolean recovery If true, tanker will recover using the AIRBOSS marshal pattern. -- @field #number terminaltype Terminal type of used parking spots on airbases. +-- @field #boolean unlimitedfuel If true, the tanker will have unlimited fuel. -- @extends Core.Fsm#FSM --- Recovery Tanker. @@ -300,6 +301,7 @@ RECOVERYTANKER = { eplrs = nil, recovery = nil, terminaltype = nil, + unlimitedfuel = false, } --- Unique ID (global). @@ -308,7 +310,7 @@ _RECOVERYTANKERID=0 --- Class version. -- @field #string version -RECOVERYTANKER.version="1.0.9" +RECOVERYTANKER.version="1.0.10" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -326,6 +328,7 @@ RECOVERYTANKER.version="1.0.9" -- DONE: Set AA TACAN. -- DONE: Add refueling event/state. -- DONE: Possibility to add already present/spawned aircraft, e.g. for warehouse. +-- DONE: Add unlimited fuel ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -550,6 +553,15 @@ end -- User functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Set the tanker to have unlimited fuel. +-- @param #RECOVERYTANKER self +-- @param #boolean OnOff If true, the tanker will have unlimited fuel. +-- @return #RECOVERYTANKER self +function RECOVERYTANKER:SetUnlimitedFuel(OnOff) + self.unlimitedfuel = OnOff + return self +end + --- Set the speed the tanker flys in its orbit pattern. -- @param #RECOVERYTANKER self -- @param #number speed True air speed (TAS) in knots. Default 274 knots, which results in ~250 KIAS. @@ -899,6 +911,14 @@ function RECOVERYTANKER:onafterStart(From, Event, To) -- Spawn tanker. We need to introduce an alias in case this class is used twice. This would confuse the spawn routine. local Spawn=SPAWN:NewWithAlias(self.tankergroupname, self.alias) + if self.unlimitedfuel then + Spawn:OnSpawnGroup( + function (grp) + grp:CommandSetUnlimitedFuel(self.unlimitedfuel) + end + ) + end + -- Set radio frequency and modulation. Spawn:InitRadioCommsOnOff(true) Spawn:InitRadioFrequency(self.RadioFreq) From 3ffb6e192a9a8c2f87044725618b31520a063a14 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 24 Oct 2023 13:35:10 +0200 Subject: [PATCH 383/603] #EASYGCICAP - added better race track options --- Moose Development/Moose/DCS.lua | 19 ---- .../Moose/Functional/Warehouse.lua | 7 +- Moose Development/Moose/Ops/AirWing.lua | 48 ++++++++-- Moose Development/Moose/Ops/Auftrag.lua | 93 ++++++++++++++++++- Moose Development/Moose/Ops/Cohort.lua | 2 +- Moose Development/Moose/Ops/EasyGCICAP.lua | 36 ++++++- Moose Development/Moose/Ops/FlightGroup.lua | 3 + Moose Development/Moose/Ops/Legion.lua | 4 +- Moose Development/Moose/Ops/OpsGroup.lua | 20 ++++ .../Moose/Wrapper/Controllable.lua | 48 ++++++++++ 10 files changed, 237 insertions(+), 43 deletions(-) diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index b03eff621..26a0a99e7 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -1603,25 +1603,6 @@ do -- AI -- @field OPTION_RADIO_USAGE_KILL -- @field JETT_TANKS_IF_EMPTY -- @field FORCED_ATTACK - - --- - -- @type AI.Option.Air.id.FORMATION - -- @field LINE_ABREAST - -- @field TRAIL - -- @field WEDGE - -- @field ECHELON_RIGHT - -- @field ECHELON_LEFT - -- @field FINGER_FOUR - -- @field SPREAD_FOUR - -- @field WW2_BOMBER_ELEMENT - -- @field WW2_BOMBER_ELEMENT_HEIGHT - -- @field WW2_FIGHTER_VIC - -- @field HEL_WEDGE - -- @field HEL_ECHELON - -- @field HEL_FRONT - -- @field HEL_COLUMN - -- @field COMBAT_BOX - -- @field JAVELIN_DOWN --- -- @type AI.Option.Air.val diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 41160847a..97222cd6f 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3616,9 +3616,10 @@ function WAREHOUSE:onafterStatus(From, Event, To) end -- Print queue after processing requests. - self:_PrintQueue(self.queue, "Queue waiting") - self:_PrintQueue(self.pending, "Queue pending") - + if self.verbosity > 2 then + self:_PrintQueue(self.queue, "Queue waiting") + self:_PrintQueue(self.pending, "Queue pending") + end -- Check fuel for all assets. --self:_CheckFuel() diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index ed166e23d..9d918e5d9 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -54,7 +54,9 @@ -- @field #string takeoffType Take of type. -- @field #boolean despawnAfterLanding Aircraft are despawned after landing. -- @field #boolean despawnAfterHolding Aircraft are despawned after holding. --- +-- @field #boolean capOptionPatrolRaceTrack Use closer patrol race track or standard orbit auftrag. +-- @field #number capFormation If capOptionPatrolRaceTrack is true, set the formation, also. +-- -- @extends Ops.Legion#LEGION --- *I fly because it releases my mind from the tyranny of petty things.* -- Antoine de Saint-Exupery @@ -128,6 +130,8 @@ AIRWING = { pointsAWACS = {}, pointsRecon = {}, markpoints = false, + capOptionPatrolRaceTrack = false, + capFormation = nil, } --- Payload data. @@ -179,7 +183,7 @@ AIRWING = { --- AIRWING class version. -- @field #string version -AIRWING.version="0.9.3" +AIRWING.version="0.9.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -699,6 +703,24 @@ function AIRWING:SetNumberCAP(n) return self end +--- Set CAP flight formation. +-- @param #AIRWING self +-- @param #number Formation Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). +-- @return #AIRWING self +function AIRWING:SetCAPFormation(Formation) + self.capFormation = Formation + return self +end + +--- Set CAP close race track.We'll utilize the AUFTRAG PatrolRaceTrack instead of a standard race track orbit task. +-- @param #AIRWING self +-- @param #boolean OnOff If true, switch this on, else switch off. Off by default. +-- @return #AIRWING self +function AIRWING:SetCapCloseRaceTrack(OnOff) + self.capOptionPatrolRaceTrack = OnOff + return self +end + --- Set number of TANKER flights with Boom constantly in the air. -- @param #AIRWING self -- @param #number Nboom Number of flights. Default 1. @@ -1112,13 +1134,14 @@ end -- @return #AIRWING self function AIRWING:CheckCAP() - local Ncap=0 --self:CountMissionsInQueue({AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT}) + local Ncap=0 + -- Count CAP missions. for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG - if mission:IsNotOver() and mission.type==AUFTRAG.Type.GCICAP and mission.patroldata then + if mission:IsNotOver() and (mission.type==AUFTRAG.Type.GCICAP or mission.type == AUFTRAG.Type.PATROLRACETRACK) and mission.patroldata then Ncap=Ncap+1 end @@ -1130,8 +1153,18 @@ function AIRWING:CheckCAP() local altitude=patrol.altitude+1000*patrol.noccupied - local missionCAP=AUFTRAG:NewGCICAP(patrol.coord, altitude, patrol.speed, patrol.heading, patrol.leg) - + local missionCAP = nil -- Ops.Auftrag#AUFTRAG + + if self.capOptionPatrolRaceTrack then + + missionCAP=AUFTRAG:NewPATROL_RACETRACK(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg, self.capFormation) + + else + + missionCAP=AUFTRAG:NewGCICAP(patrol.coord, altitude, patrol.speed, patrol.heading, patrol.leg) + + end + missionCAP.patroldata=patrol patrol.noccupied=patrol.noccupied+1 @@ -1150,7 +1183,7 @@ end -- @return #AIRWING self function AIRWING:CheckRECON() - local Ncap=0 --self:CountMissionsInQueue({AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT}) + local Ncap=0 -- Count CAP missions. for _,_mission in pairs(self.missionqueue) do @@ -1278,7 +1311,6 @@ function AIRWING:CheckAWACS() end - for i=1,self.nflightsAWACS-N do local patrol=self:_GetPatrolData(self.pointsAWACS) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index c70ce6377..a1d234fbe 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -443,6 +443,7 @@ _AUFTRAGSNR=0 -- @field #string REARMING Rearming mission. -- @field #string CAPTUREZONE Capture zone mission. -- @field #string NOTHING Nothing. +-- @field #string PATROLRACETRACK Patrol Racetrack. AUFTRAG.Type={ ANTISHIP="Anti Ship", AWACS="AWACS", @@ -484,10 +485,10 @@ AUFTRAG.Type={ RELOCATECOHORT="Relocate Cohort", AIRDEFENSE="Air Defence", EWR="Early Warning Radar", - --RECOVERYTANKER="Recovery Tanker", REARMING="Rearming", CAPTUREZONE="Capture Zone", NOTHING="Nothing", + PATROLRACETRACK="Patrol Racetrack", } --- Special task description. @@ -511,6 +512,7 @@ AUFTRAG.Type={ -- @field #string REARMING Rearming. -- @field #string CAPTUREZONE Capture OPS zone. -- @field #string NOTHING Nothing. +-- @field #string PATROLRACETRACK Patrol Racetrack. AUFTRAG.SpecialTask={ FORMATION="Formation", PATROLZONE="PatrolZone", @@ -532,6 +534,7 @@ AUFTRAG.SpecialTask={ REARMING="Rearming", CAPTUREZONE="Capture Zone", NOTHING="Nothing", + PATROLRACETRACK="Patrol Racetrack", } --- Mission status. @@ -652,7 +655,7 @@ AUFTRAG.Category={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="1.2.0" +AUFTRAG.version="1.2.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1017,7 +1020,7 @@ end -- @param #number Altitude Hover altitude in feet AGL. Default is 50 feet above ground. -- @param #number Time Time in seconds to hold the hover. Default 300 seconds. -- @param #number Speed Speed in knots to fly to the target coordinate. Default 150kn. --- @param #number MissionAlt Altitide to fly towards the mission in feet AGL. Default 1000ft. +-- @param #number MissionAlt Altitude to fly towards the mission in feet AGL. Default 1000ft. -- @return #AUFTRAG self function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt) @@ -1049,6 +1052,58 @@ function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt) return mission end +--- **[AIR]** Create an enhanced orbit race track mission. Planes will keep closer to the track. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Where to start the race track. +-- @param #number Altitude (Optional) Altitude in feet. Defaults to 20,000ft. +-- @param #number Speed (Optional) Speed in knots. Defaults to 300kn. +-- @param #number Heading (Optional) Heading in degrees, 0 to 360. Defaults to 90 degree (East). +-- @param #number Leg (Optional) Leg of the race track in NM. Defaults to 10nm. +-- @param #number Formation (Optional) Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). +-- @return #AUFTRAG self +function AUFTRAG:NewPATROL_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg,Formation) + + local mission = AUFTRAG:New(AUFTRAG.Type.PATROLRACETRACK) + + -- Target. + mission:_TargetFromObject(Coordinate) + + -- Set Altitude. + if Altitude then + mission.TrackAltitude=UTILS.FeetToMeters(Altitude) + else + mission.TrackAltitude=UTILS.FeetToMeters(20000) + end + + -- Points + mission.TrackPoint1 = Coordinate + + local leg = UTILS.NMToMeters(Leg) or UTILS.NMToMeters(10) + + local heading = Heading or 90 + + if heading < 0 or heading > 360 then heading = 90 end + + mission.TrackPoint2 = Coordinate:Translate(leg,heading,true) + + -- Orbit speed in m/s TAS. + mission.TrackSpeed = UTILS.IasToTas(UTILS.KnotsToKmph(Speed or 300), mission.TrackAltitude) + + -- Mission speed in km/h and altitude + mission.missionSpeed = UTILS.KnotsToKmph(Speed or 300) + mission.missionAltitude = mission.TrackAltitude * 0.9 + mission.missionTask=ENUMS.MissionTask.CAP + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + + --- **[AIR]** Create an ORBIT mission, which can be either a circular orbit or a race-track pattern. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Where to orbit. @@ -1181,7 +1236,7 @@ function AUFTRAG:NewORBIT_GROUP(Group, Altitude, Speed, Leg, Heading, OffsetVec2 end ---- **[AIR]** Create a Ground Controlled CAP (GCICAP) mission. Flights with this task are considered for A2A INTERCEPT missions by the CHIEF class. They will perform a compat air patrol but not engage by +--- **[AIR]** Create a Ground Controlled CAP (GCICAP) mission. Flights with this task are considered for A2A INTERCEPT missions by the CHIEF class. They will perform a combat air patrol but not engage by -- themselfs. They wait for the CHIEF to tell them whom to engage. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Where to orbit. @@ -2688,6 +2743,8 @@ function AUFTRAG:NewAUTO(EngageGroup) mission=AUFTRAG:NewTANKER(Coordinate,Altitude,Speed,Heading,Leg,RefuelSystem) elseif auftrag==AUFTRAG.Type.TROOPTRANSPORT then mission=AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet,DropoffCoordinate,PickupCoordinate) + elseif auftrag==AUFTRAG.Type.PATROLRACETRACK then + mission=AUFTRAG:NewPATROL_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg,Formation) else end @@ -6343,8 +6400,32 @@ function AUFTRAG:GetDCSMissionTask() DCStask.params=param - table.insert(DCStasks, DCStask) + table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.PATROLRACETRACK then + --------------------- + -- Enhanced Orbit Racetrack -- + --------------------- + + local DCStask={} + DCStask.id=AUFTRAG.SpecialTask.PATROLRACETRACK + + local param={} + -- ONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation, Delay) + + param.TrackAltitude = self.TrackAltitude + param.TrackSpeed = self.TrackSpeed + param.TrackPoint1 = self.TrackPoint1 + param.TrackPoint2 = self.TrackPoint2 + param.missionSpeed = self.missionSpeed + param.missionAltitude = self.missionAltitude + param.TrackFormation = self.TrackFormation + + DCStask.params=param + + table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.HOVER then --------------------- @@ -6619,6 +6700,8 @@ function AUFTRAG:GetMissionTaskforMissionType(MissionType) mtask=ENUMS.MissionTask.NOTHING elseif MissionType==AUFTRAG.Type.HOVER then mtask=ENUMS.MissionTask.NOTHING + elseif MissionType==AUFTRAG.Type.PATROLRACETRACK then + mtask=ENUMS.MissionTask.CAP end return mtask diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index afd37f936..92aeb0914 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -1098,7 +1098,7 @@ function COHORT:RecruitAssets(MissionType, Npayloads) -- Assets on mission NOTHING are considered. table.insert(assets, asset) - elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and MissionType==AUFTRAG.Type.INTERCEPT then + elseif self.legion:IsAssetOnMission(asset, {AUFTRAG.Type.GCICAP, AUFTRAG.Type.PATROLRACETRACK}) and MissionType==AUFTRAG.Type.INTERCEPT then -- Check if the payload of this asset is compatible with the mission. -- Note: we do not check the payload as an asset that is on a GCICAP mission should be able to do an INTERCEPT as well! diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 248318fe7..0633f8b23 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -64,6 +64,7 @@ -- @field Core.Set#SET_ZONE NoGoZoneSet -- @field #boolean Monitor -- @field #boolean TankerInvisible +-- @field #number CapFormation -- @extends Core.Fsm#FSM --- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown. @@ -207,6 +208,7 @@ EASYGCICAP = { NoGoZoneSet = nil, Monitor = false, TankerInvisible = true, + CapFormation = nil, } --- Internal Squadron data type @@ -242,7 +244,7 @@ EASYGCICAP = { --- EASYGCICAP class version. -- @field #string version -EASYGCICAP.version="0.0.8" +EASYGCICAP.version="0.0.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -289,6 +291,7 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName) self.repeatsonfailure = 3 self.Monitor = false self.TankerInvisible = true + self.CapFormation = ENUMS.Formation.FixedWing.FingerFour.Group -- Set some string id for output to DCS.log file. self.lid=string.format("EASYGCICAP %s | ", self.alias) @@ -313,6 +316,14 @@ end -- Functions ------------------------------------------------------------------------- +--- Set CAP formation. +-- @param #EASYGCICAP self +-- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group +-- @return #EASYGCICAP self +function EASYGCICAP:SetCAPFormation(Formation) + self.CapFormation = Formation + return self +end --- Set Tanker and AWACS to be invisible to enemy AI eyes -- @param #EASYGCICAP self @@ -476,6 +487,8 @@ end function EASYGCICAP:_AddAirwing(Airbasename, Alias) self:T(self.lid.."_AddAirwing "..Airbasename) + local CapFormation = self.CapFormation + -- Create Airwing local CAP_Wing = AIRWING:New(Airbasename,Alias) CAP_Wing:SetVerbosityLevel(3) @@ -484,11 +497,15 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) CAP_Wing:SetAirbase(AIRBASE:FindByName(Airbasename)) CAP_Wing:SetRespawnAfterDestroyed() CAP_Wing:SetNumberCAP(self.capgrouping) + CAP_Wing:SetCapCloseRaceTrack(true) + if CapFormation then + CAP_Wing:SetCAPFormation(CapFormation) + end if #self.ManagedTK > 0 then CAP_Wing:SetNumberTankerBoom(1) CAP_Wing:SetNumberTankerProbe(1) end - if #self.ManagedAW > 0 then + if #self.ManagedEWR > 0 then CAP_Wing:SetNumberAWACS(1) end if #self.ManagedREC > 0 then @@ -515,6 +532,9 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) flightgroup:SetDetection(true) flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet) flightgroup:SetOutOfAAMRTB() + if CapFormation then + flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation) + end end if Mission.type == AUFTRAG.Type.TANKER or Mission.type == AUFTRAG.Type.AWACS or Mission.type == AUFTRAG.Type.RECON then if TankerInvisible then @@ -888,7 +908,7 @@ function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames self:T(self.lid.."_AddSquadron "..SquadName) -- Add Squadrons local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) - Squadron_One:AddMissionCapability({AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.ALERT5}) + Squadron_One:AddMissionCapability({AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5}) --Squadron_One:SetFuelLowRefuel(true) Squadron_One:SetFuelLowThreshold(0.3) Squadron_One:SetTurnoverTime(10,20) @@ -900,7 +920,7 @@ function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING wing:AddSquadron(Squadron_One) - wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.ALERT5},75) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5},75) return self end @@ -1234,12 +1254,16 @@ function EASYGCICAP:onafterStatus(From,Event,To) local capmission = 0 local interceptmission = 0 local reconmission = 0 + local awacsmission = 0 + local tankermission = 0 for _,_wing in pairs(self.wings) do local count = _wing[1]:CountAssetsOnMission(MissionTypes,Cohort) local count2 = _wing[1]:CountAssets(true,MissionTypes,Attributes) - capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.GCICAP}) + capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.GCICAP,AUFTRAG.Type.PATROLRACETRACK}) interceptmission = interceptmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.INTERCEPT}) reconmission = reconmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.RECON}) + awacsmission = awacsmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.AWACS}) + tankermission = tankermission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.TANKER}) assets = assets + count instock = instock + count2 end @@ -1251,6 +1275,8 @@ function EASYGCICAP:onafterStatus(From,Event,To) text = text.."\nMissions: "..capmission+interceptmission text = text.."\n - CAP: "..capmission text = text.."\n - Intercept: "..interceptmission + text = text.."\n - AWACS: "..awacsmission + text = text.."\n - TANKER: "..tankermission text = text.."\n - Recon: "..reconmission MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug) end diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 5800625df..1bbd1c8bb 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -2605,6 +2605,9 @@ function FLIGHTGROUP:onbeforeUpdateRoute(From, Event, To, n, N) elseif task.dcstask.id==AUFTRAG.SpecialTask.RECON then -- For recon missions, we need to allow the update as we insert new waypoints. self:T2(self.lid.."Allowing update route for Task: ReconMission") + elseif task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then + -- For recon missions, we need to allow the update as we insert new waypoints. + self:T2(self.lid.."Allowing update route for Task: Patrol Race Track") elseif task.dcstask.id==AUFTRAG.SpecialTask.HOVER then -- For recon missions, we need to allow the update as we insert new waypoints. self:T2(self.lid.."Allowing update route for Task: Hover") diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 5db2ad31f..0f6535e99 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -965,7 +965,7 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission, Assets) local pause=false -- Check if mission is INTERCEPT and asset is currently on GCI mission. If so, GCI is paused. - if currM.type==AUFTRAG.Type.GCICAP and Mission.type==AUFTRAG.Type.INTERCEPT then + if (currM.type==AUFTRAG.Type.GCICAP or currM.type==AUFTRAG.Type.PATROLRACETRACK) and Mission.type==AUFTRAG.Type.INTERCEPT then pause=true elseif (currM.type==AUFTRAG.Type.ONGUARD or currM.type==AUFTRAG.Type.PATROLZONE) and (Mission.type==AUFTRAG.Type.ARTY or Mission.type==AUFTRAG.Type.GROUNDATTACK) then pause=true @@ -3183,7 +3183,7 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu 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.type==AUFTRAG.Type.GCICAP and MissionType==AUFTRAG.Type.INTERCEPT then + elseif (currmission.type==AUFTRAG.Type.GCICAP or currmission.type==AUFTRAG.Type.PATROLRACETRACK) and MissionType==AUFTRAG.Type.INTERCEPT then -- Prefer assets that are on GCICAP to perform INTERCEPTS. We set this even higher than alert5 because they are already in the air. score=score+35 elseif (currmission.type==AUFTRAG.Type.ONGUARD or currmission.type==AUFTRAG.Type.PATROLZONE) and (MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK) then diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index ba562e542..0ba09f730 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -4463,6 +4463,26 @@ function OPSGROUP:_UpdateTask(Task, Mission) if target then self:EngageTarget(target, speed, Task.dcstask.params.formation) end + + elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then + + --- + -- Task "Patrol Race Track" Mission. + --- + + if self.isFlightgroup then + self:T("We are Special Auftrag Patrol Race Track, starting now ...") + --self:I({Task.dcstask.params}) + --[[ + Task.dcstask.params.TrackAltitude = self.TrackAltitude + Task.dcstask.params.TrackSpeed = self.TrackSpeed + Task.dcstask.params.TrackPoint1 = self.TrackPoint1 + Task.dcstask.params.TrackPoint2 = self.TrackPoint2 + Task.dcstask.params.TrackFormation = self.TrackFormation + --]] + local aircraft = self:GetGroup() + aircraft:PatrolRaceTrack(Task.dcstask.params.TrackPoint1,Task.dcstask.params.TrackPoint2,Task.dcstask.params.TrackAltitude,Task.dcstask.params.TrackSpeed,Task.dcstask.params.TrackFormation,1) + end elseif Task.dcstask.id==AUFTRAG.SpecialTask.HOVER then diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 9c9a68d58..20abf3e2a 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -5329,3 +5329,51 @@ function CONTROLLABLE:TaskAerobaticsBarrelRoll(TaskAerobatics,Repeats,InitAltitu return TaskAerobatics end + +--- [Air] Make an airplane or helicopter patrol between two points in a racetrack - resulting in a much tighter track around the start and end points. +-- @param #CONTROLLABLE self +-- @param Core.Point#COORDINATE Point1 Start point. +-- @param Core.Point#COORDINATE Point2 End point. +-- @param #number Altitude (Optional) Altitude in meters. Defaults to the altitude of the coordinate. +-- @param #number Speed (Optional) Speed in kph. Defaults to 500 kph. +-- @param #number Formation (Optional) Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). +-- @param #number Delay (Optional) Set the task after delay seconds only. +-- @return #CONTROLLABLE self +function CONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation, Delay) + + local PatrolGroup = self -- Wrapper.Group#GROUP + + if not self:IsInstanceOf( "GROUP" ) then + PatrolGroup = self:GetGroup() -- Wrapper.Group#GROUP + end + + local delay = Delay or 1 + + self:F( { PatrolGroup = PatrolGroup:GetName() } ) + + if PatrolGroup:IsAir() then + if Formation then + PatrolGroup:SetOption(AI.Option.Air.id.FORMATION,Formation) -- https://wiki.hoggitworld.com/view/DCS_option_formation + end + + local FromCoord = PatrolGroup:GetCoordinate() + local ToCoord = Point1:GetCoordinate() + + -- Calculate the new Route + if Altitude then + FromCoord:SetAltitude(Altitude) + ToCoord:SetAltitude(Altitude) + end + + -- Create a "air waypoint", which is a "point" structure that can be given as a parameter to a Task + local Route = {} + Route[#Route + 1] = FromCoord:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true, nil, DCSTasks, description, timeReFuAr ) + Route[#Route + 1] = ToCoord:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true, nil, DCSTasks, description, timeReFuAr ) + + local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRaceTrack", Point2, Point1, Altitude, Speed, Formation, Delay ) + PatrolGroup:SetTaskWaypoint( Route[#Route], TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. + PatrolGroup:Route( Route, Delay ) -- Move after delay seconds to the Route. See the Route method for details. + end + + return self +end From d7788a2a2efae67cb5d24aba352251d3865a2e2a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 24 Oct 2023 18:24:49 +0200 Subject: [PATCH 384/603] #PLAYERTASK * Remove client from task, even if only player name is available --- Moose Development/Moose/Ops/PlayerTask.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 15c2581ff..71a680cb4 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update Sept 2023 +-- @date Last Update Oct 2023 do @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.20" +PLAYERTASK.version="0.1.21" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -470,10 +470,11 @@ end --- [User] Remove a client from this task -- @param #PLAYERTASK self -- @param Wrapper.Client#CLIENT Client +-- @param #string Name Name of the client -- @return #PLAYERTASK self -function PLAYERTASK:RemoveClient(Client) +function PLAYERTASK:RemoveClient(Client,Name) self:T(self.lid.."RemoveClient") - local name = Client:GetPlayerName() + local name = Name or Client:GetPlayerName() if self.Clients:HasUniqueID(name) then self.Clients:PullByID(name) if self.verbose then @@ -1551,7 +1552,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.61" +PLAYERTASKCONTROLLER.version="0.1.62" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2186,6 +2187,10 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) task:RemoveClient(Client) --text = "Task aborted!" text = self.gettext:GetEntry("TASKABORT",self.locale) + else + task:RemoveClient(nil,EventData.IniPlayerName) + --text = "Task aborted!" + text = self.gettext:GetEntry("TASKABORT",self.locale) end else --text = "No active task!" From 3c252f01bef645bef9eecf2725fc4e0ae5d0bcb0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 25 Oct 2023 08:39:11 +0200 Subject: [PATCH 385/603] #OPSGROUP, CONTROLLER * AUFTRAG Patrol Race Track will use ASL not AGL as default --- Moose Development/Moose/Ops/OpsGroup.lua | 2 +- Moose Development/Moose/Wrapper/Controllable.lua | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 0ba09f730..3ae97c282 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -4481,7 +4481,7 @@ function OPSGROUP:_UpdateTask(Task, Mission) Task.dcstask.params.TrackFormation = self.TrackFormation --]] local aircraft = self:GetGroup() - aircraft:PatrolRaceTrack(Task.dcstask.params.TrackPoint1,Task.dcstask.params.TrackPoint2,Task.dcstask.params.TrackAltitude,Task.dcstask.params.TrackSpeed,Task.dcstask.params.TrackFormation,1) + aircraft:PatrolRaceTrack(Task.dcstask.params.TrackPoint1,Task.dcstask.params.TrackPoint2,Task.dcstask.params.TrackAltitude,Task.dcstask.params.TrackSpeed,Task.dcstask.params.TrackFormation,false,1) end elseif Task.dcstask.id==AUFTRAG.SpecialTask.HOVER then diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 20abf3e2a..89e3541f8 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -5337,9 +5337,10 @@ end -- @param #number Altitude (Optional) Altitude in meters. Defaults to the altitude of the coordinate. -- @param #number Speed (Optional) Speed in kph. Defaults to 500 kph. -- @param #number Formation (Optional) Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). +-- @param #boolean AGL (Optional) If true, set altitude to above ground level (AGL), not above sea level (ASL). -- @param #number Delay (Optional) Set the task after delay seconds only. -- @return #CONTROLLABLE self -function CONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation, Delay) +function CONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation, AGL, Delay) local PatrolGroup = self -- Wrapper.Group#GROUP @@ -5361,8 +5362,10 @@ function CONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation -- Calculate the new Route if Altitude then - FromCoord:SetAltitude(Altitude) - ToCoord:SetAltitude(Altitude) + local asl = true + if AGL then asl = false end + FromCoord:SetAltitude(Altitude, asl) + ToCoord:SetAltitude(Altitude, asl) end -- Create a "air waypoint", which is a "point" structure that can be given as a parameter to a Task From d728afd6f20888e17a945d034a664f65b3b308ab Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 25 Oct 2023 08:45:36 +0200 Subject: [PATCH 386/603] #docu --- Moose Development/Moose/Ops/Auftrag.lua | 4 ++-- Moose Development/Moose/Ops/EasyGCICAP.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index a1d234fbe..92511531c 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1055,8 +1055,8 @@ end --- **[AIR]** Create an enhanced orbit race track mission. Planes will keep closer to the track. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Where to start the race track. --- @param #number Altitude (Optional) Altitude in feet. Defaults to 20,000ft. --- @param #number Speed (Optional) Speed in knots. Defaults to 300kn. +-- @param #number Altitude (Optional) Altitude in feet. Defaults to 20,000ft ASL. +-- @param #number Speed (Optional) Speed in knots. Defaults to 300kn TAS. -- @param #number Heading (Optional) Heading in degrees, 0 to 360. Defaults to 90 degree (East). -- @param #number Leg (Optional) Leg of the race track in NM. Defaults to 10nm. -- @param #number Formation (Optional) Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 0633f8b23..0d2ce7d99 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -569,8 +569,8 @@ end -- @param #EASYGCICAP self -- @param #string AirbaseName Name of the Wing's airbase -- @param Core.Point#COORDINATE Coordinate. --- @param #number Altitude Defaults to 25000 feet. --- @param #number Speed Defaults to 300 knots. +-- @param #number Altitude Defaults to 25000 feet ASL. +-- @param #number Speed Defaults to 300 knots TAS. -- @param #number Heading Defaults to 90 degrees (East). -- @param #number LegLength Defaults to 15 NM. -- @return #EASYGCICAP self From a429f8c0aa426c23513b4d0139d0b98eab35f303 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 25 Oct 2023 13:47:20 +0200 Subject: [PATCH 387/603] #FC --- Moose Development/Moose/Ops/FlightControl.lua | 76 +++++++++++++------ 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 54fe4ef31..905b3d3fa 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -4,7 +4,7 @@ -- -- * Manage aircraft departure and arrival -- * Handles AI and human players --- * Limit number of AI groups taxiing, taking off and landing simultaniously +-- * Limit number of AI groups taxiing, taking off and landing simultaneously -- * Immersive voice overs via SRS text-to-speech -- * Define holding patterns for airdromes -- @@ -61,6 +61,7 @@ -- @field #number runwaydestroyed Time stamp (abs), when runway was destroyed. If `nil`, runway is operational. -- @field #number runwayrepairtime Time in seconds until runway will be repaired after it was destroyed. Default is 3600 sec (one hour). -- @field #boolean markerParking If `true`, occupied parking spots are marked. +-- @field #boolean nosubs If `true`, SRS TTS is without subtitles. -- @extends Core.Fsm#FSM --- **Ground Control**: Airliner X, Good news, you are clear to taxi to the active. @@ -122,7 +123,7 @@ -- * `Length` is the length of the pattern. -- * `FlightLevelMin` is the lowest altitude at which aircraft can hold. -- * `FlightLevelMax` is the highest altitude at which aircraft can hold. --- * `Prio` is the priority of this holdig stacks. If multiple patterns are defined, patterns with higher prio will be filled first. +-- * `Prio` is the priority of this holding stacks. If multiple patterns are defined, patterns with higher prio will be filled first. -- -- # Parking Guard -- @@ -137,13 +138,13 @@ -- -- # Limits for Inbound and Outbound Flights -- --- You can define limits on how many aircraft are simultaniously landing and taking off. This avoids (DCS) problems where taxiing aircraft cause a "traffic jam" on the taxi way(s) +-- You can define limits on how many aircraft are simultaneously landing and taking off. This avoids (DCS) problems where taxiing aircraft cause a "traffic jam" on the taxi way(s) -- and bring the whole airbase effectively to a stand still. -- -- ## Landing Limits -- -- The number of groups getting landing clearance can be set with the @{#FLIGHTCONTROL.SetLimitLanding}(*Nlanding, Ntakeoff*) function. --- The first parameter, `Nlanding`, defines how many groups get clearance simultaniously. +-- The first parameter, `Nlanding`, defines how many groups get clearance simultaneously. -- -- The second parameter, `Ntakeoff`, sets a limit on how many flights can take off whilst inbound flights still get clearance. By default, this is set to zero because the runway can only be used for takeoff *or* -- landing. So if you have a flight taking off, inbound fights will have to wait until the runway is clear. @@ -155,7 +156,7 @@ -- ## Taxiing/Takeoff Limits -- -- The number of AI flight groups getting clearance to taxi to the runway can be set with the @{#FLIGHTCONTROL.SetLimitTaxi}(*Nlanding, Ntakeoff*) function. --- The first parameter, `Ntaxi`, defines how many groups are allowed to taxi to the runway simultaniously. Note that once the AI starts to taxi, we loose complete control over it. +-- The first parameter, `Ntaxi`, defines how many groups are allowed to taxi to the runway simultaneously. Note that once the AI starts to taxi, we loose complete control over it. -- They will follow their internal logic to get the the runway and take off. Therefore, giving clearance to taxi is equivalent to giving them clearance for takeoff. -- -- By default, the parameter only counts the number of flights taxiing *to* the runway. If you set the second parameter, `IncludeInbound`, to `true`, this will also count the flights @@ -237,9 +238,9 @@ -- atcNellis:SetParkingGuardStatic("Static Generator F Template") -- -- Set taxi speed limit to 25 knots. -- atcNellis:SetSpeedLimitTaxi(25) --- -- Set that max 3 groups are allowed to taxi simultaniously. +-- -- Set that max 3 groups are allowed to taxi simultaneously. -- atcNellis:SetLimitTaxi(3, false, 1) --- -- Set that max 2 groups are allowd to land simultaniously and unlimited number (99) groups can land, while other groups are taking off. +-- -- Set that max 2 groups are allowd to land simultaneously and unlimited number (99) groups can land, while other groups are taking off. -- atcNellis:SetLimitLanding(2, 99) -- -- Use Google for text-to-speech. -- atcNellis:SetSRSTower(nil, nil, "en-AU-Standard-A", nil, nil, "D:\\Path To Google\\GoogleCredentials.json") @@ -270,6 +271,7 @@ FLIGHTCONTROL = { Nparkingspots = nil, holdingpatterns = {}, hpcounter = 0, + nosubs = false, } --- Holding point. Contains holding stacks. @@ -327,7 +329,7 @@ FLIGHTCONTROL.FlightStatus={ --- FlightControl class version. -- @field #string version -FLIGHTCONTROL.version="0.7.3" +FLIGHTCONTROL.version="0.7.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -407,6 +409,7 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port, self:SetFrequency(Frequency, Modulation) self:SetMarkHoldingPattern(true) self:SetRunwayRepairtime() + self.nosubs = false -- Set SRS Port self:SetSRSPort(Port or 5002) @@ -556,6 +559,22 @@ function FLIGHTCONTROL:SetVerbosity(VerbosityLevel) return self end +--- Set subtitles to appear on SRS TTS messages. +-- @param #FLIGHTCONTROL self +-- @return #FLIGHTCONTROL self +function FLIGHTCONTROL:SwitchSubtitlesOn() + self.nosubs = false + return self +end + +--- Set subtitles to appear on SRS TTS messages. +-- @param #FLIGHTCONTROL self +-- @return #FLIGHTCONTROL self +function FLIGHTCONTROL:SwitchSubtitlesOff() + self.nosubs = true + return self +end + --- Set the tower frequency. -- @param #FLIGHTCONTROL self -- @param #number Frequency Frequency in MHz. Default 305 MHz. @@ -595,7 +614,7 @@ end -- @param #string Culture Culture, e.g. "en-GB" (default). -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. -- @param #number Volume Volume. Default 1.0. --- @param #string Label Name under which SRS transmitts. +-- @param #string Label Name under which SRS transmits. -- @param #string PathToGoogleCredentials Path to google credentials json file. -- @param #number Port Server port for SRS -- @return #FLIGHTCONTROL self @@ -626,7 +645,7 @@ end -- @param #string Culture Culture, e.g. "en-GB" (default). -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. See [Google Voices](https://cloud.google.com/text-to-speech/docs/voices). -- @param #number Volume Volume. Default 1.0. --- @param #string Label Name under which SRS transmitts. Default `self.alias`. +-- @param #string Label Name under which SRS transmits. Default `self.alias`. -- @param #string PathToGoogleCredentials Path to google credentials json file. -- @return #FLIGHTCONTROL self function FLIGHTCONTROL:SetSRSTower(Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials) @@ -644,7 +663,7 @@ end -- @param #string Culture Culture, e.g. "en-US" (default). -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. -- @param #number Volume Volume. Default 1.0. --- @param #string Label Name under which SRS transmitts. Default "Pilot". +-- @param #string Label Name under which SRS transmits. Default "Pilot". -- @param #string PathToGoogleCredentials Path to google credentials json file. -- @return #FLIGHTCONTROL self function FLIGHTCONTROL:SetSRSPilot(Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials) @@ -657,17 +676,17 @@ function FLIGHTCONTROL:SetSRSPilot(Gender, Culture, Voice, Volume, Label, PathTo end ---- Set the number of aircraft groups, that are allowed to land simultaniously. +--- Set the number of aircraft groups, that are allowed to land simultaneously. -- Note that this restricts AI and human players. -- -- By default, up to two groups get landing clearance. They are spaced out in time, i.e. after the first one got cleared, the second has to wait a bit. -- This -- -- By default, landing clearance is only given when **no** other flight is taking off. You can adjust this for airports with more than one runway or --- in cases where simulatious takeoffs and landings are unproblematic. Note that only because there are multiple runways, it does not mean the AI uses them. +-- in cases where simultaneous takeoffs and landings are unproblematic. Note that only because there are multiple runways, it does not mean the AI uses them. -- -- @param #FLIGHTCONTROL self --- @param #number Nlanding Max number of aircraft landing simultaniously. Default 2. +-- @param #number Nlanding Max number of aircraft landing simultaneously. Default 2. -- @param #number Ntakeoff Allowed number of aircraft taking off for groups to get landing clearance. Default 0. -- @return #FLIGHTCONTROL self function FLIGHTCONTROL:SetLimitLanding(Nlanding, Ntakeoff) @@ -691,7 +710,7 @@ function FLIGHTCONTROL:SetLandingInterval(dt) end ---- Set the number of **AI** aircraft groups, that are allowed to taxi simultaniously. +--- Set the number of **AI** aircraft groups, that are allowed to taxi simultaneously. -- If the limit is reached, other AI groups not get taxi clearance to taxi to the runway. -- -- By default, this only counts the number of AI that taxi from their parking position to the runway. @@ -887,7 +906,7 @@ end -- Note that this is the time, the DCS engine uses not something we can control on a user level or we could get via scripting. -- You need to input the value. On the DCS forum it was stated that this is currently one hour. Hence this is the default value. -- @param #FLIGHTCONTROL self --- @param #number RepairTime Time in seconds until the runway is repaired. Default 3600 sec (one hour). +-- @param #number RepairTime Time in seconds until the runway is repaired. Default 3600sec (one hour). -- @return #FLIGHTCONTROL self function FLIGHTCONTROL:SetRunwayRepairtime(RepairTime) self.runwayrepairtime=RepairTime or 3600 @@ -1010,7 +1029,7 @@ function FLIGHTCONTROL:onbeforeStatusUpdate() if Tqueue>0 then -- Debug info. local text=string.format("Still got %d messages in the radio queue. Will call status again in %.1f sec", #self.msrsqueue, Tqueue) - self:I(self.lid..text) + self:T(self.lid..text) -- Call status again in dt seconds. self:__StatusUpdate(-Tqueue) @@ -2753,9 +2772,10 @@ function FLIGHTCONTROL:_PlayerInfoATIS(groupname) if flight then local text=string.format("Airbase %s ATIS:", self.airbasename) - + local srstxt = string.format("Airbase %s ", self.airbasename) if self.atis then text=text..string.format("\nATIS %.3f MHz %s", self.atis.frequency, UTILS.GetModulationName(self.atis.modulation)) + srstxt=srstxt..string.format("ATIS %.3f Megahertz %s", self.atis.frequency, UTILS.GetModulationName(self.atis.modulation)) if self.atis.towerfrequency then local tower="" for _,freq in pairs(self.atis.towerfrequency) do @@ -2779,7 +2799,17 @@ function FLIGHTCONTROL:_PlayerInfoATIS(groupname) end -- Message to flight - self:TextMessageToFlight(text, flight, 10, true) + + --self:TextMessageToFlight(text, flight, 10, true) + -- Call sign. + local callsign=self:_GetCallsignName(flight) + + -- Pilot calls inbound for landing. + local rtext=string.format("%s, %s, request ATIS frequency.", self.alias, callsign) + + -- Radio message. + self:TransmissionPilot(rtext, flight) + self:TransmissionTower(srstxt,flight,10) else self:E(self.lid..string.format("Cannot find flight group %s.", tostring(groupname))) @@ -3390,7 +3420,7 @@ function FLIGHTCONTROL:_PlayerRequestDirectLanding(groupname) if nTakeoff>self.NlandingTakeoff then -- Message text. - local text=string.format("%s, negative! We have currently traffic taking off", callsign) + local text=string.format("%s, negative! We have currently traffic taking off!", callsign) -- Send message. self:TransmissionTower(text, flight, 10) @@ -3854,7 +3884,7 @@ function FLIGHTCONTROL:_PlayerArrived(groupname) else -- Message text. - local text=string.format("%s, %s, arrived at parking position", self.alias, callsign) + local text=string.format("%s, %s, arrived at parking position.", self.alias, callsign) -- Transmit message. self:TransmissionPilot(text, flight) @@ -4277,7 +4307,7 @@ function FLIGHTCONTROL:TransmissionTower(Text, Flight, Delay) local subgroups=nil if Flight and not Flight.isAI then local playerData=Flight:_GetPlayerData() - if playerData.subtitles then + if playerData.subtitles and (not self.nosubs) then subgroups=subgroups or {} table.insert(subgroups, Flight.group) end @@ -4324,7 +4354,7 @@ function FLIGHTCONTROL:TransmissionPilot(Text, Flight, Delay) local subgroups=nil if Flight and not Flight.isAI then local playerData=Flight:_GetPlayerData() - if playerData.subtitles then + if playerData.subtitles and (not self.nosubs) then subgroups=subgroups or {} table.insert(subgroups, Flight.group) end From 50eeae0b3fadc24e2cc2f6699dd5625727808987 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 26 Oct 2023 09:43:56 +0200 Subject: [PATCH 388/603] SPAWN --- Moose Development/Moose/Core/Spawn.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index adcafc31f..7e299cdbb 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -2783,7 +2783,7 @@ end -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned. -- @usage -- --- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2() +-- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2() -- -- -- Spawn at the zone center position at the height specified in the ME of the group template! -- SpawnAirplanes:SpawnFromPointVec2( SpawnPointVec2 ) From b5ff10d9603e734db1be38ae365764f00ac0e667 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 26 Oct 2023 12:45:11 +0200 Subject: [PATCH 389/603] #UNIT --- Moose Development/Moose/Ops/Cohort.lua | 1 + Moose Development/Moose/Wrapper/Unit.lua | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index 5ed60451c..ca83bf8f7 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -972,6 +972,7 @@ function COHORT:CanMission(Mission) if Mission.refuelSystem and Mission.refuelSystem==self.tankerSystem then -- Correct refueling system. + self:T(self.lid..string.format("INFO: Correct refueling system requested=%s != %s=available", tostring(Mission.refuelSystem), tostring(self.tankerSystem))) else self:T(self.lid..string.format("INFO: Wrong refueling system requested=%s != %s=available", tostring(Mission.refuelSystem), tostring(self.tankerSystem))) return false diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 447d1d2f3..0fc411ebd 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -561,7 +561,7 @@ end --- Check if the unit is a tanker. Also retrieves the refuelling system (boom or probe) if applicable. -- @param #UNIT self --- @return #boolean If true, unit is refuelable (checks for the attribute "Refuelable"). +-- @return #boolean If true, unit is a tanker (checks for the attribute "Tankers"). -- @return #number Refueling system (if any): 0=boom, 1=probe. function UNIT:IsTanker() self:F2( self.UnitName ) @@ -582,7 +582,7 @@ function UNIT:IsTanker() -- Some hard coded data as this is not in the descriptors... if typename=="IL-78M" then system=1 --probe - elseif typename=="KC130" then + elseif typename=="KC130" or typename=="KC130J" then system=1 --probe elseif typename=="KC135BDA" then system=1 --probe @@ -590,6 +590,10 @@ function UNIT:IsTanker() system=1 --probe elseif typename=="S-3B Tanker" then system=1 --probe + elseif typename=="KC_10_Extender" then + system=1 --probe + elseif typename=="KC_10_Extender_D" then + system=0 --boom end end From b8ffb626b943670f92e4249ca51d6ce396e05ec7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 29 Oct 2023 17:44:47 +0100 Subject: [PATCH 390/603] #CTLD * Adding re-packing dropped units --- Moose Development/Moose/Ops/CTLD.lua | 58 +++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 8c77f8eb8..d98ffc882 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -19,10 +19,12 @@ -- === -- -- ### Author: **Applevangelist** (Moose Version), ***Ciribob*** (original), Thanks to: Shadowze, Cammel (testing), bbirchnz (additional code!!) +-- ### Repack addition for crates: **Raiden** +-- -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update June 2023 +-- Last Update October 2023 do @@ -700,6 +702,7 @@ do -- -- my_ctld.useprefix = true -- (DO NOT SWITCH THIS OFF UNLESS YOU KNOW WHAT YOU ARE DOING!) Adjust **before** starting CTLD. If set to false, *all* choppers of the coalition side will be enabled for CTLD. -- my_ctld.CrateDistance = 35 -- List and Load crates in this radius only. +-- my_ctld.PackDistance = 35 -- Pack crates in this radius only -- my_ctld.dropcratesanywhere = false -- Option to allow crates to be dropped anywhere. -- my_ctld.dropAsCargoCrate = false -- Parachuted herc cargo is not unpacked automatically but placed as crate to be unpacked. Needs a cargo with the same name defined like the cargo that was dropped. -- my_ctld.maximumHoverHeight = 15 -- Hover max this high to load. @@ -1121,6 +1124,7 @@ CTLD = { Spawned_Crates = {}, -- Holds objects for crates spawned generally Spawned_Cargo = {}, -- Binds together spawned_crates and their CTLD_CARGO objects CrateDistance = 35, -- list crates in this radius + PackDistance = 35, -- pack crates in this radius debug = false, wpZones = {}, dropOffZones = {}, @@ -1144,6 +1148,7 @@ CTLD = { -- DONE: List cargo in stock -- DONE: Limit of troops, crates buildable? -- DONE: Allow saving of Troops & Vehicles +-- DONE: Adding re-packing dropped units ------------------------------ --- Radio Beacons @@ -1223,7 +1228,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.40" +CTLD.version="1.0.41" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1341,6 +1346,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- setup self.CrateDistance = 35 -- list/load crates in this radius + self.PackDistance = 35 -- pack objects in this radius self.ExtractFactor = 3.33 -- factor for troops extraction, i.e. CrateDistance * Extractfactor self.prefixes = Prefixes or {"Cargoheli"} self.useprefix = true @@ -2260,9 +2266,10 @@ end -- @param #CTLD_CARGO Cargo -- @param #number number Number of crates to generate (for dropping) -- @param #boolean drop If true we\'re dropping from heli rather than loading. -function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) +-- @param #boolean pack If true we\'re packing crates from a template rather than loading or dropping +function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack) self:T(self.lid .. " _GetCrates") - if not drop then + if not drop and not pack then local cgoname = Cargo:GetName() -- check if we have stock local instock = Cargo:GetStock() @@ -2279,18 +2286,20 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) local width = 20 local distance = nil local zone = nil - if not drop then + if not drop and not pack then inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) if not inzone then ---@diagnostic disable-next-line: cast-local-type inzone, ship, zone, distance, width = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) end - else + elseif drop and not pack then if self.dropcratesanywhere then -- #1570 inzone = true else inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) end + elseif pack and not drop then + inzone = true end if not inzone then @@ -3229,6 +3238,42 @@ function CTLD:_BuildCrates(Group, Unit,Engineering) return self end +--- (Internal) Function to repair nearby vehicles / FOBs +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit + +function CTLD:_PackCratesNearby(Group, Unit) + self:T(self.lid .. " _PackCratesNearby") + ----------------------------------------- + -- search for nearest group to player + -- determine if group is packable + -- generate crates and destroy group + ----------------------------------------- + + -- get nearby vehicles + local location = Group:GetCoordinate() -- get coordinate of group using function + local nearestGroups = SET_GROUP:New():FilterCoalitions("blue"):FilterZones({ZONE_RADIUS:New("TempZone", location:GetVec2(), self.PackDistance, false)}):FilterOnce() -- get all groups withing PackDistance from group using function + -- get template name of all vehicles in zone + + -- determine if group is packable + for _, _Group in pairs(nearestGroups.Set) do -- convert #SET_GROUP to a list of Wrapper.Group#GROUP + for _, _Template in pairs(_DATABASE.Templates.Groups) do -- iterate through the database of templates + if (string.match(_Group:GetName(), _Template.GroupName)) then -- check if the Wrapper.Group#GROUP near the player is in the list of templates by name + -- generate crates and destroy group + for _, _entry in pairs(self.Cargo_Crates) do -- iterate through #CTLD_CARGO + if (_entry.Templates[1] == _Template.GroupName) then -- check if the #CTLD_CARGO matches the template name + _Group:Destroy() -- if a match is found destroy the Wrapper.Group#GROUP near the player + self:_GetCrates(Group, Unit, _entry, nil, false, true) -- spawn the appropriate crates near the player + return self + end + end + end + end + end + return self +end + --- (Internal) Function to repair nearby vehicles / FOBs -- @param #CTLD self -- @param Wrapper.Group#GROUP Group @@ -3541,6 +3586,7 @@ function CTLD:_RefreshF10Menus() if cancrates then local loadmenu = MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates, self._LoadCratesNearby, self, _group, _unit) local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates) + local packmenu = MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit) if self.usesubcats then local subcatmenus = {} From 408f9d9c0ccd52fd6501fecfc9bbf96d9d390024 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 2 Nov 2023 18:16:30 +0100 Subject: [PATCH 391/603] #ATIS and #SRS --- Moose Development/Moose/Ops/ATIS.lua | 23 +++++++++++++++++-- Moose Development/Moose/Ops/FlightControl.lua | 6 ++++- Moose Development/Moose/Ops/OpsGroup.lua | 4 ++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 94bd002fc..e906c5219 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -353,6 +353,7 @@ -- DEWPOINT = "Taupunkt", -- ALTIMETER = "Hoehenmesser", -- ACTIVERUN = "Aktive Startbahn", +-- ACTIVELANDING = "Aktive Landebahn", -- LEFT = "Links", -- RIGHT = "Rechts", -- RWYLENGTH = "Startbahn", @@ -721,7 +722,8 @@ ATIS.Messages = { TEMPERATURE = "Temperature", DEWPOINT = "Dew point", ALTIMETER = "Altimeter", - ACTIVERUN = "Active runway", + ACTIVERUN = "Active runway take off", + ACTIVELANDING = "Active runway landing", LEFT = "Left", RIGHT = "Right", RWYLENGTH = "Runway length", @@ -781,6 +783,7 @@ ATIS.Messages = { DEWPOINT = "Taupunkt", ALTIMETER = "Hoehenmesser", ACTIVERUN = "Aktive Startbahn", + ACTIVELANDING = "Aktive Landebahn", LEFT = "Links", RIGHT = "Rechts", RWYLENGTH = "Startbahn", @@ -841,6 +844,7 @@ ATIS.Messages = { DEWPOINT = "Punto de rocio", ALTIMETER = "Altímetro", ACTIVERUN = "Pista activa", + ACTIVELANDING = "Pista de aterrizaje activa", LEFT = "Izquierda", RIGHT = "Derecha", RWYLENGTH = "Longitud de pista", @@ -880,7 +884,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.10.2" +ATIS.version = "0.10.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -902,6 +906,7 @@ ATIS.version = "0.10.2" -- DONE: Set magnetic variation. -- DONE: New DCS 2.7 weather presets. -- DONE: Added TextAndSound localization +-- DONE: Added SRS spelling out both take off and landing runway ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -1534,6 +1539,7 @@ function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) self.msrs:SetCoalition(self:GetCoalition()) self.msrs:SetLabel("ATIS") self.msrs:SetGoogle(GoogleKey) + self.msrs:SetCoordinate(self.airbase:GetCoordinate()) self.msrsQ = MSRSQUEUE:New("ATIS") self.msrsQ:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers) if self.dTQueueCheck<=10 then @@ -2508,6 +2514,19 @@ function ATIS:onafterBroadcast( From, Event, To ) -- Active runway. local subtitle if runwayLanding then + local actrun = self.gettext:GetEntry("ACTIVELANDING",self.locale) + --subtitle=string.format("Active runway landing %s", runwayLanding) + subtitle=string.format("%s %s", actrun, runwayTakeoff) + if rwyTakeoffLeft==true then + --subtitle=subtitle.." Left" + subtitle=subtitle.." "..self.gettext:GetEntry("LEFT",self.locale) + elseif rwyTakeoffLeft==false then + --subtitle=subtitle.." Right" + subtitle=subtitle.." "..self.gettext:GetEntry("RIGHT",self.locale) + end + alltext = alltext .. ";\n" .. subtitle + end + if runwayTakeoff then local actrun = self.gettext:GetEntry("ACTIVERUN",self.locale) --subtitle=string.format("Active runway %s", runwayLanding) subtitle=string.format("%s %s", actrun, runwayLanding) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 905b3d3fa..6d26c78f6 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -424,12 +424,14 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port, self.msrsTower=MSRS:New(PathToSRS, Frequency, Modulation) self.msrsTower:SetPort(self.Port) self.msrsTower:SetGoogle(GoogleKey) + self.msrsTower:SetCoordinate(self:GetCoordinate()) self:SetSRSTower() -- SRS for Pilot. self.msrsPilot=MSRS:New(PathToSRS, Frequency, Modulation) self.msrsPilot:SetPort(self.Port) self.msrsPilot:SetGoogle(GoogleKey) + self.msrsTower:SetCoordinate(self:GetCoordinate()) self:SetSRSPilot() -- Wait at least 10 seconds after last radio message before calling the next status update. @@ -4341,7 +4343,7 @@ function FLIGHTCONTROL:TransmissionPilot(Text, Flight, Delay) local text=self:_GetTextForSpeech(Text) -- MSRS instance to use. - local msrs=self.msrsPilot + local msrs=self.msrsPilot -- Sound.SRS#MSRS if Flight.useSRS and Flight.msrs then @@ -4361,6 +4363,8 @@ function FLIGHTCONTROL:TransmissionPilot(Text, Flight, Delay) end -- Add transmission to msrsqueue. + local coordinate = Flight:GetCoordinate(true) + msrs:SetCoordinate() self.msrsqueue:NewTransmission(text, nil, msrs, nil, 1, subgroups, Text, nil, self.frequency, self.modulation) end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 3ae97c282..b5eb4079c 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -2345,6 +2345,10 @@ function OPSGROUP:RadioTransmission(Text, Delay, SayCallsign, Frequency) local freq, modu, radioon=self:GetRadio() + local coord = self:GetCoordinate() + + self.msrs:SetCoordinate(coord) + if Frequency then self.msrs:SetFrequencies(Frequency) else From f0de6ca9e197e16e5b17470332787c91056a7910 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 2 Nov 2023 19:26:51 +0100 Subject: [PATCH 392/603] #ATIS --- Moose Development/Moose/Ops/ATIS.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index e906c5219..6eca5b64a 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -722,8 +722,8 @@ ATIS.Messages = { TEMPERATURE = "Temperature", DEWPOINT = "Dew point", ALTIMETER = "Altimeter", - ACTIVERUN = "Active runway take off", - ACTIVELANDING = "Active runway landing", + ACTIVERUN = "Active runway departure", + ACTIVELANDING = "Active runway arrival", LEFT = "Left", RIGHT = "Right", RWYLENGTH = "Runway length", @@ -2516,11 +2516,11 @@ function ATIS:onafterBroadcast( From, Event, To ) if runwayLanding then local actrun = self.gettext:GetEntry("ACTIVELANDING",self.locale) --subtitle=string.format("Active runway landing %s", runwayLanding) - subtitle=string.format("%s %s", actrun, runwayTakeoff) - if rwyTakeoffLeft==true then + subtitle=string.format("%s %s", actrun, runwayLanding) + if rwyLandingLeft==true then --subtitle=subtitle.." Left" subtitle=subtitle.." "..self.gettext:GetEntry("LEFT",self.locale) - elseif rwyTakeoffLeft==false then + elseif rwyLandingLeft==false then --subtitle=subtitle.." Right" subtitle=subtitle.." "..self.gettext:GetEntry("RIGHT",self.locale) end @@ -2529,11 +2529,11 @@ function ATIS:onafterBroadcast( From, Event, To ) if runwayTakeoff then local actrun = self.gettext:GetEntry("ACTIVERUN",self.locale) --subtitle=string.format("Active runway %s", runwayLanding) - subtitle=string.format("%s %s", actrun, runwayLanding) - if rwyLandingLeft==true then + subtitle=string.format("%s %s", actrun, runwayTakeoff) + if rwyTakeoffLeft==true then --subtitle=subtitle.." Left" subtitle=subtitle.." "..self.gettext:GetEntry("LEFT",self.locale) - elseif rwyLandingLeft==false then + elseif rwyTakeoffLeft==false then --subtitle=subtitle.." Right" subtitle=subtitle.." "..self.gettext:GetEntry("RIGHT",self.locale) end From b1823824c278d968ea674e7ed8f48a937205ff65 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 3 Nov 2023 13:33:21 +0100 Subject: [PATCH 393/603] #SRS Improvements --- Moose Development/Moose/Core/Message.lua | 3 ++ Moose Development/Moose/Ops/Airboss.lua | 3 +- Moose Development/Moose/Ops/Awacs.lua | 13 +++++- Moose Development/Moose/Ops/CSAR.lua | 45 +++++++++++++++---- Moose Development/Moose/Ops/PlayerRecce.lua | 49 +++++++++++++++++++-- Moose Development/Moose/Ops/PlayerTask.lua | 10 +++-- Moose Development/Moose/Sound/SRS.lua | 33 ++++++++------ 7 files changed, 126 insertions(+), 30 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index b33fa92d2..715ab77a4 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -521,6 +521,9 @@ end function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volume,coordinate) if _MESSAGESRS.SRSQ then _MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.Voice) + if coordinate then + _MESSAGESRS.MSRS:SetCoordinate(coordinate) + end _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency,modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,voice or _MESSAGESRS.Voice,volume,self.MessageCategory) end return self diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 6d2f11b5e..2ffb3d8ba 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -1746,7 +1746,7 @@ AIRBOSS.MenuF10Root = nil --- Airboss class version. -- @field #string version -AIRBOSS.version = "1.3.2" +AIRBOSS.version = "1.3.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -3071,6 +3071,7 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum self.SRS:SetPath(PathToSRS) self.SRS:SetPort(Port or 5002) self.SRS:SetLabel(self.AirbossRadio.alias or "AIRBOSS") + self.SRS:SetCoordinate(self.carrier:GetCoordinate()) --self.SRS:SetModulations(Modulations) if GoogleCreds then self.SRS:SetGoogle(GoogleCreds) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index d55b0534d..eb1a4dd33 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -507,7 +507,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.57", -- #string + version = "0.2.58", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -971,6 +971,7 @@ AWACS.TaskStatus = { -- DONE - (WIP) Reporting -- DONE - Do not report non-airborne groups -- DONE - Added option for helos +-- DONE - Added setting a coordinate for SRS ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -6191,6 +6192,16 @@ function AWACS:onafterStatus(From, Event, To) -- Check on AUFTRAG status for CAP AI if self:Is("Running") and (awacsalive or self.AwacsInZone) then + + -- update coord for SRS + + if self.AwacsSRS then + self.AwacsSRS:SetCoordinate(self.AwacsFG:GetCoordinate()) + if self.TacticalSRS then + self.TacticalSRS:SetCoordinate(self.AwacsFG:GetCoordinate()) + end + end + self:_CheckAICAPOnStation() self:_CleanUpContacts() diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index bfcae5968..becdf6ec3 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -31,6 +31,7 @@ -- @image OPS_CSAR.jpg -- Date: May 2023 +-- Last: Update Oct 2024 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM @@ -116,21 +117,21 @@ -- mycsar.ADFRadioPwr = 1000 -- ADF Beacons sending with 1KW as default -- mycsar.PilotWeight = 80 -- Loaded pilots weigh 80kgs each -- --- ## 2.1 Experimental Features +-- ## 2.1 SRS Features and Other Features -- --- WARNING - Here\'ll be dragons! --- DANGER - For this to work you need to de-sanitize your mission environment (all three entries) in \Scripts\MissionScripting.lua --- Needs SRS => 1.9.6 to work (works on the **server** side of SRS) -- mycsar.useSRS = false -- Set true to use FF\'s SRS integration -- mycsar.SRSPath = "C:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your SRS installation -- server(!) -- mycsar.SRSchannel = 300 -- radio channel -- mycsar.SRSModulation = radio.modulation.AM -- modulation -- mycsar.SRSport = 5002 -- and SRS Server port -- mycsar.SRSCulture = "en-GB" -- SRS voice culture --- mycsar.SRSVoice = nil -- SRS voice, relevant for Google TTS +-- mycsar.SRSVoice = nil -- SRS voice for downed pilot, relevant for Google TTS -- mycsar.SRSGPathToCredentials = nil -- Path to your Google credentials json file, set this if you want to use Google TTS -- mycsar.SRSVolume = 1 -- Volume, between 0 and 1 -- mycsar.SRSGender = "male" -- male or female voice +-- mycsar.CSARVoice = MSRS.Voices.Google.Standard.en_US_Standard_A -- SRS voice for CSAR Controller, relevant for Google TTS +-- mycsar.CSARVoiceMS = MSRS.Voices.Microsoft.Hedda -- SRS voice for CSAR Controller, relevant for MS Desktop TTS +-- mycsar.coordinate -- Coordinate from which CSAR TTS is sending. Defaults to a random MASH object position -- -- -- mycsar.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection. Requires mycsar.enableForAI to be set to true. --shagrat -- mycsar.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. @@ -465,7 +466,10 @@ function CSAR:New(Coalition, Template, Alias) self.SRSGPathToCredentials = nil self.SRSVolume = 1.0 -- volume 0.0 to 1.0 self.SRSGender = "male" -- male or female - + self.CSARVoice = MSRS.Voices.Google.Standard.en_US_Standard_A + self.CSARVoiceMS = MSRS.Voices.Microsoft.Hedda + self.coordinate = nil -- Core.Point#COORDINATE + local AliaS = string.gsub(self.alias," ","_") self.filename = string.format("CSAR_%s_Persist.csv",AliaS) @@ -1737,7 +1741,16 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak, _overrid end -- integrate SRS if _speak and self.useSRS then - self.SRSQueue:NewTransmission(_text,nil,self.msrs,nil,2) + local coord = _unit:GetCoordinate() + if coord then + self.msrs:SetCoordinate(coord) + end + _text = string.gsub(_text,"km"," kilometer") + _text = string.gsub(_text,"nm"," nautical miles") + --self.msrs:SetVoice(self.SRSVoice) + --self.SRSQueue:NewTransmission(_text,nil,self.msrs,nil,1) + self:I("Voice = "..self.SRSVoice) + self.SRSQueue:NewTransmission(_text,duration,self.msrs,tstart,2,subgroups,subtitle,subduration,self.SRSchannel,self.SRSModulation,gender,culture,self.SRSVoice,volume,label,coord) end return self end @@ -1908,6 +1921,14 @@ end function CSAR:_DisplayToAllSAR(_message, _side, _messagetime) self:T(self.lid .. " _DisplayToAllSAR") local messagetime = _messagetime or self.messageTime + if self.msrs then + local voice = self.CSARVoice or MSRS.Voices.Google.Standard.en_GB_Standard_F + if self.msrs.google == nil then + voice = self.CSARVoiceMS or MSRS.Voices.Microsoft.Hedda + end + self:I("Voice = "..voice) + self.SRSQueue:NewTransmission(_message,duration,self.msrs,tstart,2,subgroups,subtitle,subduration,self.SRSchannel,self.SRSModulation,gender,culture,voice,volume,label,self.coordinate) + end for _, _unitName in pairs(self.csarUnits) do local _unit = self:_GetSARHeli(_unitName) if _unit and not self.suppressmessages then @@ -2268,6 +2289,12 @@ 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 not self.coordinate then + local csarhq = self.mash:GetRandom() + if csarhq then + self.coordinate = csarhq:GetCoordinate() + end + end if self.wetfeettemplate then self.usewetfeet = true end @@ -2275,7 +2302,7 @@ function CSAR:onafterStart(From, Event, To) local path = self.SRSPath local modulation = self.SRSModulation local channel = self.SRSchannel - self.msrs = MSRS:New(path,channel,modulation) + self.msrs = MSRS:New(path,channel,modulation) -- Sound.SRS#MSRS self.msrs:SetPort(self.SRSport) self.msrs:SetLabel("CSAR") self.msrs:SetCulture(self.SRSCulture) @@ -2287,7 +2314,7 @@ function CSAR:onafterStart(From, Event, To) end self.msrs:SetVolume(self.SRSVolume) self.msrs:SetLabel("CSAR") - self.SRSQueue = MSRSQUEUE:New("CSAR") + self.SRSQueue = MSRSQUEUE:New("CSAR") -- Sound.SRS#MSRSQUEUE end self:__Status(-10) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 120afb451..e225c96fe 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -104,7 +104,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.18", + version = "0.0.19", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, @@ -1524,8 +1524,12 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername) end if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2) - self.SRSQueue:NewTransmission(text2tts,nil,self.SRS,nil,2) + self.SRSQueue:NewTransmission(text2tts,nil,self.SRS,nil,3) MESSAGE:New(text2,10,self.Name or "FACA"):ToCoalition(self.Coalition) else MESSAGE:New(text1,10,self.Name or "FACA"):ToClient(Client) @@ -1560,8 +1564,12 @@ function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername) local text1 = "Going home!" if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2) - self.SRSQueue:NewTransmission(texttts,nil,self.SRS,nil,2) + self.SRSQueue:NewTransmission(texttts,nil,self.SRS,nil,3) MESSAGE:New(text,10,self.Name or "FACA"):ToCoalition(self.Coalition) else MESSAGE:New(text,10,self.Name or "FACA"):ToCoalition(self.Coalition) @@ -1617,6 +1625,9 @@ function PLAYERRECCE:onafterTargetDetected(From, Event, To, Targetsbyclock, Clie local ttstext = string.format("Target! %s! %s oh clock, %d %s!", ThreatTxt, i, targetdistance, dunits) if self.UseSRS then local grp = Client:GetGroup() + if clientcoord then + self.SRS:SetCoordinate(clientcoord) + end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) @@ -1653,6 +1664,10 @@ function PLAYERRECCE:onafterTargetDetected(From, Event, To, Targetsbyclock, Clie local ttstext = string.format("%d targets! %s oh clock, %d %s!", targetno, i, targetdistance, dunits) if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) @@ -1694,6 +1709,10 @@ function PLAYERRECCE:onafterIllumination(From, Event, To, Client, Playername, Ta local ttstext = "Sunshine!" if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) @@ -1733,6 +1752,10 @@ function PLAYERRECCE:onafterTargetsSmoked(From, Event, To, Client, Playername, T local ttstext = "Smoke and Mirrors!" if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) @@ -1772,6 +1795,10 @@ function PLAYERRECCE:onafterTargetsFlared(From, Event, To, Client, Playername, T local ttstext = "Fire works!" if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) @@ -1817,6 +1844,10 @@ function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Laserc local ttstext = "Laser on!" if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) @@ -1861,6 +1892,10 @@ function PLAYERRECCE:onafterShack(From, Event, To, Client, Target, Targettype) local ttstext = "Shack!" if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) @@ -1904,6 +1939,10 @@ function PLAYERRECCE:onafterTargetLOSLost(From, Event, To, Client, Target) local ttstext = "Lost L O S!" if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) @@ -1952,6 +1991,10 @@ function PLAYERRECCE:onafterTargetReportSent(From, Event, To, Client, TargetSet) local text = "Upload completed!" if self.UseSRS then local grp = Client:GetGroup() + local coord = grp:GetCoordinate() + if coord then + self.SRS:SetCoordinate(coord) + end self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,1,{grp},text,10) else MESSAGE:New(text,10,self.Name or "FACA"):ToClient(Client) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 71a680cb4..c840f5538 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.21" +PLAYERTASK.version="0.1.22" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -935,7 +935,7 @@ end do ------------------------------------------------------------------------------------------------------------------- -- PLAYERTASKCONTROLLER --- TODO: PLAYERTASKCONTROLLER + -- TODO: PLAYERTASKCONTROLLER -- DONE Playername customized -- DONE Coalition-level screen info to SET based -- DONE Flash directions @@ -4003,8 +4003,9 @@ end -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS +-- @param Core.Point#COORDINATE Coordinate Coordinate from which the controller radio is sending -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey) +function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Coordinate) self:T(self.lid.."SetSRS") self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" -- self.Gender = Gender or "male" -- @@ -4029,6 +4030,9 @@ function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Cultu if self.PathToGoogleKey then self.SRS:SetGoogle(self.PathToGoogleKey) end + if Coordinate then + self.SRS:SetCoordinate(Coordinate) + end self.SRSQueue = MSRSQUEUE:New(self.MenuName or self.Name) self.SRSQueue:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers) return self diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index f025d622a..57e3fddcc 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -824,15 +824,16 @@ end -- @param #MSRS self -- @param #string Text Text message. -- @param #number Delay Delay in seconds, before the message is played. +-- @param Core.Point#COORDINATE Coordinate Coordinate. -- @return #MSRS self -function MSRS:PlayText(Text, Delay) +function MSRS:PlayText(Text, Delay, Coordinate) if Delay and Delay>0 then - self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, 0) + self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate) else -- Get command line. - local command=self:_GetCommand() + local command=self:_GetCommand(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,Coordinate) -- Append text. command=command..string.format(" --text=\"%s\"", tostring(Text)) @@ -856,11 +857,12 @@ end -- @param #string Voice Voice. -- @param #number Volume Volume. -- @param #string Label Label. +-- @param Core.Point#COORDINATE Coordinate Coordinate. -- @return #MSRS self -function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label) +function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) if Delay and Delay>0 then - self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label) + self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) else -- Ensure table. @@ -874,7 +876,7 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture end -- Get command line. - local command=self:_GetCommand(Frequencies, Modulations, nil, Gender, Voice, Culture, Volume, nil, nil, Label) + local command=self:_GetCommand(Frequencies, Modulations, nil, Gender, Voice, Culture, Volume, nil, nil, Label, Coordinate) -- Append text. command=command..string.format(" --text=\"%s\"", tostring(Text)) @@ -1065,8 +1067,9 @@ end -- @param #number speed Speed. -- @param #number port Port. -- @param #string label Label, defaults to "ROBOT" (displayed sender name in the radio overlay of SRS) - No spaces allowed! +-- @param Core.Point#COORDINATE coordinate Coordinate. -- @return #string Command. -function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port,label) +function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port,label,coordinate) local path=self:GetPath() or STTS.DIRECTORY local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" @@ -1080,6 +1083,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp speed=speed or self.speed port=port or self.port label=label or self.Label + coordinate=coordinate or self.coordinate -- Replace modulation modus=modus:gsub("0", "AM") @@ -1104,8 +1108,8 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp end -- Set coordinate. - if self.coordinate then - local lat,lon,alt=self:_GetLatLongAlt(self.coordinate) + if coordinate then + local lat,lon,alt=self:_GetLatLongAlt(coordinate) command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt) end @@ -1667,6 +1671,7 @@ MSRSQUEUE = { -- @field #string voice Voice if any -- @field #number volume Volume -- @field #string label Label to be used +-- @field Core.Point#COORDINATE coordinate Coordinate for this transmission --- Create a new MSRSQUEUE object for a given radio frequency/modulation. -- @param #MSRSQUEUE self @@ -1751,8 +1756,9 @@ end -- @param #string voice Specific voice -- @param #number volume Volume setting -- @param #string label Label to be used +-- @param Core.Point#COORDINATE coordinate Coordinate to be used -- @return #MSRSQUEUE.Transmission Radio transmission table. -function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation, gender, culture, voice, volume, label) +function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation, gender, culture, voice, volume, label,coordinate) if self.TransmitOnlyWithPlayers then if self.PlayerSet and self.PlayerSet:CountAlive() == 0 then @@ -1792,7 +1798,8 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr transmission.voice = voice transmission.gender = volume transmission.label = label - + transmission.coordinate = coordinate + -- Add transmission to queue. self:AddTransmission(transmission) @@ -1805,9 +1812,9 @@ end function MSRSQUEUE:Broadcast(transmission) if transmission.frequency then - transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label) + transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label, transmission.coordinate) else - transmission.msrs:PlayText(transmission.text) + transmission.msrs:PlayText(transmission.text,nil,transmission.coordinate) end local function texttogroup(gid) From 7a97fed8a135cb1eda1fe84528dce5c0e219c37e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 4 Nov 2023 17:26:28 +0100 Subject: [PATCH 394/603] #SRS --- Moose Development/Moose/Core/Message.lua | 1 + Moose Development/Moose/Ops/EasyGCICAP.lua | 14 -------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 715ab77a4..65aa9f02b 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -524,6 +524,7 @@ function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volum if coordinate then _MESSAGESRS.MSRS:SetCoordinate(coordinate) end + local category = string.gsub(self.MessageCategory,":","") _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency,modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,voice or _MESSAGESRS.Voice,volume,self.MessageCategory) end return self diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 0d2ce7d99..c3192dae1 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -1140,20 +1140,6 @@ function EASYGCICAP:_StartIntel() -- Do we have a matching airwing? if targetairwing then local AssetCount = targetairwing:CountAssetsOnMission(MissionTypes,Cohort) - --[[ - local Assets = targetairwing:GetAssetsOnMission(AUFTRAG.Type.GCICAP) - for _,_asset in pairs(Assets) do - local asset = _asset -- Functional.Warehouse#WAREHOUSE.Assetitem - local fg = asset.flightgroup - local name = asset.spawngroupname - local mission = fg:GetMissionCurrent() - local mtype = mission.type - local distance = position:Get3DDistance(fg:GetCoordinate()) or 1000*1000 - distance = distance / 1000 - local text = string.format("FlightGroup %s on mission %s with distance %d km",name,mtype,distance) - local m = MESSAGE:New(text,15,"GCICAP"):ToAllIf(self.debug):ToLog() - end - --]] -- Enough airframes on mission already? self:T(self.lid.." Assets on Mission "..AssetCount) if AssetCount <= MaxAliveMissions then From b7159f7334533f37d2fcc03533f3768e2ff51ca6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 5 Nov 2023 13:01:38 +0100 Subject: [PATCH 395/603] # --- Moose Development/Moose/Core/Message.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 65aa9f02b..aeae8b95e 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -525,7 +525,7 @@ function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volum _MESSAGESRS.MSRS:SetCoordinate(coordinate) end local category = string.gsub(self.MessageCategory,":","") - _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency,modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,voice or _MESSAGESRS.Voice,volume,self.MessageCategory) + _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency,modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,voice or _MESSAGESRS.Voice,volume,category,coordinate) end return self end From c76507040143073b36d14de4d01fb1d782d8b146 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 6 Nov 2023 18:35:17 +0100 Subject: [PATCH 396/603] #FLIGHTGROUP * Resolve a stalemate when all jobs are done but _CheckGroupDone isn't called any longer --- Moose Development/Moose/Ops/FlightGroup.lua | 22 +++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index e203bbff3..dbc77e2ea 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -1256,21 +1256,21 @@ function FLIGHTGROUP:Status() -- Check damage. self:_CheckDamage() - + + -- Get current mission (if any). + local mission=self:GetMissionCurrent() + -- TODO: Check if group is waiting? if self:IsWaiting() then if self.Twaiting and self.dTwait then if timer.getAbsTime()>self.Twaiting+self.dTwait then --self.Twaiting=nil --self.dTwait=nil - --self:Cruise() + --self:_CheckGroupDone() end end end - -- Get current mission (if any). - local mission=self:GetMissionCurrent() - -- If mission, check if DCS task needs to be updated. if mission and mission.updateDCSTask then @@ -1363,7 +1363,7 @@ function FLIGHTGROUP:Status() else -- Check damage. - self:_CheckDamage() + self:_CheckDamage() end --- @@ -1617,9 +1617,15 @@ function FLIGHTGROUP:Status() --- self:_PrintTaskAndMissionStatus() - - -- Current mission. + + -- All done? + -- Get current mission (if any). local mission=self:GetMissionCurrent() + if not mission then + self.Twaiting=nil + self.dTwait=nil + self:_CheckGroupDone() + end end From 7f650913d959ed97b9e43967943f67a72f728fd3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 7 Nov 2023 11:07:28 +0100 Subject: [PATCH 397/603] #EVENT * Add player UCID to event data structure (for multi-player) --- Moose Development/Moose/Core/Event.lua | 18 ++++++++++++++++++ Moose Development/Moose/Utilities/Utils.lua | 11 +++++++---- Moose Development/Moose/Wrapper/Net.lua | 4 +--- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 201bb5b6f..2479e8823 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -283,6 +283,7 @@ EVENTS = { -- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Wrapper.Group#GROUP} of the initiator Group object. -- @field #string IniGroupName UNIT) The initiating GROUP name (same as IniDCSGroupName). -- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot. +-- @field #string IniPlayerUCID (UNIT) The UCID of the initiating player in case the Unit is a client or player slot and on a multi-player server. -- @field DCS#coalition.side IniCoalition (UNIT) The coalition of the initiator. -- @field DCS#Unit.Category IniCategory (UNIT) The category of the initiator. -- @field #string IniTypeName (UNIT) The type name of the initiator. @@ -298,6 +299,7 @@ EVENTS = { -- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Wrapper.Group#GROUP} of the target Group object. -- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName). -- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot. +-- @field #string TgtPlayerUCID (UNIT) The UCID of the target player in case the Unit is a client or player slot and on a multi-player server. -- @field DCS#coalition.side TgtCoalition (UNIT) The coalition of the target. -- @field DCS#Unit.Category TgtCategory (UNIT) The category of the target. -- @field #string TgtTypeName (UNIT) The type name of the target. @@ -1145,6 +1147,14 @@ function EVENT:onEvent( Event ) end Event.IniPlayerName = Event.IniDCSUnit:getPlayerName() + if Event.IniPlayerName then + -- get UUCID + local PID = NET.GetPlayerIDByName(nil,Event.IniPlayerName) + if PID then + Event.IniPlayerUCID = net.get_player_info(tonumber(PID), 'ucid') + --env.info("Event.IniPlayerUCID="..tostring(Event.IniPlayerUCID),false) + end + end Event.IniCoalition = Event.IniDCSUnit:getCoalition() Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniCategory = Event.IniDCSUnit:getDesc().category @@ -1217,6 +1227,14 @@ function EVENT:onEvent( Event ) Event.TgtGroupName = Event.TgtDCSGroupName end Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() + if Event.TgtPlayerName then + -- get UUCID + local PID = NET.GetPlayerIDByName(nil,Event.TgtPlayerName) + if PID then + Event.TgtPlayerUCID = net.get_player_info(tonumber(PID), 'ucid') + --env.info("Event.TgtPlayerUCID="..tostring(Event.TgtPlayerUCID),false) + end + end Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() Event.TgtCategory = Event.TgtDCSUnit:getDesc().category Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index ab607f1b7..24d29e0c9 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -441,19 +441,22 @@ UTILS.BasicSerialize = function(s) end end +--- Print a table to log in a nice format +-- @param #table table The table to print +-- @param #number ident Number of idents function UTILS.PrintTableToLog(table, indent) if not table then - BASE:E("No table passed!") + env.warning("No table passed!") return end if not indent then indent = 0 end for k, v in pairs(table) do if type(v) == "table" then - BASE:I(string.rep(" ", indent) .. tostring(k) .. " = {") + env.info(string.rep(" ", indent) .. tostring(k) .. " = {") UTILS.PrintTableToLog(v, indent + 1) - BASE:I(string.rep(" ", indent) .. "}") + env.info(string.rep(" ", indent) .. "}") else - BASE:I(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v)) + env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v)) end end end diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index 289d13155..3d01adc24 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -470,11 +470,9 @@ end -- @return #number PlayerID or nil function NET:GetPlayerIDByName(Name) if not Name then return nil end - local playerList = self:GetPlayerList() - self:T({playerList}) + local playerList = net.get_player_list() for i=1,#playerList do local playerName = net.get_name(i) - self:T({playerName}) if playerName == Name then return playerList[i] end From d5a9f776f7675a893460e0476a27f9ae54cbdfb4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 8 Nov 2023 11:09:49 +0100 Subject: [PATCH 398/603] Various --- Moose Development/Moose/Ops/FlightControl.lua | 16 +++++++++++++--- Moose Development/Moose/Wrapper/Airbase.lua | 2 +- Moose Development/Moose/Wrapper/Net.lua | 4 ++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 6d26c78f6..bab56059a 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -329,7 +329,7 @@ FLIGHTCONTROL.FlightStatus={ --- FlightControl class version. -- @field #string version -FLIGHTCONTROL.version="0.7.4" +FLIGHTCONTROL.version="0.7.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -4441,11 +4441,21 @@ function FLIGHTCONTROL:SpawnParkingGuard(unit) -- Length of the unit + 3 meters. local size, x, y, z=unit:GetObjectSize() + local xdiff = 3 + --Fix for hangars, puts the guy out front and not on top. + if AIRBASE._CheckTerminalType(spot.TerminalType, AIRBASE.TerminalType.Shelter) then + xdiff = 27-(x*0.5) + end + + if (AIRBASE._CheckTerminalType(spot.TerminalType, AIRBASE.TerminalType.OpenMed) or AIRBASE._CheckTerminalType(spot.TerminalType, AIRBASE.TerminalType.Shelter)) and self.airbasename == AIRBASE.Sinai.Ramon_Airbase then + xdiff = 12 + end + -- Debug message. - self:T2(self.lid..string.format("Parking guard for %s: heading=%d, distance x=%.1f m", unit:GetName(), heading, x)) + self:T2(self.lid..string.format("Parking guard for %s: heading=%d, length x=%.1f m, xdiff=%.1f m", unit:GetName(), heading, x, xdiff)) -- Coordinate for the guard. - local Coordinate=coordinate:Translate(0.75*x+3, heading) + local Coordinate=coordinate:Translate(x*0.5+xdiff, heading) -- Let him face the aircraft. local lookat=heading-180 diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index c8c55c81b..da1510608 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1773,7 +1773,7 @@ function AIRBASE:_CheckParkingLists(TerminalID) end --- Helper function to check for the correct terminal type including "artificial" ones. --- @param #number Term_Type Termial type from getParking routine. +-- @param #number Term_Type Terminal type from getParking routine. -- @param #AIRBASE.TerminalType termtype Terminal type from AIRBASE.TerminalType enumerator. -- @return #boolean True if terminal types match. function AIRBASE._CheckTerminalType(Term_Type, termtype) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index 3d01adc24..761b6829b 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -5,7 +5,7 @@ -- === -- -- ### Author: **Applevangelist** --- # Last Update June 2023 +-- # Last Update Oct 2023 -- -- === -- @@ -43,7 +43,7 @@ do -- @field #NET NET = { ClassName = "NET", - Version = "0.1.2", + Version = "0.1.3", BlockTime = 600, BlockedPilots = {}, BlockedUCIDs = {}, From a7366103c92d3356c9b0446325ffa7efc0f6428d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 8 Nov 2023 11:24:34 +0100 Subject: [PATCH 399/603] CLIENT --- Moose Development/Moose/Wrapper/Client.lua | 35 ++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index d53630890..e2a29aa02 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -531,8 +531,6 @@ function CLIENT:ShowCargo() end - - --- The main message driver for the CLIENT. -- This function displays various messages to the Player logged into the CLIENT through the DCS World Messaging system. -- @param #CLIENT self @@ -578,3 +576,36 @@ function CLIENT:Message( Message, MessageDuration, MessageCategory, MessageInter end end end + +--- [Multi-Player Server] Get UCID from a CLIENT. +-- @param #CLIENT self +-- @return #string UCID +function CLIENT:GetUCID() + local PID = NET.GetPlayerIDByName(nil,self:GetPlayerName()) + return net.get_player_info(tonumber(PID), 'ucid') +end + +--- [Multi-Player Server] Return a table of attributes for a given CLIENT. If optional attribute is present, only that value is returned. +-- @param #CLIENT self +-- @param #string Attribute (Optional) The attribute to obtain. List see below. +-- @return #table PlayerInfo or nil if it cannot be found +-- @usage +-- Returned table holds these attributes: +-- +-- 'id' : player ID +-- 'name' : player name +-- 'side' : 0 - spectators, 1 - red, 2 - blue +-- 'slot' : slot ID of the player or +-- 'ping' : ping of the player in ms +-- 'ipaddr': IP address of the player, SERVER ONLY +-- 'ucid' : Unique Client Identifier, SERVER ONLY +-- +function CLIENT:GetPlayerInfo(Attribute) + local PID = NET.GetPlayerIDByName(nil,self:GetPlayerName()) + if PID then + return net.get_player_info(tonumber(PID), Attribute) + else + return nil + end +end + From 40da0fbb1cafbe1e7fcf70398c2810c95bfde388 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 8 Nov 2023 17:01:32 +0100 Subject: [PATCH 400/603] #PLAYERRECCE * Added doku for OnAfter.. calls --- Moose Development/Moose/Ops/PlayerRecce.lua | 159 ++++++++++++++++++-- 1 file changed, 148 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index e225c96fe..d83d16183 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -104,7 +104,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.19", + version = "0.0.20", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, @@ -268,6 +268,146 @@ function PLAYERRECCE:New(Name, Coalition, PlayerSet) self:I(self.lid.." Started.") + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "Start". Starts the PLAYERRECCE. Note: Start() is called automatically after New(). + -- @function [parent=#PLAYERRECCE] Start + -- @param #PLAYERRECCE self + + --- Triggers the FSM event "Start" after a delay. Starts the PLAYERRECCE. Note: Start() is called automatically after New(). + -- @function [parent=#PLAYERRECCE] __Start + -- @param #PLAYERRECCE self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Stop". Stops the PLAYERRECCE and all its event handlers. + -- @param #PLAYERRECCE self + + --- Triggers the FSM event "Stop" after a delay. Stops the PLAYERRECCE and all its event handlers. + -- @function [parent=#PLAYERRECCE] __Stop + -- @param #PLAYERRECCE self + -- @param #number delay Delay in seconds. + + --- FSM Function OnAfterRecceOnStation. Recce came on station. + -- @function [parent=#PLAYERRECCE] OnAfterRecceOnStation + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client + -- @param #string Playername + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterRecceOffStation. Recce went off duty. + -- @function [parent=#PLAYERRECCE] OnAfterRecceOffStation + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client + -- @param #string Playername + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterTargetDetected. Targets detected. + -- @function [parent=#PLAYERRECCE] OnAfterTargetDetected + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param #table Targetsbyclock #table with index 1..12 containing a #table of Wrapper.Unit#UNIT objects each. + -- @param Wrapper.Client#CLIENT Client + -- @param #string Playername + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterTargetsSmoked. Smoke grenade shot. + -- @function [parent=#PLAYERRECCE] OnAfterTargetsSmoked + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client + -- @param #string Playername + -- @param Core.Set#SET_UNIT TargetSet + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterTargetsFlared. Flares shot. + -- @function [parent=#PLAYERRECCE] OnAfterTargetsFlared + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client + -- @param #string Playername + -- @param Core.Set#SET_UNIT TargetSet + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterIllumination. Illumination rocket shot. + -- @function [parent=#PLAYERRECCE] OnAfterIllumination + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client + -- @param #string Playername + -- @param Core.Set#SET_UNIT TargetSet + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterTargetLasing. Lasing a new target. + -- @function [parent=#PLAYERRECCE] OnAfterTargetLasing + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Unit#UNIT Target + -- @param #number Lasercode + -- @param #number Lasingtime + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterTargetLOSLost. Lost LOS on lased target. + -- @function [parent=#PLAYERRECCE] OnAfterTargetLOSLost + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Unit#UNIT Target + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterTargetReport. Laser target report sent. + -- @function [parent=#PLAYERRECCE] OnAfterTargetReport + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client + -- @param Core.Set#SET_UNIT TargetSet + -- @param Wrapper.Unit#UNIT Target Target currently lased + -- @param #string Text + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterTargetReportSent. All targets report sent. + -- @function [parent=#PLAYERRECCE] OnAfterTargetReportSent + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client Client sending the report + -- @param #string Playername Player name + -- @param Core.Set#SET_UNIT TargetSet Set of targets + -- @return #PLAYERRECCE self + + --- FSM Function OnAfterShack. Lased target has been destroyed. + -- @function [parent=#PLAYERRECCE] OnAfterShack + -- @param #PLAYERRECCE self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Client#CLIENT Client + -- @param Wrapper.Unit#UNIT Target The destroyed target (if obtainable) + -- @return #PLAYERRECCE self + return self end @@ -399,7 +539,7 @@ end -- @param #PLAYERRECCE self -- @param Wrapper.Client#CLIENT client -- @param #string playername --- @return #boolen OnOff +-- @return #boolean OnOff function PLAYERRECCE:_CameraOn(client,playername) local camera = true local unit = client -- Wrapper.Unit#UNIT @@ -1582,7 +1722,7 @@ end -- @param #string From -- @param #string Event -- @param #string To --- @param #table Targetsbyclock +-- @param #table Targetsbyclock. #table with index 1..12 containing a #table of Wrapper.Unit#UNIT objects each. -- @param Wrapper.Client#CLIENT Client -- @param #string Playername -- @return #PLAYERRECCE self @@ -1862,9 +2002,8 @@ end -- @param #string To -- @param Wrapper.Client#CLIENT Client -- @param Wrapper.Unit#UNIT Target --- @param #string Targettype -- @return #PLAYERRECCE self -function PLAYERRECCE:onafterShack(From, Event, To, Client, Target, Targettype) +function PLAYERRECCE:onafterShack(From, Event, To, Client, Target) self:T({From, Event, To}) local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS @@ -1972,7 +2111,6 @@ function PLAYERRECCE:onafterTargetReport(From, Event, To, Client, TargetSet, Tar end end end - --self:__TargetReportSent(-2,Client, TargetSet, Target, Text) return self end @@ -1981,12 +2119,11 @@ end -- @param #string From -- @param #string Event -- @param #string To --- @param Wrapper.Client#CLIENT Client --- @param Core.Set#SET_UNIT TargetSet --- @param Wrapper.Unit#UNIT Target --- @param #string Text +-- @param Wrapper.Client#CLIENT Client Client sending the report +-- @param #string Playername Player name +-- @param Core.Set#SET_UNIT TargetSet Set of targets -- @return #PLAYERRECCE self -function PLAYERRECCE:onafterTargetReportSent(From, Event, To, Client, TargetSet) +function PLAYERRECCE:onafterTargetReportSent(From, Event, To, Client, Playername, TargetSet) self:T({From, Event, To}) local text = "Upload completed!" if self.UseSRS then From 7913b83f200a8b0870816d43a2b3f6f49ebc4515 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 8 Nov 2023 17:43:39 +0100 Subject: [PATCH 401/603] matching --- Moose Development/Moose/Wrapper/Group.lua | 60 +++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 5ac314940..2868918f4 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -31,7 +31,8 @@ -- ### Contributions: -- -- * **Entropy**, **Afinegan**: Came up with the requirement for AIOnOff(). --- +-- * **Applevangelist**: various +-- -- === -- -- @module Wrapper.Group @@ -45,12 +46,16 @@ --- Wrapper class of the DCS world Group object. -- +-- ## Finding groups +-- -- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance: -- -- * @{#GROUP.Find}(): Find a GROUP instance from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using a DCS Group object. -- * @{#GROUP.FindByName}(): Find a GROUP instance from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using a DCS Group name. +-- * @{#GROUP:FindByMatching}(): Find a GROUP instance from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using pattern matching. +-- * @{#GROUP:FindByAllMatching}(): Find all GROUP instances from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using pattern matching. -- --- # 1. Tasking of groups +-- ## Tasking of groups -- -- A GROUP is derived from the wrapper class CONTROLLABLE (@{Wrapper.Controllable#CONTROLLABLE}). -- See the @{Wrapper.Controllable} task methods section for a description of the task methods. @@ -285,7 +290,7 @@ function GROUP:Find( DCSGroup ) return GroupFound end ---- Find the created GROUP using the DCS Group Name. +--- Find a GROUP using the DCS Group Name. -- @param #GROUP self -- @param #string GroupName The DCS Group Name. -- @return #GROUP The GROUP. @@ -295,6 +300,55 @@ function GROUP:FindByName( GroupName ) return GroupFound end +--- Find the first(!) GROUP matching using patterns. Note that this is **a lot** slower than `:FindByName()`! +-- @param #GROUP self +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA. +-- @return #GROUP The GROUP. +-- @usage +-- -- Find a group with a partial group name +-- local grp = GROUP:FindByMatching( "Apple" ) +-- -- will return e.g. a group named "Apple-1-1" +-- +-- -- using a pattern +-- local grp = GROUP:FindByMatching( ".%d.%d$" ) +-- -- will return the first group found ending in "-1-1" to "-9-9", but not e.g. "-10-1" +function GROUP:FindByMatching( Pattern ) + local GroupFound = nil + + for name,group in pairs(_DATABASE.GROUPS) do + if string.match(name, Pattern ) then + GroupFound = group + break + end + end + + return GroupFound +end + +--- Find all GROUP objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`! +-- @param #GROUP self +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA. +-- @return #table Groups Table of matching #GROUP objects found +-- @usage +-- -- Find all group with a partial group name +-- local grptable = GROUP:FindAllByMatching( "Apple" ) +-- -- will return all groups with "Apple" in the name +-- +-- -- using a pattern +-- local grp = GROUP:FindAllByMatching( ".%d.%d$" ) +-- -- will return the all groups found ending in "-1-1" to "-9-9", but not e.g. "-10-1" or "-1-10" +function GROUP:FindAllByMatching( Pattern ) + local GroupsFound = {} + + for name,group in pairs(_DATABASE.GROUPS) do + if string.match(name, Pattern ) then + GroupsFound[#GroupsFound+1] = group + end + end + + return GroupsFound +end + -- DCS Group methods support. --- Returns the DCS Group. From 7b57bf3eceb3541f53a4292e59194c4bcc4d891b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 9 Nov 2023 15:05:25 +0100 Subject: [PATCH 402/603] Fixes --- Moose Development/Moose/Core/Base.lua | 2 +- Moose Development/Moose/Core/ClientMenu.lua | 94 +++++++++++++++++++-- Moose Development/Moose/Core/Database.lua | 68 +++++++++------ Moose Development/Moose/Ops/PlayerTask.lua | 18 ++-- Moose Development/Moose/Utilities/Utils.lua | 2 +- 5 files changed, 144 insertions(+), 40 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index ce3675189..595f89279 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -1341,7 +1341,7 @@ function BASE:I( Arguments ) env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) ) else - env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.BasicSerialize( Arguments ) ) ) + env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.OneLineSerialize( Arguments ) ) ) end end diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index d5ace7d9e..ff52def04 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -20,7 +20,7 @@ -- -- @module Core.ClientMenu -- @image Core_Menu.JPG --- last change: Sept 2023 +-- last change: Oct 2023 -- TODO ---------------------------------------------------------------------------------------------------------------- @@ -304,6 +304,8 @@ end -- @field #table menutree -- @field #number entrycount -- @field #boolean debug +-- @field #table PlayerMenu +-- @field #number Coalition -- @extends Core.Base#BASE --- *As a child my family's menu consisted of two choices: take it, or leave it.* @@ -390,7 +392,7 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.1.1", + version = "0.1.3", name = nil, clientset = nil, menutree = {}, @@ -399,26 +401,108 @@ CLIENTMENUMANAGER = { entrycount = 0, rootentries = {}, debug = true, + PlayerMenu = {}, + Coalition = nil, } --- Create a new ClientManager instance. -- @param #CLIENTMENUMANAGER self -- @param Core.Set#SET_CLIENT ClientSet The set of clients to manage. -- @param #string Alias The name of this manager. +-- @param #number Coalition (Optional) Coalition of this Manager, defaults to coalition.side.BLUE -- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:New(ClientSet, Alias) +function CLIENTMENUMANAGER:New(ClientSet, Alias, Coalition) -- Inherit everything from FSM class. local self=BASE:Inherit(self, BASE:New()) -- #CLIENTMENUMANAGER self.clientset = ClientSet + self.PlayerMenu = {} self.name = Alias or "Nightshift" + self.Coalition = Coalition or coalition.side.BLUE -- Log id. self.lid=string.format("CLIENTMENUMANAGER %s | %s | ", self.version, self.name) if self.debug then - self:T(self.lid.."Created") + self:I(self.lid.."Created") end return self end +--- [Internal] Event handling +-- @param #CLIENTMENUMANAGER self +-- @param Core.Event#EVENTDATA EventData +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:_EventHandler(EventData) + self:T(self.lid.."_EventHandler: "..EventData.id) + --self:I(self.lid.."_EventHandler: "..tostring(EventData.IniPlayerName)) + if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then + self:T(self.lid.."Leave event for player: "..tostring(EventData.IniPlayerName)) + local Client = _DATABASE:FindClient( EventData.IniPlayerName ) + if Client then + self:ResetMenu(Client) + end + elseif (EventData.id == EVENTS.PlayerEnterAircraft) and EventData.IniCoalition == self.Coalition then + if EventData.IniPlayerName and EventData.IniGroup then + if (not self.clientset:IsIncludeObject(_DATABASE:FindClient( EventData.IniPlayerName ))) then + self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName) + return self + end + --self:I(self.lid.."Join event for player: "..EventData.IniPlayerName) + local player = _DATABASE:FindClient( EventData.IniPlayerName ) + self:Propagate(player) + end + elseif EventData.id == EVENTS.PlayerEnterUnit then + -- special for CA slots + local grp = GROUP:FindByName(EventData.IniGroupName) + if grp:IsGround() then + self:T(string.format("Player %s entered GROUND unit %s!",EventData.IniPlayerName,EventData.IniUnitName)) + local IsPlayer = EventData.IniDCSUnit:getPlayerName() + if IsPlayer then + + local client=_DATABASE.CLIENTS[EventData.IniDCSUnitName] --Wrapper.Client#CLIENT + + -- Add client in case it does not exist already. + if not client then + + -- Debug info. + self:I(string.format("Player '%s' joined ground unit '%s' of group '%s'", tostring(EventData.IniPlayerName), tostring(EventData.IniDCSUnitName), tostring(EventData.IniDCSGroupName))) + + client=_DATABASE:AddClient(EventData.IniDCSUnitName) + + -- Add player. + client:AddPlayer(EventData.IniPlayerName) + + -- Add player. + if not _DATABASE.PLAYERS[EventData.IniPlayerName] then + _DATABASE:AddPlayer( EventData.IniUnitName, EventData.IniPlayerName ) + end + + -- Player settings. + local Settings = SETTINGS:Set( EventData.IniPlayerName ) + Settings:SetPlayerMenu(EventData.IniUnit) + end + --local player = _DATABASE:FindClient( EventData.IniPlayerName ) + self:Propagate(client) + end + end + end + + return self +end + +--- Set this Client Manager to auto-propagate menus to newly joined players. Useful if you have **one** menu structure only. +-- @param #CLIENTMENUMANAGER self +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:InitAutoPropagation() + -- Player Events + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) + self:HandleEvent(EVENTS.Ejection, self._EventHandler) + self:HandleEvent(EVENTS.Crash, self._EventHandler) + self:HandleEvent(EVENTS.PilotDead, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler) + self:SetEventPriority(5) + return self +end + --- Create a new entry in the generic structure. -- @param #CLIENTMENUMANAGER self -- @param #string Text Text of the F10 menu entry. @@ -571,7 +655,7 @@ end -- @return #CLIENTMENU Entry function CLIENTMENUMANAGER:Propagate(Client) self:T(self.lid.."Propagate") - self:T(Client) + --self:I(UTILS.PrintTableToLog(Client,1)) local Set = self.clientset.Set if Client then Set = {Client} diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 1e8bd40d4..75f7be002 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -126,6 +126,8 @@ function DATABASE:New() self:SetEventPriority( 1 ) self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + -- DCS 2.9 fixed CA event for players -- TODO: reset unit when leaving + self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) @@ -810,6 +812,7 @@ function DATABASE:AddPlayer( UnitName, PlayerName ) self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName ) self.PLAYERSJOINED[PlayerName] = PlayerName end + end --- Deletes a player from the DATABASE based on the Player Name. @@ -1470,39 +1473,43 @@ function DATABASE:_EventOnDeadOrCrash( Event ) end ---- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). +--- Handles the OnPlayerEnterUnit event to fill the active players table for CA units (with the unit filter applied). -- @param #DATABASE self -- @param Core.Event#EVENTDATA Event function DATABASE:_EventOnPlayerEnterUnit( Event ) self:F2( { Event } ) if Event.IniDCSUnit then - if Event.IniObjectCategory == 1 then + -- Player entering a CA slot + if Event.IniObjectCategory == 1 and Event.IniGroup and Event.IniGroup:IsGround() then + + local IsPlayer = Event.IniDCSUnit:getPlayerName() + if IsPlayer then - -- Add unit. - self:AddUnit( Event.IniDCSUnitName ) + -- Debug info. + self:I(string.format("Player '%s' joined GROUND unit '%s' of group '%s'", tostring(Event.IniPlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName))) + + local client= self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT + + -- Add client in case it does not exist already. + if not client then + client=self:AddClient(Event.IniDCSUnitName) + end + + -- Add player. + client:AddPlayer(Event.IniPlayerName) - -- Ini unit. - Event.IniUnit = self:FindUnit( Event.IniDCSUnitName ) - - -- Add group. - self:AddGroup( Event.IniDCSGroupName ) - - -- Get player unit. - local PlayerName = Event.IniDCSUnit:getPlayerName() - - if PlayerName then - - if not self.PLAYERS[PlayerName] then - self:AddPlayer( Event.IniDCSUnitName, PlayerName ) + -- Add player. + if not self.PLAYERS[Event.IniPlayerName] then + self:AddPlayer( Event.IniUnitName, Event.IniPlayerName ) end - local Settings = SETTINGS:Set( PlayerName ) - Settings:SetPlayerMenu( Event.IniUnit ) + -- Player settings. + local Settings = SETTINGS:Set( Event.IniPlayerName ) + Settings:SetPlayerMenu(Event.IniUnit) - else - self:E("ERROR: getPlayerName() returned nil for event PlayerEnterUnit") end + end end end @@ -1513,15 +1520,26 @@ end -- @param Core.Event#EVENTDATA Event function DATABASE:_EventOnPlayerLeaveUnit( Event ) self:F2( { Event } ) - + + local function FindPlayerName(UnitName) + local playername = nil + for _name,_unitname in pairs(self.PLAYERS) do + if _unitname == UnitName then + playername = _name + break + end + end + return playername + end + if Event.IniUnit then if Event.IniObjectCategory == 1 then -- Try to get the player name. This can be buggy for multicrew aircraft! - local PlayerName = Event.IniUnit:GetPlayerName() - - if PlayerName then --and self.PLAYERS[PlayerName] then + local PlayerName = Event.IniUnit:GetPlayerName() or FindPlayerName(Event.IniUnitName) + + if PlayerName then -- Debug info. self:I(string.format("Player '%s' left unit %s", tostring(PlayerName), tostring(Event.IniUnitName))) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index c840f5538..a4d019531 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1552,7 +1552,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.62" +PLAYERTASKCONTROLLER.version="0.1.63" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -1585,7 +1585,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.TaskQueue = FIFO:New() -- Utilities.FiFo#FIFO self.TasksPerPlayer = FIFO:New() -- Utilities.FiFo#FIFO self.PrecisionTasks = FIFO:New() -- Utilities.FiFo#FIFO - self.PlayerMenu = {} -- #table + --self.PlayerMenu = {} -- #table self.FlashPlayer = {} -- #table self.AllowFlash = false self.lasttaskcount = 0 @@ -2175,10 +2175,10 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then if EventData.IniPlayerName then self:T(self.lid.."Event for player: "..EventData.IniPlayerName) - if self.PlayerMenu[EventData.IniPlayerName] then - self.PlayerMenu[EventData.IniPlayerName]:Remove() - self.PlayerMenu[EventData.IniPlayerName] = nil - end + --if self.PlayerMenu[EventData.IniPlayerName] then + --self.PlayerMenu[EventData.IniPlayerName]:Remove() + --self.PlayerMenu[EventData.IniPlayerName] = nil + --end local text = "" if self.TasksPerPlayer:HasUniqueID(EventData.IniPlayerName) then local task = self.TasksPerPlayer:PullByID(EventData.IniPlayerName) -- Ops.PlayerTask#PLAYERTASK @@ -2187,6 +2187,8 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) task:RemoveClient(Client) --text = "Task aborted!" text = self.gettext:GetEntry("TASKABORT",self.locale) + self.ActiveTaskMenuTemplate:ResetMenu(Client) + self.JoinTaskMenuTemplate:ResetMenu(Client) else task:RemoveClient(nil,EventData.IniPlayerName) --text = "Task aborted!" @@ -2236,8 +2238,8 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation) end if EventData.IniPlayerName then - self.PlayerMenu[EventData.IniPlayerName] = nil - local player = CLIENT:FindByName(EventData.IniUnitName) + --self.PlayerMenu[EventData.IniPlayerName] = nil + local player = _DATABASE:FindClient( EventData.IniPlayerName ) self:_SwitchMenuForClient(player,"Info") end end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 24d29e0c9..33350e85e 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -443,7 +443,7 @@ end --- Print a table to log in a nice format -- @param #table table The table to print --- @param #number ident Number of idents +-- @param #number indent Number of idents function UTILS.PrintTableToLog(table, indent) if not table then env.warning("No table passed!") From 847df24ab05f4dc8e4d35de49fe5341cc6c46eda Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 9 Nov 2023 15:16:35 +0100 Subject: [PATCH 403/603] docu --- Moose Development/Moose/Core/ClientMenu.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 85bbeb2d4..7d4fecde6 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -347,7 +347,7 @@ end -- local mymenu_lv3b = menumgr:NewEntry("Level 3 aab",mymenu_lv2a) -- local mymenu_lv3c = menumgr:NewEntry("Level 3 aac",mymenu_lv2a) -- --- menumgr:Propagate() +-- menumgr:Propagate() -- propagate **once** to all clients in the SET_CLIENT -- -- ## Remove a single entry's subtree -- @@ -386,7 +386,11 @@ end -- -- ## Reset all and clear the reference tree -- --- menumgr:ResetMenuComplete() +-- menumgr:ResetMenuComplete() +-- +-- ## Set to auto-propagate for CLIENTs joining the SET_CLIENT **after** the script is loaded - handy if you have a single menu tree. +-- +-- menumgr:InitAutoPropagation() -- -- @field #CLIENTMENUMANAGER CLIENTMENUMANAGER = { From fb4914a120a348e85a80f5c1278f3e133aa5c3a7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 11 Nov 2023 19:01:51 +0100 Subject: [PATCH 404/603] changes --- Moose Development/Moose/Core/ClientMenu.lua | 2 +- Moose Development/Moose/Core/Set.lua | 124 ++++++++++++------ .../Moose/Functional/Autolase.lua | 76 +++++++++-- Moose Development/Moose/Ops/PlayerRecce.lua | 2 +- 4 files changed, 150 insertions(+), 54 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 7d4fecde6..1ec4f01a6 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -668,7 +668,7 @@ function CLIENTMENUMANAGER:Propagate(Client) for _,_client in pairs(Set) do local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then - local playername = client:GetPlayerName() + local playername = client:GetPlayerName() or "none" if not self.playertree[playername] then self.playertree[playername] = {} end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 6082a9fd6..e6e308c8d 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4030,8 +4030,8 @@ do -- SET_CLIENT Countries = nil, ClientPrefixes = nil, Zones = nil, - Playernames = nil, - Callsigns = nil, + Playernames = nil, + Callsigns = nil, }, FilterMeta = { Coalitions = { @@ -4317,6 +4317,8 @@ do -- SET_CLIENT self:UnHandleEvent(EVENTS.Birth) self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.Crash) + --self:UnHandleEvent(EVENTS.PlayerEnterUnit) + --self:UnHandleEvent(EVENTS.PlayerLeaveUnit) if self.Filter.Zones and self.ZoneTimer and self.ZoneTimer:IsRunning() then self.ZoneTimer:Stop() @@ -4335,6 +4337,9 @@ do -- SET_CLIENT self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventPlayerEnterUnit) + --self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventPlayerLeaveUnit) + --self:SetEventPriority(1) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) local timing = self.ZoneTimerInterval or 30 @@ -4345,6 +4350,43 @@ do -- SET_CLIENT return self end + + --- Handle CA slots addition + -- @param #SET_CLIENT self + -- @param Core.Event#EVENTDATA Event + -- @return #SET_CLIENT self + function SET_CLIENT:_EventPlayerEnterUnit(Event) + self:I( "_EventPlayerEnterUnit" ) + if Event.IniDCSUnit then + if Event.IniObjectCategory == 1 and Event.IniGroup and Event.IniGroup:IsGround() then + -- CA Slot entered + local ObjectName, Object = self:AddInDatabase( Event ) + self:I( ObjectName, UTILS.PrintTableToLog(Object) ) + if Object and self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) + end + end + end + return self + end + + --- Handle CA slots removal + -- @param #SET_CLIENT self + -- @param Core.Event#EVENTDATA Event + -- @return #SET_CLIENT self + function SET_CLIENT:_EventPlayerLeaveUnit(Event) + self:I( "_EventPlayerLeaveUnit" ) + if Event.IniDCSUnit then + if Event.IniObjectCategory == 1 and Event.IniGroup and Event.IniGroup:IsGround() then + -- CA Slot left + local ObjectName, Object = self:FindInDatabase( Event ) + if ObjectName then + self:Remove( ObjectName ) + end + end + end + return self + end --- Handles the Database to check on an event (birth) that the Object was added in the Database. -- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! @@ -4548,45 +4590,45 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientPrefix end - if self.Filter.Zones then - local MClientZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - self:T3( "Zone:", ZoneName ) - local unit = MClient:GetClientGroupUnit() - if unit and unit:IsInZone(Zone) then - MClientZone = true - end - end - MClientInclude = MClientInclude and MClientZone - end - - if self.Filter.Playernames then - local MClientPlayername = false - local playername = MClient:GetPlayerName() or "Unknown" - --self:T(playername) - for _,_Playername in pairs(self.Filter.Playernames) do - if playername and string.find(playername,_Playername) then - MClientPlayername = true - end - end - self:T( { "Evaluated Playername", MClientPlayername } ) - MClientInclude = MClientInclude and MClientPlayername - end - - if self.Filter.Callsigns then - local MClientCallsigns = false - local callsign = MClient:GetCallsign() - --self:I(callsign) - for _,_Callsign in pairs(self.Filter.Callsigns) do - if callsign and string.find(callsign,_Callsign,1,true) then - MClientCallsigns = true - end - end - self:T( { "Evaluated Callsign", MClientCallsigns } ) - MClientInclude = MClientInclude and MClientCallsigns - end - - end + if self.Filter.Zones then + local MClientZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + self:T3( "Zone:", ZoneName ) + local unit = MClient:GetClientGroupUnit() + if unit and unit:IsInZone(Zone) then + MClientZone = true + end + end + MClientInclude = MClientInclude and MClientZone + end + + if self.Filter.Playernames then + local MClientPlayername = false + local playername = MClient:GetPlayerName() or "Unknown" + --self:T(playername) + for _,_Playername in pairs(self.Filter.Playernames) do + if playername and string.find(playername,_Playername) then + MClientPlayername = true + end + end + self:T( { "Evaluated Playername", MClientPlayername } ) + MClientInclude = MClientInclude and MClientPlayername + end + + if self.Filter.Callsigns then + local MClientCallsigns = false + local callsign = MClient:GetCallsign() + --self:I(callsign) + for _,_Callsign in pairs(self.Filter.Callsigns) do + if callsign and string.find(callsign,_Callsign,1,true) then + MClientCallsigns = true + end + end + self:T( { "Evaluated Callsign", MClientCallsigns } ) + MClientInclude = MClientInclude and MClientCallsigns + end + + end self:T2( MClientInclude ) return MClientInclude end diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 109a5c335..381d013b9 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -74,7 +74,7 @@ -- @image Designation.JPG -- -- Date: 24 Oct 2021 --- Last Update: Aug 2022 +-- Last Update: Oct 2023 -- --- Class AUTOLASE -- @type AUTOLASE @@ -84,6 +84,9 @@ -- @field #string alias -- @field #boolean debug -- @field #string version +-- @field Core.Set#SET_GROUP RecceSet +-- @field #table LaserCodes +-- @field #table playermenus -- @extends Ops.Intel#INTEL --- @@ -109,9 +112,10 @@ AUTOLASE = { -- @field #string unittype -- @field Core.Point#COORDINATE coordinate + --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.1.21" +AUTOLASE.version = "0.1.22" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -196,6 +200,8 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self.NoMenus = false self.minthreatlevel = 0 self.blacklistattributes = {} + self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes + self.playermenus = {} -- Set some string id for output to DCS.log file. self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -214,7 +220,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) if PilotSet then self.usepilotset = true self.pilotset = PilotSet - self:HandleEvent(EVENTS.PlayerEnterAircraft) + self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) --self:SetPilotMenu() end --self.SetPilotMenu() @@ -298,6 +304,16 @@ end -- Helper Functions ------------------------------------------------------------------- +--- [User] Set a table of possible laser codes. +-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 } . +-- @param #AUTOLASE self +-- @param #list<#number> LaserCodes +-- @return #AUTOLASE +function AUTOLASE:SetLaserCodes( LaserCodes ) + self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes } + return self +end + --- (Internal) Function to set pilot menu. -- @param #AUTOLASE self -- @return #AUTOLASE self @@ -308,8 +324,28 @@ function AUTOLASE:SetPilotMenu() local Unit = _unit -- Wrapper.Unit#UNIT if Unit and Unit:IsAlive() then local Group = Unit:GetGroup() - local lasemenu = MENU_GROUP_COMMAND:New(Group,"Autolase Status",nil,self.ShowStatus,self,Group,Unit) - lasemenu:Refresh() + local unitname = Unit:GetName() + if self.playermenus[unitname] then self.playermenus[unitname]:Remove() end + local lasetopm = MENU_GROUP:New(Group,"Autolase",nil) + self.playermenus[unitname] = lasetopm + local lasemenu = MENU_GROUP_COMMAND:New(Group,"Status",lasetopm,self.ShowStatus,self,Group,Unit) + for _,_grp in pairs(self.RecceSet.Set) do + local grp = _grp -- Wrapper.Group#GROUP + local unit = grp:GetUnit(1) + --local name = grp:GetName() + if unit and unit:IsAlive() then + local name = unit:GetName() + local mname = string.gsub(name,".%d+.%d+$","") + local code = self:GetLaserCode(name) + local unittop = MENU_GROUP:New(Group,"Change laser code for "..mname,lasetopm) + for _,_code in pairs(self.LaserCodes) do + local text = tostring(_code) + if _code == code then text = text.."(*)" end + local changemenu = MENU_GROUP_COMMAND:New(Group,text,unittop,self.SetRecceLaserCode,self,name,_code,true) + end + end + end + --lasemenu:Refresh() end end else @@ -324,7 +360,7 @@ end -- @param #AUTOLASE self -- @param Core.Event#EVENTDATA EventData -- @return #AUTOLASE self -function AUTOLASE:OnEventPlayerEnterAircraft(EventData) +function AUTOLASE:_EventHandler(EventData) self:SetPilotMenu() return self end @@ -397,7 +433,7 @@ end --- (User) Function enable sending messages via SRS. -- @param #AUTOLASE self -- @param #boolean OnOff Switch usage on and off --- @param #string Path Path to SRS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalon +-- @param #string Path Path to SRS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalone -- @param #number Frequency Frequency to send, e.g. 243 -- @param #number Modulation Modulation i.e. radio.modulation.AM or radio.modulation.FM -- @param #string Label (Optional) Short label to be used on the SRS Client Overlay @@ -465,10 +501,20 @@ end -- @param #AUTOLASE self -- @param #string RecceName (Unit!) Name of the Recce -- @param #number Code The lase code +-- @param #boolean Refresh If true, refresh menu entries -- @return #AUTOLASE self -function AUTOLASE:SetRecceLaserCode(RecceName, Code) +function AUTOLASE:SetRecceLaserCode(RecceName, Code, Refresh) local code = Code or 1688 self.RecceLaserCode[RecceName] = code + if Refresh then + self:SetPilotMenu() + if self.notifypilots then + if string.find(RecceName,"#") then + RecceName = string.match(RecceName,"^(.*)#") + end + self:NotifyPilots(string.format("Code for %s set to: %d",RecceName,Code),15) + end + end return self end @@ -995,6 +1041,9 @@ end function AUTOLASE:onbeforeRecceKIA(From,Event,To,RecceName) self:T({From, Event, To, RecceName}) if self.notifypilots or self.debug then + if string.find(RecceName,"#") then + RecceName = string.match(RecceName,"^(.*)#") + end local text = string.format("Recce %s KIA!",RecceName) self:NotifyPilots(text,self.reporttimeshort) end @@ -1029,6 +1078,9 @@ end function AUTOLASE:onbeforeTargetLost(From,Event,To,UnitName,RecceName) self:T({From, Event, To, UnitName,RecceName}) if self.notifypilots or self.debug then + if string.find(RecceName,"#") then + RecceName = string.match(RecceName,"^(.*)#") + end local text = string.format("%s lost sight of unit %s.",RecceName,UnitName) self:NotifyPilots(text,self.reporttimeshort) end @@ -1046,6 +1098,9 @@ end function AUTOLASE:onbeforeLaserTimeout(From,Event,To,UnitName,RecceName) self:T({From, Event, To, UnitName,RecceName}) if self.notifypilots or self.debug then + if string.find(RecceName,"#") then + RecceName = string.match(RecceName,"^(.*)#") + end local text = string.format("%s laser timeout on unit %s.",RecceName,UnitName) self:NotifyPilots(text,self.reporttimeshort) end @@ -1063,10 +1118,9 @@ function AUTOLASE:onbeforeLasing(From,Event,To,LaserSpot) self:T({From, Event, To, LaserSpot.unittype}) if self.notifypilots or self.debug then local laserspot = LaserSpot -- #AUTOLASE.LaserSpot - local name = laserspot.reccename - if string.find(name,"#") then + local name = laserspot.reccename if string.find(name,"#") then name = string.match(name,"^(.*)#") - end + end local text = string.format("%s is lasing %s code %d\nat %s",name,laserspot.unittype,laserspot.lasercode,laserspot.location) self:NotifyPilots(text,self.reporttimeshort+5) end diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index d83d16183..f06edd09e 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -486,7 +486,7 @@ function PLAYERRECCE:_GetClockDirection(unit, target) end --- [User] Set a table of possible laser codes. --- Each new RECCE can select a code from this table, default is 1688. +-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 }. -- @param #PLAYERRECCE self -- @param #list<#number> LaserCodes -- @return #PLAYERRECCE From 2483cadbbd56bee1e5463923b6f1c4891b78ffb5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 12 Nov 2023 12:12:49 +0100 Subject: [PATCH 405/603] Autolase smoke menu --- Moose Development/Moose/Functional/Autolase.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 381d013b9..5bfbab806 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -329,6 +329,9 @@ function AUTOLASE:SetPilotMenu() local lasetopm = MENU_GROUP:New(Group,"Autolase",nil) self.playermenus[unitname] = lasetopm local lasemenu = MENU_GROUP_COMMAND:New(Group,"Status",lasetopm,self.ShowStatus,self,Group,Unit) + local smoke = (self.smoketargets == true) and "off" or "on" + local smoketext = string.format("Switch smoke targets to %s",smoke) + local smokemenu = MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets)) for _,_grp in pairs(self.RecceSet.Set) do local grp = _grp -- Wrapper.Group#GROUP local unit = grp:GetUnit(1) @@ -570,6 +573,9 @@ end function AUTOLASE:SetSmokeTargets(OnOff,Color) self.smoketargets = OnOff self.smokecolor = Color or SMOKECOLOR.Red + local smktxt = OnOff == true and "on" or "off" + local Message = "Smoking targets is now "..smktxt.."!" + self:NotifyPilots(Message,10) return self end From b9606ceceb059708c2a7cae6ad9e3752eb892492 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 12 Nov 2023 16:53:46 +0100 Subject: [PATCH 406/603] #SEAD * Added data and actions for TALD ADM_141 --- Moose Development/Moose/Functional/Sead.lua | 22 +++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index b80246523..aaeb9afd0 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -19,7 +19,7 @@ -- -- ### Authors: **FlightControl**, **applevangelist** -- --- Last Update: September 2023 +-- Last Update: Oct 2023 -- -- === -- @@ -80,6 +80,7 @@ SEAD = { ["BGM_109"] = "BGM_109", ["AGM_154"] = "AGM_154", ["HY-2"] = "HY-2", + ["ADM_141A"] = "ADM_141A", } --- Missile enumerators - from DCS ME and Wikipedia @@ -100,6 +101,7 @@ SEAD = { ["BGM_109"] = {460, 0.705}, --in-game ~465kn ["AGM_154"] = {130, 0.61}, ["HY-2"] = {90,1}, + ["ADM_141A"] = {126,0.6}, } --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. @@ -143,7 +145,7 @@ function SEAD:New( SEADGroupPrefixes, Padding ) self:AddTransition("*", "ManageEvasion", "*") self:AddTransition("*", "CalculateHitZone", "*") - self:I("*** SEAD - Started Version 0.4.4") + self:I("*** SEAD - Started Version 0.4.5") return self end @@ -348,8 +350,9 @@ end -- @param #string SEADWeaponName -- @param Wrapper.Group#GROUP SEADGroup Attacker Group -- @param #number timeoffset Offset for tti calc +-- @param Wrapper.Weapon#WEAPON Weapon -- @return #SEAD self -function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,timeoffset) +function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,timeoffset,Weapon) local timeoffset = timeoffset or 0 if _targetskill == "Random" then -- when skill is random, choose a skill local Skills = { "Average", "Good", "High", "Excellent" } @@ -372,6 +375,10 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP reach = wpndata[1] * 1.1 local mach = wpndata[2] wpnspeed = math.floor(mach * 340.29) + if Weapon then + wpnspeed = Weapon:GetSpeed() + self:T(string.format("*** SEAD - Weapon Speed from WEAPON: %f m/s",wpnspeed)) + end end -- time to impact local _tti = math.floor(_distance / wpnspeed) - timeoffset -- estimated impact time @@ -457,6 +464,9 @@ function SEAD:HandleEventShot( EventData ) local SEADWeapon = EventData.Weapon -- Identify the weapon fired local SEADWeaponName = EventData.WeaponName -- return weapon type + local WeaponWrapper = WEAPON:New(EventData.Weapon) + --local SEADWeaponSpeed = WeaponWrapper:GetSpeed() -- mps + self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName) --self:T({ SEADWeapon }) @@ -513,7 +523,11 @@ function SEAD:HandleEventShot( EventData ) end end if SEADGroupFound == true then -- yes we are being attacked - self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup) + if string.find(SEADWeaponName,"ADM_141",1,true) then + self:__ManageEvasion(2,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper) + else + self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper) + end end end return self From b32453bd9c363a9cb890a38c60e4c5090c33879c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 14 Nov 2023 11:52:55 +0100 Subject: [PATCH 407/603] #Shoot&Scoot --- Moose Development/Moose/Functional/Mantis.lua | 30 +- Moose Development/Moose/Functional/Sead.lua | 3 +- Moose Development/Moose/Functional/Shorad.lua | 261 +++++++++++------- 3 files changed, 190 insertions(+), 104 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 72de47973..1ae8604ca 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -487,7 +487,11 @@ do -- mybluemantis:Start() -- function MANTIS:New(name,samprefix,ewrprefix,hq,coalition,dynamic,awacs, EmOnOff, Padding, Zones) - + + + -- Inherit everything from BASE class. + local self = BASE:Inherit(self, FSM:New()) -- #MANTIS + -- DONE: Create some user functions for these -- DONE: Make HQ useful -- DONE: Set SAMs to auto if EWR dies @@ -549,6 +553,10 @@ do self.ShoradGroupSet = SET_GROUP:New() -- Core.Set#SET_GROUP self.FilterZones = Zones + self.SkateZones = nil + self.SkateNumber = 3 + self.shootandscoot = false + self.UseEmOnOff = true if EmOnOff == false then self.UseEmOnOff = false @@ -560,9 +568,6 @@ do self.advAwacs = false end - -- Inherit everything from BASE class. - local self = BASE:Inherit(self, FSM:New()) -- #MANTIS - -- Set the string id for output to DCS.log file. self.lid=string.format("MANTIS %s | ", self.name) @@ -787,6 +792,19 @@ do return self end + --- Add a SET_ZONE of zones for Shoot&Scoot - SHORAD units will move around + -- @param #MANTIS self + -- @param Core.Set#SET_ZONE ZoneSet Set of zones to be used. Units will move around to the next (random) zone between 100m and 3000m away. + -- @param #number Number Number of closest zones to be considered, defaults to 3. + -- @return #MANTIS self + function MANTIS:AddScootZones(ZoneSet, Number) + self:T(self.lid .. " AddScootZones") + self.SkateZones = ZoneSet + self.SkateNumber = Number or 3 + self.shootandscoot = true + return self + end + --- Function to set accept and reject zones. -- @param #MANTIS self -- @param #table AcceptZones Table of @{Core.Zone#ZONE} objects @@ -1786,6 +1804,10 @@ do self.Shorad:SetDefenseLimits(80,95) self.ShoradLink = true self.Shorad.Groupset=self.ShoradGroupSet + self.Shorad.debug = self.debug + end + if self.shootandscoot and self.SkateZones then + self.Shorad:AddScootZones(self.SkateZones,self.SkateNumber or 3) end self:__Status(-math.random(1,10)) return self diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index aaeb9afd0..d2f03703f 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -34,7 +34,7 @@ -- -- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon. -- Once a HARM attack is detected, SEAD will shut down the radars of the attacked SAM site and take evasive action by moving the SAM --- vehicles around (*if* they are drivable, that is). There's a component of randomness in detection and evasion, which is based on the +-- vehicles around (*if* they are driveable, that is). There's a component of randomness in detection and evasion, which is based on the -- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a -- period of time to stay defensive, before it takes evasive actions. -- @@ -66,7 +66,6 @@ SEAD = { -- @field Harms SEAD.Harms = { ["AGM_88"] = "AGM_88", - --["AGM_45"] = "AGM_45", ["AGM_122"] = "AGM_122", ["AGM_84"] = "AGM_84", ["AGM_45"] = "AGM_45", diff --git a/Moose Development/Moose/Functional/Shorad.lua b/Moose Development/Moose/Functional/Shorad.lua index 8affe3cf6..ff341ddbb 100644 --- a/Moose Development/Moose/Functional/Shorad.lua +++ b/Moose Development/Moose/Functional/Shorad.lua @@ -21,6 +21,7 @@ -- @image Functional.Shorad.jpg -- -- Date: Nov 2021 +-- Last Update: Nov 2023 ------------------------------------------------------------------------- --- **SHORAD** class, extends Core.Base#BASE @@ -39,8 +40,11 @@ -- @field #boolean DefendHarms Default true, intercept incoming HARMS -- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles -- @field #number DefenseLowProb Default 70, minimum detection limit --- @field #number DefenseHighProb Default 90, maximim detection limit +-- @field #number DefenseHighProb Default 90, maximum detection limit -- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green. +-- @field #boolean shootandscoot +-- @field #number SkateNumber +-- @field Core.Set#SET_ZONE SkateZones -- @extends Core.Base#BASE @@ -99,7 +103,10 @@ SHORAD = { DefendMavs = true, DefenseLowProb = 70, DefenseHighProb = 90, - UseEmOnOff = false, + UseEmOnOff = true, + shootandscoot = false, + SkateNumber = 3, + SkateZones = nil, } ----------------------------------------------------------------------- @@ -112,7 +119,6 @@ do -- @field Harms SHORAD.Harms = { ["AGM_88"] = "AGM_88", - --["AGM_45"] = "AGM_45", ["AGM_122"] = "AGM_122", ["AGM_84"] = "AGM_84", ["AGM_45"] = "AGM_45", @@ -123,6 +129,8 @@ do ["X_25"] = "X_25", ["X_31"] = "X_31", ["Kh25"] = "Kh25", + ["HY-2"] = "HY-2", + ["ADM_141A"] = "ADM_141A", } --- TODO complete list? @@ -134,7 +142,6 @@ do ["Kh29"] = "Kh29", ["Kh31"] = "Kh31", ["Kh66"] = "Kh66", - --["BGM_109"] = "BGM_109", } --- Instantiates a new SHORAD object @@ -146,7 +153,7 @@ do -- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call -- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral" -- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch) - -- @retunr #SHORAD self + -- @return #SHORAD self function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff) local self = BASE:Inherit( self, FSM:New() ) self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition}) @@ -165,8 +172,9 @@ do self.DefendMavs = true self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin - self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green - self:I("*** SHORAD - Started Version 0.3.1") + self.UseEmOnOff = true -- Decide if we are using Emission on/off (default) or AlarmState red/green + if UseEmOnOff == false then self.UseEmOnOff = UseEmOnOff end + self:I("*** SHORAD - Started Version 0.3.2") -- Set the string id for output to DCS.log file. self.lid=string.format("SHORAD %s | ", self.name) self:_InitState() @@ -176,12 +184,14 @@ do self:SetStartState("Running") self:AddTransition("*", "WakeUpShorad", "*") self:AddTransition("*", "CalculateHitZone", "*") + self:AddTransition("*", "ShootAndScoot", "*") return self end --- Initially set all groups to alarm state GREEN -- @param #SHORAD self + -- @return #SHORAD self function SHORAD:_InitState() self:T(self.lid .. " _InitState") local table = {} @@ -205,9 +215,23 @@ do return self end + --- Add a SET_ZONE of zones for Shoot&Scoot + -- @param #SHORAD self + -- @param Core.Set#SET_ZONE ZoneSet Set of zones to be used. Units will move around to the next (random) zone between 100m and 3000m away. + -- @param #number Number Number of closest zones to be considered, defaults to 3. + -- @return #SHORAD self + function SHORAD:AddScootZones(ZoneSet, Number) + self:T(self.lid .. " AddScootZones") + self.SkateZones = ZoneSet + self.SkateNumber = Number or 3 + self.shootandscoot = true + return self + end + --- Switch debug state on -- @param #SHORAD self -- @param #boolean debug Switch debug on (true) or off (false) + -- @return #SHORAD self function SHORAD:SwitchDebug(onoff) self:T( { onoff } ) if onoff then @@ -220,6 +244,7 @@ do --- Switch debug state on -- @param #SHORAD self + -- @return #SHORAD self function SHORAD:SwitchDebugOn() self.debug = true --tracing @@ -230,6 +255,7 @@ do --- Switch debug state off -- @param #SHORAD self + -- @return #SHORAD self function SHORAD:SwitchDebugOff() self.debug = false BASE:TraceOff() @@ -239,6 +265,7 @@ do --- Switch defense for HARMs -- @param #SHORAD self -- @param #boolean onoff + -- @return #SHORAD self function SHORAD:SwitchHARMDefense(onoff) self:T( { onoff } ) local onoff = onoff or true @@ -249,6 +276,7 @@ do --- Switch defense for AGMs -- @param #SHORAD self -- @param #boolean onoff + -- @return #SHORAD self function SHORAD:SwitchAGMDefense(onoff) self:T( { onoff } ) local onoff = onoff or true @@ -260,6 +288,7 @@ do -- @param #SHORAD self -- @param #number low Minimum detection limit, integer 1-100 -- @param #number high Maximum detection limit integer 1-100 + -- @return #SHORAD self function SHORAD:SetDefenseLimits(low,high) self:T( { low, high } ) local low = low or 70 @@ -278,6 +307,7 @@ do --- Set the number of seconds a SHORAD site will stay active -- @param #SHORAD self -- @param #number seconds Number of seconds systems stay active + -- @return #SHORAD self function SHORAD:SetActiveTimer(seconds) self:T(self.lid .. " SetActiveTimer") local timer = seconds or 600 @@ -291,6 +321,7 @@ do --- Set the number of meters for the SHORAD defense zone -- @param #SHORAD self -- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active + -- @return #SHORAD self function SHORAD:SetDefenseRadius(meters) self:T(self.lid .. " SetDefenseRadius") local radius = meters or 20000 @@ -304,6 +335,7 @@ do --- Set using Emission on/off instead of changing alarm state -- @param #SHORAD self -- @param #boolean switch Decide if we are changing alarm state or AI state + -- @return #SHORAD self function SHORAD:SetUsingEmOnOff(switch) self:T(self.lid .. " SetUsingEmOnOff") self.UseEmOnOff = switch or false @@ -375,11 +407,11 @@ do local shorad = self.Groupset local shoradset = shorad:GetAliveSet() --#table local returnname = false + --local TDiff = 1 for _,_groups in pairs (shoradset) do local groupname = _groups:GetName() if string.find(groupname, tgtgrp, 1, true) then returnname = true - --_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive end end return returnname @@ -426,6 +458,7 @@ do -- @param #number Radius Radius of the #ZONE -- @param #number ActiveTimer Number of seconds to stay active -- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC + -- @return #SHORAD self -- @usage Use this function to integrate with other systems, example -- -- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart() @@ -452,28 +485,35 @@ do local targetzone = ZONE_RADIUS:New("Shorad",targetvec2,Radius) -- create a defense zone to check local groupset = self.Groupset --Core.Set#SET_GROUP local shoradset = groupset:GetAliveSet() --#table + -- local function to switch off shorad again local function SleepShorad(group) - local groupname = group:GetName() - self.ActiveGroups[groupname] = nil - if self.UseEmOnOff then - group:EnableEmission(false) - --group:SetAIOff() - else - group:OptionAlarmStateGreen() + if group and group:IsAlive() then + local groupname = group:GetName() + self.ActiveGroups[groupname] = nil + if self.UseEmOnOff then + group:EnableEmission(false) + else + group:OptionAlarmStateGreen() + end + local text = string.format("Sleeping SHORAD %s", group:GetName()) + self:T(text) + local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug) + --Shoot and Scoot + if self.shootandscoot then + self:__ShootAndScoot(1,group) + end end - local text = string.format("Sleeping SHORAD %s", group:GetName()) - self:T(text) - local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug) end + -- go through set and find the one(s) to activate + local TDiff = 4 for _,_group in pairs (shoradset) do if _group:IsAnyInZone(targetzone) then local text = string.format("Waking up SHORAD %s", _group:GetName()) self:T(text) local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug) if self.UseEmOnOff then - --_group:SetAIOn() _group:EnableEmission(true) end _group:OptionAlarmStateRed() @@ -481,91 +521,128 @@ do if self.ActiveGroups[groupname] == nil then -- no timer yet for this group self.ActiveGroups[groupname] = { Timing = ActiveTimer } local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit - timer.scheduleFunction(SleepShorad, _group, endtime) + self.ActiveGroups[groupname].Timer = TIMER:New(SleepShorad,_group):Start(endtime) + --Shoot and Scoot + if self.shootandscoot then + self:__ShootAndScoot(TDiff,_group) + TDiff=TDiff+1 + end end end end return self end ---- (Internal) Calculate hit zone of an AGM-88 --- @param #SHORAD self --- @param #table SEADWeapon DCS.Weapon object --- @param Core.Point#COORDINATE pos0 Position of the plane when it fired --- @param #number height Height when the missile was fired --- @param Wrapper.Group#GROUP SEADGroup Attacker group --- @return #SHORAD self -function SHORAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup) - self:T("**** Calculating hit zone") - if SEADWeapon and SEADWeapon:isExist() then - --local pos = SEADWeapon:getPoint() + --- (Internal) Calculate hit zone of an AGM-88 + -- @param #SHORAD self + -- @param #table SEADWeapon DCS.Weapon object + -- @param Core.Point#COORDINATE pos0 Position of the plane when it fired + -- @param #number height Height when the missile was fired + -- @param Wrapper.Group#GROUP SEADGroup Attacker group + -- @return #SHORAD self + function SHORAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup) + self:T("**** Calculating hit zone") + if SEADWeapon and SEADWeapon:isExist() then + --local pos = SEADWeapon:getPoint() + + -- postion and height + local position = SEADWeapon:getPosition() + local mheight = height + -- heading + local wph = math.atan2(position.x.z, position.x.x) + if wph < 0 then + wph=wph+2*math.pi + end + wph=math.deg(wph) + + -- velocity + local wpndata = SEAD.HarmData["AGM_88"] + local mveloc = math.floor(wpndata[2] * 340.29) + local c1 = (2*mheight*9.81)/(mveloc^2) + local c2 = (mveloc^2) / 9.81 + local Ropt = c2 * math.sqrt(c1+1) + if height <= 5000 then + Ropt = Ropt * 0.72 + elseif height <= 7500 then + Ropt = Ropt * 0.82 + elseif height <= 10000 then + Ropt = Ropt * 0.87 + elseif height <= 12500 then + Ropt = Ropt * 0.98 + end + + -- look at a couple of zones across the trajectory + for n=1,3 do + local dist = Ropt - ((n-1)*20000) + local predpos= pos0:Translate(dist,wph) + if predpos then - -- postion and height - local position = SEADWeapon:getPosition() - local mheight = height - -- heading - local wph = math.atan2(position.x.z, position.x.x) - if wph < 0 then - wph=wph+2*math.pi - end - wph=math.deg(wph) - - -- velocity - local wpndata = SEAD.HarmData["AGM_88"] - local mveloc = math.floor(wpndata[2] * 340.29) - local c1 = (2*mheight*9.81)/(mveloc^2) - local c2 = (mveloc^2) / 9.81 - local Ropt = c2 * math.sqrt(c1+1) - if height <= 5000 then - Ropt = Ropt * 0.72 - elseif height <= 7500 then - Ropt = Ropt * 0.82 - elseif height <= 10000 then - Ropt = Ropt * 0.87 - elseif height <= 12500 then - Ropt = Ropt * 0.98 + local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000) + + if self.debug then + predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false) + targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true) + end + + local seadset = self.Groupset + local tgtcoord = targetzone:GetRandomPointVec2() + local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord) + local _targetgroup = nil + local _targetgroupname = "none" + local _targetskill = "Random" + if tgtgrp and tgtgrp:IsAlive() then + _targetgroup = tgtgrp + _targetgroupname = tgtgrp:GetName() -- group name + _targetskill = tgtgrp:GetUnit(1):GetSkill() + self:T("*** Found Target = ".. _targetgroupname) + self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT) + end + end + end end - - -- look at a couple of zones across the trajectory - for n=1,3 do - local dist = Ropt - ((n-1)*20000) - local predpos= pos0:Translate(dist,wph) - if predpos then + return self + end - local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000) - - if self.debug then - predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false) - targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true) - end - - local seadset = self.Groupset - local tgtcoord = targetzone:GetRandomPointVec2() - local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord) - local _targetgroup = nil - local _targetgroupname = "none" - local _targetskill = "Random" - if tgtgrp and tgtgrp:IsAlive() then - _targetgroup = tgtgrp - _targetgroupname = tgtgrp:GetName() -- group name - _targetskill = tgtgrp:GetUnit(1):GetSkill() - self:T("*** Found Target = ".. _targetgroupname) - self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT) + --- (Internal) Shoot and Scoot + -- @param #SHORAD self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Wrapper.Group#GROUP Shorad Shorad group + -- @return #SHORAD self + function SHORAD:onafterShootAndScoot(From,Event,To,Shorad) + self:T( { From,Event,To } ) + local possibleZones = {} + local mindist = 100 + local maxdist = 3000 + if Shorad and Shorad:IsAlive() then + local NowCoord = Shorad:GetCoordinate() + for _,_zone in pairs(self.SkateZones.Set) do + local zone = _zone -- Core.Zone#ZONE_RADIUS + local dist = NowCoord:Get2DDistance(zone:GetCoordinate()) + if dist >= mindist and dist <= maxdist then + possibleZones[#possibleZones+1] = zone + if #possibleZones == self.SkateNumber then break end end end - end + if #possibleZones > 0 and Shorad:GetVelocityKMH() < 2 then + local rand = math.floor(math.random(1,#possibleZones*1000)/1000+0.5) + if rand == 0 then rand = 1 end + self:T(self.lid .. " ShootAndScoot to zone "..rand) + local ToCoordinate = possibleZones[rand]:GetCoordinate() + Shorad:RouteGroundTo(ToCoordinate,20,"Cone",1) + end + end + return self end - return self -end --- Main function - work on the EventData -- @param #SHORAD self -- @param Core.Event#EVENTDATA EventData The event details table data set + -- @return #SHORAD self function SHORAD:HandleEventShot( EventData ) self:T( { EventData } ) self:T(self.lid .. " HandleEventShot") - --local ShootingUnit = EventData.IniDCSUnit - --local ShootingUnitName = EventData.IniDCSUnitName local ShootingWeapon = EventData.Weapon -- Identify the weapon fired local ShootingWeaponName = EventData.WeaponName -- return weapon type -- get firing coalition @@ -603,20 +680,11 @@ end if targetcat == Object.Category.UNIT then -- UNIT targetunit = UNIT:Find(targetdata) elseif targetcat == Object.Category.STATIC then -- STATIC - --self:T("Static Target Data") - --self:T({targetdata:isExist()}) - --self:T({targetdata:getPoint()}) local tgtcoord = COORDINATE:NewFromVec3(targetdata:getPoint()) - --tgtcoord:MarkToAll("Missile Target",true) - - local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord) + local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord) local tgtcoord1 = tgtgrp1:GetCoordinate() - --tgtcoord1:MarkToAll("Close target SAM",true) - local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord) local tgtcoord2 = tgtgrp2:GetCoordinate() - --tgtcoord2:MarkToAll("Close target SHORAD",true) - local dist1 = tgtcoord:Get2DDistance(tgtcoord1) local dist2 = tgtcoord:Get2DDistance(tgtcoord2) @@ -628,10 +696,8 @@ end targetcat = Object.Category.UNIT end end - --local targetunitname = Unit.getName(targetdata) -- Unit name if targetunit and targetunit:IsAlive() then local targetunitname = targetunit:GetName() - --local targetgroup = Unit.getGroup(Weapon.getTarget(ShootingWeapon)) --targeted group local targetgroup = nil local targetgroupname = "none" if targetcat == Object.Category.UNIT then @@ -649,7 +715,6 @@ end self:T( text ) local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug) -- check if we or a SAM site are the target - --local TargetGroup = EventData.TgtGroup -- Wrapper.Group#GROUP local shotatus = self:_CheckShotAtShorad(targetgroupname) --#boolean local shotatsams = self:_CheckShotAtSams(targetgroupname) --#boolean -- if being shot at, find closest SHORADs to activate From 57ae54d6652a475a74db71306625a4df92135047 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Nov 2023 10:17:26 +0100 Subject: [PATCH 408/603] #bugfix, docu --- Moose Development/Moose/Core/Database.lua | 2 +- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 75f7be002..da647e4f5 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -756,7 +756,7 @@ end -- cargo --- Finds a CLIENT based on the ClientName. -- @param #DATABASE self --- @param #string ClientName +-- @param #string ClientName - Note this is the UNIT name of the client! -- @return Wrapper.Client#CLIENT The found CLIENT. function DATABASE:FindClient( ClientName ) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index a4d019531..0ad893b7e 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -2239,7 +2239,7 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) end if EventData.IniPlayerName then --self.PlayerMenu[EventData.IniPlayerName] = nil - local player = _DATABASE:FindClient( EventData.IniPlayerName ) + local player = _DATABASE:FindClient( EventData.IniUnitName ) self:_SwitchMenuForClient(player,"Info") end end From 078ffc9bafc2ebf70ceb5fb550849a44d578099d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Nov 2023 18:15:23 +0100 Subject: [PATCH 409/603] #MESSAGE * Fixes for ToSRS via MS Desktop --- Moose Development/Moose/Core/Message.lua | 53 +++++++++++++++++------- Moose Development/Moose/Sound/SRS.lua | 12 +++--- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index aeae8b95e..4f4a40e50 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -465,7 +465,7 @@ _MESSAGESRS = {} -- @param #string PathToCredentials (optional) Path to credentials file for e.g. Google. -- @param #number Frequency Frequency in MHz. Can also be given as a #table of frequencies. -- @param #number Modulation Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. --- @param #string Gender (optional) Gender, i.e. "male" or "female", defaults to "male". +-- @param #string Gender (optional) Gender, i.e. "male" or "female", defaults to "female". -- @param #string Culture (optional) Culture, e.g. "en-US", defaults to "en-GB" -- @param #string Voice (optional) Voice. Will override gender and culture settings, e.g. MSRS.Voices.Microsoft.Hazel or MSRS.Voices.Google.Standard.de_DE_Standard_D. Hint on Microsoft voices - working voices are limited to Hedda, Hazel, David, Zira and Hortense. **Must** be installed on your Desktop or Server! -- @param #number Coalition (optional) Coalition, can be coalition.side.RED, coalition.side.BLUE or coalition.side.NEUTRAL. Defaults to coalition.side.NEUTRAL. @@ -480,24 +480,42 @@ _MESSAGESRS = {} -- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS() -- function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate) - _MESSAGESRS.MSRS = MSRS:New(PathToSRS,Frequency,Modulation,Volume) - _MESSAGESRS.MSRS:SetCoalition(Coalition) + _MESSAGESRS.MSRS = MSRS:New(PathToSRS,Frequency or 243,Modulation or radio.modulation.AM,Volume) + + _MESSAGESRS.frequency = Frequency + _MESSAGESRS.modulation = Modulation or radio.modulation.AM + + _MESSAGESRS.MSRS:SetCoalition(Coalition or coalition.side.NEUTRAL) + _MESSAGESRS.coalition = Coalition or coalition.side.NEUTRAL + + _MESSAGESRS.coordinate = Coordinate _MESSAGESRS.MSRS:SetCoordinate(Coordinate) + _MESSAGESRS.MSRS:SetCulture(Culture) - _MESSAGESRS.Culture = Culture - --_MESSAGESRS.MSRS:SetFrequencies(Frequency) + _MESSAGESRS.Culture = Culture or "en-GB" + _MESSAGESRS.MSRS:SetGender(Gender) - _MESSAGESRS.Gender = Gender + _MESSAGESRS.Gender = Gender or "female" + _MESSAGESRS.MSRS:SetGoogle(PathToCredentials) + _MESSAGESRS.google = PathToCredentials + _MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE") - --_MESSAGESRS.MSRS:SetModulations(Modulation) - --_MESSAGESRS.MSRS:SetPath(PathToSRS) - _MESSAGESRS.MSRS:SetPort(Port) - -- _MESSAGESRS.MSRS:SetVolume(Volume) - _MESSAGESRS.MSRS:SetVoice(Voice) - _MESSAGESRS.Voice = Voice + _MESSAGESRS.label = Label or "MESSAGE" + + _MESSAGESRS.MSRS:SetPort(Port or 5002) + _MESSAGESRS.port = Port or 5002 + + _MESSAGESRS.volume = Volume or 1 + _MESSAGESRS.MSRS:SetVolume(_MESSAGESRS.volume) + + if Voice then _MESSAGESRS.MSRS:SetVoice(Voice) end + + _MESSAGESRS.voice = Voice --or MSRS.Voices.Microsoft.Hedda + --if _MESSAGESRS.google and not Voice then _MESSAGESRS.Voice = MSRS.Voices.Google.Standard.en_GB_Standard_A end + --_MESSAGESRS.MSRS:SetVoice(Voice or _MESSAGESRS.voice) + _MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE") - env.info(_MESSAGESRS.MSRS.provider,false) end --- Sends a message via SRS. @@ -505,7 +523,7 @@ end -- @param #number frequency (optional) Frequency in MHz. Can also be given as a #table of frequencies. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. -- @param #number modulation (optional) Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. -- @param #string gender (optional) Gender, i.e. "male" or "female". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. --- @param #string culture (optional) Culture, e.g. "en-US. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. +-- @param #string culture (optional) Culture, e.g. "en-US". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. -- @param #string voice (optional) Voice. Will override gender and culture settings. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. -- @param #number coalition (optional) Coalition, can be coalition.side.RED, coalition.side.BLUE or coalition.side.NEUTRAL. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. -- @param #number volume (optional) Volume, can be between 0.0 and 1.0 (loudest). Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`. @@ -519,13 +537,16 @@ end -- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS() -- function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volume,coordinate) + local tgender = gender or _MESSAGESRS.Gender if _MESSAGESRS.SRSQ then - _MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.Voice) + if voice then + _MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.voice) + end if coordinate then _MESSAGESRS.MSRS:SetCoordinate(coordinate) end local category = string.gsub(self.MessageCategory,":","") - _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency,modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,voice or _MESSAGESRS.Voice,volume,category,coordinate) + _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency or _MESSAGESRS.frequency,modulation or _MESSAGESRS.modulation, gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,nil,volume or _MESSAGESRS.volume,category,coordinate or _MESSAGESRS.coordinate) end return self end diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 57e3fddcc..856595d0b 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1119,7 +1119,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp end -- Debug output. - self:T("MSRS command="..command) + self:I("MSRS command="..command) return command end @@ -1243,7 +1243,7 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) self.gender = MSRS_Config.Gender or "male" self.google = MSRS_Config.Google self.Label = MSRS_Config.Label or "MSRS" - self.voice = MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel + self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel if MSRS_Config.GRPC then self.provider = MSRS_Config.GRPC.DefaultProvider if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then @@ -1267,7 +1267,7 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) MSRS.gender = MSRS_Config.Gender or "male" MSRS.google = MSRS_Config.Google MSRS.Label = MSRS_Config.Label or "MSRS" - MSRS.voice = MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel + MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel if MSRS_Config.GRPC then MSRS.provider = MSRS_Config.GRPC.DefaultProvider if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then @@ -1695,7 +1695,7 @@ end -- @param #MSRSQUEUE self -- @return #MSRSQUEUE self The MSRSQUEUE object. function MSRSQUEUE:Clear() - self:I(self.lid.."Clearning MSRSQUEUE") + self:I(self.lid.."Clearing MSRSQUEUE") self.queue={} return self end @@ -1796,7 +1796,7 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr transmission.gender = gender transmission.culture = culture transmission.voice = voice - transmission.gender = volume + transmission.volume = volume transmission.label = label transmission.coordinate = coordinate @@ -1810,7 +1810,7 @@ end -- @param #MSRSQUEUE self -- @param #MSRSQUEUE.Transmission transmission The transmission. function MSRSQUEUE:Broadcast(transmission) - + if transmission.frequency then transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label, transmission.coordinate) else From 9f08d53262556300281ed90e66540baaf6235f71 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 Nov 2023 14:07:26 +0100 Subject: [PATCH 410/603] #PLAYERRECCE * Fixes for new Gazelle models --- Moose Development/Moose/Ops/PlayerRecce.lua | 40 ++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index f06edd09e..9ee2c1d74 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -104,7 +104,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.20", + version = "0.0.21", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, @@ -570,26 +570,31 @@ function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle) local unit = Gazelle -- Wrapper.Unit#UNIT if unit and unit:IsAlive() then local dcsunit = Unit.getByName(Gazelle:GetName()) - local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg + local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg, local vivivertical = dcsunit:getDrawArgumentValue(216) or 0 -- L/Mistral/Minigun model has no 216, ca 10deg up (=1) and down (=-1) + -- vertical model limits 1.53846, -1.10731 local vivioff = false -- -1 = -180, 1 = 180 - -- Actual view -0,66 to 0,66 - -- Nick view -0,98 to 0,98 for +/- 30° - if vivihorizontal < -0.7 then - vivihorizontal = -0.7 - vivioff = true - return 0,0,0,false - elseif vivihorizontal > 0.7 then - vivihorizontal = 0.7 + -- Actual model view -0,66 to 0,66 + -- Nick view 1.53846, -1.10731 for - 30° to +45° + if vivihorizontal < -0.67 then -- model end + vivihorizontal = -0.67 + vivioff = false + --return 0,0,0,false + elseif vivihorizontal > 0.67 then -- vivi off + vivihorizontal = 0.67 vivioff = true return 0,0,0,false end + vivivertical = vivivertical / 1.10731 -- normalize local horizontalview = vivihorizontal * -180 - local verticalview = vivivertical * -30 -- ca +/- 30° + local verticalview = vivivertical * 30 -- ca +/- 30° + --self:I(string.format("vivihorizontal=%.5f | vivivertical=%.5f",vivihorizontal,vivivertical)) + --self:I(string.format("horizontal=%.5f | vertical=%.5f",horizontalview,verticalview)) local heading = unit:GetHeading() local viviheading = (heading+horizontalview)%360 local maxview = self:_GetActualMaxLOSight(unit,viviheading, verticalview,vivioff) + --self:I(string.format("maxview=%.5f",maxview)) -- visual skew local factor = 3.15 self.GazelleViewFactors = { @@ -609,16 +614,18 @@ function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle) } local lfac = UTILS.Round(maxview,-2) if lfac <= 1300 then - factor = self.GazelleViewFactors[lfac/100] + --factor = self.GazelleViewFactors[lfac/100] + factor = 3.15 maxview = math.ceil((maxview*factor)/100)*100 end if maxview > 8000 then maxview = 8000 end + --self:I(string.format("corrected maxview=%.5f",maxview)) return viviheading, verticalview,maxview, not vivioff end return 0,0,0,false end ---- [Internal] Get the max line of sight based on unit head and camera nod via trigonometrie. Returns 0 if camera is off. +--- [Internal] Get the max line of sight based on unit head and camera nod via trigonometry. Returns 0 if camera is off. -- @param #PLAYERRECCE self -- @param Wrapper.Unit#UNIT unit The unit which LOS we want -- @param #number vheading Heading where the unit or camera is looking @@ -628,12 +635,13 @@ end function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff) self:T(self.lid.."_GetActualMaxLOSight") if vivoff then return 0 end + --if vnod < -0.03 then vnod = -0.03 end local maxview = 0 if unit and unit:IsAlive() then local typename = unit:GetTypeName() - maxview = self.MaxViewDistance[typename] or 8000 + maxview = self.MaxViewDistance[typename] or 8000 local CamHeight = self.Cameraheight[typename] or 0 - if vnod > 0 then + if vnod < 0 then -- Looking down -- determine max distance we're looking at local beta = 90 @@ -645,7 +653,7 @@ function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff) maxview = c*1.2 -- +20% end end - return maxview + return math.abs(maxview) end --- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns. From 8b20f6cab28fad085b2e2e55f80ad77edf832c59 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 Nov 2023 18:12:17 +0100 Subject: [PATCH 411/603] xx --- Moose Development/Moose/Functional/Mantis.lua | 9 ++++--- Moose Development/Moose/Functional/Range.lua | 27 +++++++++++++------ Moose Development/Moose/Sound/SRS.lua | 2 +- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 1ae8604ca..ff631d18a 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -22,7 +22,7 @@ -- @module Functional.Mantis -- @image Functional.Mantis.jpg -- --- Last Update: Sept 2023 +-- Last Update: Oct 2023 ------------------------------------------------------------------------- --- **MANTIS** class, extends Core.Base#BASE @@ -103,6 +103,7 @@ -- * Roland -- * Silkworm (though strictly speaking this is a surface to ship missile) -- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19 +-- * From IDF mod: STUNNER IDFA, TAMIR IDFA (Note all caps!) -- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2 -- -- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M @@ -373,7 +374,9 @@ MANTIS.SamData = { ["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"}, ["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"}, ["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" }, - ["SHORAD"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="Igla" } + ["SHORAD"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="Igla" }, + ["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" }, + ["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" }, } --- SAM data HDS @@ -628,7 +631,7 @@ do -- TODO Version -- @field #string version - self.version="0.8.14" + self.version="0.8.15" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) --- FSM Functions --- diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 18630e30b..e9005ace0 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -105,6 +105,7 @@ -- @field Sound.SRS#MSRSQUEUE controlsrsQ SRS queue for range controller. -- @field Sound.SRS#MSRS instructmsrs SRS wrapper for range instructor. -- @field Sound.SRS#MSRSQUEUE instructsrsQ SRS queue for range instructor. +-- @field #number Coalition Coalition side for the menu, if any. -- @extends Core.Fsm#FSM --- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven @@ -355,7 +356,8 @@ RANGE = { targetsheet = nil, targetpath = nil, targetprefix = nil, -} + Coalition = nil, + } --- Default range parameters. -- @type RANGE.Defaults @@ -591,7 +593,7 @@ RANGE.MenuF10Root = nil --- Range script version. -- @field #string version -RANGE.version = "2.7.1" +RANGE.version = "2.7.3" -- TODO list: -- TODO: Verbosity level for messages. @@ -613,8 +615,9 @@ RANGE.version = "2.7.1" --- RANGE contructor. Creates a new RANGE object. -- @param #RANGE self -- @param #string RangeName Name of the range. Has to be unique. Will we used to create F10 menu items etc. +-- @param #number Coalition (optional) Coalition of the range, if any, e.g. coalition.side.BLUE. -- @return #RANGE RANGE object. -function RANGE:New( RangeName ) +function RANGE:New( RangeName, Coalition ) -- Inherit BASE. local self = BASE:Inherit( self, FSM:New() ) -- #RANGE @@ -622,7 +625,9 @@ function RANGE:New( RangeName ) -- Get range name. -- TODO: make sure that the range name is not given twice. This would lead to problems in the F10 radio menu. self.rangename = RangeName or "Practice Range" - + + self.Coalition = Coalition + -- Log id. self.lid = string.format( "RANGE %s | ", self.rangename ) @@ -1745,10 +1750,16 @@ function RANGE:OnEventBirth( EventData ) -- Reset current strafe status. self.strafeStatus[_uid] = nil - - -- Add Menu commands after a delay of 0.1 seconds. - self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName ) - + + if self.Coalition then + if EventData.IniCoalition == self.Coalition then + self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName ) + end + else + -- Add Menu commands after a delay of 0.1 seconds. + self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName ) + end + -- By default, some bomb impact points and do not flare each hit on target. self.PlayerSettings[_playername] = {} -- #RANGE.PlayerData self.PlayerSettings[_playername].smokebombimpact = self.defaultsmokebomb diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 856595d0b..4ffa93ca4 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1119,7 +1119,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp end -- Debug output. - self:I("MSRS command="..command) + self:T("MSRS command="..command) return command end From 9df5e5165d7d3ff1842a154d030a127aadc00b95 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Nov 2023 12:05:01 +0100 Subject: [PATCH 412/603] #AWACS * Small fix for no text on clean call --- Moose Development/Moose/Ops/Awacs.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index eb1a4dd33..22428f8da 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -17,7 +17,7 @@ -- === -- -- ### Author: **applevangelist** --- @date Last Update July 2023 +-- @date Last Update Nov 2023 -- @module Ops.AWACS -- @image OPS_AWACS.jpg @@ -507,7 +507,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.58", -- #string + version = "0.2.59", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -2988,7 +2988,7 @@ function AWACS:_Picture(Group,IsGeneral) if clustersAO == 0 and clustersEWR == 0 then -- clean - self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false) + self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) else if clustersAO > 0 then From 51be801637bf54ad1a371902b92330e77f19a0ba Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Nov 2023 15:03:29 +0100 Subject: [PATCH 413/603] #PLAYERRECCE --- Moose Development/Moose/Core/Set.lua | 113 ++++++++++++-------- Moose Development/Moose/Ops/PlayerRecce.lua | 23 +++- 2 files changed, 88 insertions(+), 48 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index e6e308c8d..ee0917406 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2425,6 +2425,26 @@ do -- SET_UNIT return CountU end + --- Gets the alive set. + -- @param #SET_UNIT self + -- @return #table Table of SET objects + -- @return #SET_UNIT AliveSet + function SET_UNIT:GetAliveSet() + + local AliveSet = SET_UNIT:New() + + -- Clean the Set before returning with only the alive Groups. + for GroupName, GroupObject in pairs(self.Set) do + local GroupObject=GroupObject --Wrapper.Client#CLIENT + + if GroupObject and GroupObject:IsAlive() then + AliveSet:Add(GroupName, GroupObject) + end + end + + return AliveSet.Set or {}, AliveSet + end + --- [Internal] Private function for use of continous zone filter -- @param #SET_UNIT self -- @return #SET_UNIT self @@ -2819,51 +2839,58 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units. function SET_UNIT:GetCoordinate() - - local Coordinate = self:GetRandom():GetCoordinate() - --self:F({Coordinate:GetVec3()}) - - local x1 = Coordinate.x - local x2 = Coordinate.x - local y1 = Coordinate.y - local y2 = Coordinate.y - local z1 = Coordinate.z - local z2 = Coordinate.z - local MaxVelocity = 0 - local AvgHeading = nil - local MovingCount = 0 - - for UnitName, UnitData in pairs( self:GetSet() ) do - - local Unit = UnitData -- Wrapper.Unit#UNIT - local Coordinate = Unit:GetCoordinate() - - x1 = (Coordinate.x < x1) and Coordinate.x or x1 - x2 = (Coordinate.x > x2) and Coordinate.x or x2 - y1 = (Coordinate.y < y1) and Coordinate.y or y1 - y2 = (Coordinate.y > y2) and Coordinate.y or y2 - z1 = (Coordinate.y < z1) and Coordinate.z or z1 - z2 = (Coordinate.y > z2) and Coordinate.z or z2 - - local Velocity = Coordinate:GetVelocity() - if Velocity ~= 0 then - MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity - local Heading = Coordinate:GetHeading() - AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading - MovingCount = MovingCount + 1 - end + local Coordinate = nil + local unit = self:GetRandom() + if self:Count() == 1 and unit then + return unit:GetCoordinate() + end + if unit then + local Coordinate = unit:GetCoordinate() + --self:F({Coordinate:GetVec3()}) + + + local x1 = Coordinate.x + local x2 = Coordinate.x + local y1 = Coordinate.y + local y2 = Coordinate.y + local z1 = Coordinate.z + local z2 = Coordinate.z + local MaxVelocity = 0 + local AvgHeading = nil + local MovingCount = 0 + + for UnitName, UnitData in pairs( self:GetAliveSet() ) do + + local Unit = UnitData -- Wrapper.Unit#UNIT + local Coordinate = Unit:GetCoordinate() + + x1 = (Coordinate.x < x1) and Coordinate.x or x1 + x2 = (Coordinate.x > x2) and Coordinate.x or x2 + y1 = (Coordinate.y < y1) and Coordinate.y or y1 + y2 = (Coordinate.y > y2) and Coordinate.y or y2 + z1 = (Coordinate.y < z1) and Coordinate.z or z1 + z2 = (Coordinate.y > z2) and Coordinate.z or z2 + + local Velocity = Coordinate:GetVelocity() + if Velocity ~= 0 then + MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity + local Heading = Coordinate:GetHeading() + AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading + MovingCount = MovingCount + 1 + end + end + + AvgHeading = AvgHeading and (AvgHeading / MovingCount) + + Coordinate.x = (x2 - x1) / 2 + x1 + Coordinate.y = (y2 - y1) / 2 + y1 + Coordinate.z = (z2 - z1) / 2 + z1 + Coordinate:SetHeading( AvgHeading ) + Coordinate:SetVelocity( MaxVelocity ) + + self:F( { Coordinate = Coordinate } ) end - - AvgHeading = AvgHeading and (AvgHeading / MovingCount) - - Coordinate.x = (x2 - x1) / 2 + x1 - Coordinate.y = (y2 - y1) / 2 + y1 - Coordinate.z = (z2 - z1) / 2 + z1 - Coordinate:SetHeading( AvgHeading ) - Coordinate:SetVelocity( MaxVelocity ) - - self:F( { Coordinate = Coordinate } ) return Coordinate end diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 9ee2c1d74..c8cad0bdd 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -916,7 +916,8 @@ function PLAYERRECCE:_LaseTarget(client,targetset) -- still looking at target? local target=self.LaserTarget[playername] -- Ops.Target#TARGET local oldtarget = target:GetObject() --or laser.Target - self:T("Targetstate: "..target:GetState()) + --self:I("Targetstate: "..target:GetState()) + --self:I("Laser State: "..tostring(laser:IsLasing())) if not oldtarget or targetset:IsNotInSet(oldtarget) or target:IsDead() or target:IsDestroyed() then -- lost LOS or dead laser:LaseOff() @@ -928,12 +929,20 @@ function PLAYERRECCE:_LaseTarget(client,targetset) self.LaserTarget[playername] = nil end end + if oldtarget and (not laser:IsLasing()) then + --self:I("Switching laser back on ..") + local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688 + local lasingtime = self.lasingtime or 60 + --local targettype = target:GetTypeName() + laser:LaseOn(oldtarget,lasercode,lasingtime) + --self:__TargetLasing(-1,client,oldtarget,lasercode,lasingtime) + end elseif not laser:IsLasing() and target then local relativecam = self.LaserRelativePos[client:GetTypeName()] laser:SetRelativeStartPosition(relativecam) local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688 local lasingtime = self.lasingtime or 60 - local targettype = target:GetTypeName() + --local targettype = target:GetTypeName() laser:LaseOn(target,lasercode,lasingtime) self.LaserTarget[playername] = TARGET:New(target) self.LaserTarget[playername].TStatus = 9 @@ -1101,9 +1110,13 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) end end + local coordinate = nil + local setthreat = 0 -- smoke everything else - local coordinate = cameraset:GetCoordinate() - local setthreat = cameraset:CalculateThreatLevelA2G() + if cameraset:CountAlive() > 1 then + local coordinate = cameraset:GetCoordinate() + local setthreat = cameraset:CalculateThreatLevelA2G() + end if coordinate then local color = lowsmoke @@ -1983,7 +1996,7 @@ function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Laserc coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings) end local coordtext = coord:ToStringA2G(client,Settings) - local text = string.format("All stations, %s lasing %s\nat %s!\nCode %d, Duration %d seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime) + local text = string.format("All stations, %s lasing %s\nat %s!\nCode %d, Duration %d plus seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime) MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client) end end From b737ebb96248d072b636eadef6f72ae0fde5753c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 18 Nov 2023 16:43:44 +0100 Subject: [PATCH 414/603] xx --- Moose Development/Moose/Core/Event.lua | 13 +++--- Moose Development/Moose/Ops/CTLD.lua | 62 +++++++++++++++++--------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index b32b723c7..95ca664c5 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -1245,11 +1245,14 @@ function EVENT:onEvent( Event ) Event.TgtDCSUnit = Event.target if Event.target:isExist() and Event.id ~= 33 then -- leave out ejected seat object Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() - Event.TgtUnitName = Event.TgtDCSUnitName - Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false ) - Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() - Event.TgtCategory = Event.TgtDCSUnit:getDesc().category - Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() + -- Workaround for borked target info on cruise missiles + if Event.TgtDCSUnitName and Event.TgtDCSUnitName ~= "" then + Event.TgtUnitName = Event.TgtDCSUnitName + Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false ) + Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() + end else Event.TgtDCSUnitName = string.format("No target object for Event ID %s", tostring(Event.id)) Event.TgtUnitName = Event.TgtDCSUnitName diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 80c8d7d6e..a284e8cea 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -24,7 +24,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update October 2023 +-- Last Update November 2023 do @@ -741,7 +741,7 @@ do -- -- -- E.g. update unit capabilities for testing. Please stay realistic in your mission design. -- -- Make a Gazelle into a heavy truck, this type can load both crates and troops and eight of each type, up to 4000 kgs: --- my_ctld:UnitCapabilities("SA342L", true, true, 8, 8, 12, 4000) +-- my_ctld:SetUnitCapabilities("SA342L", true, true, 8, 8, 12, 4000) -- -- -- Default unit type capabilities are: -- ["SA342Mistral"] = {type="SA342Mistral", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12, cargoweightlimit = 400}, @@ -1200,14 +1200,14 @@ CTLD.CargoZoneType = { -- @field #CTLD_CARGO.Enum Type Type enumerator (for moves). --- Unit capabilities. --- @type CTLD.UnitCapabilities +-- @type CTLD.UnitTypeCapabilities -- @field #string type Unit type. -- @field #boolean crates Can transport crate. -- @field #boolean troops Can transport troops. -- @field #number cratelimit Number of crates transportable. -- @field #number trooplimit Number of troop units transportable. -- @field #number cargoweightlimit Max loadable kgs of cargo. -CTLD.UnitTypes = { +CTLD.UnitTypeCapabilities = { ["SA342Mistral"] = {type="SA342Mistral", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12, cargoweightlimit = 400}, ["SA342L"] = {type="SA342L", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12, cargoweightlimit = 400}, ["SA342M"] = {type="SA342M", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12, cargoweightlimit = 400}, @@ -1228,7 +1228,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.41" +CTLD.version="1.0.42" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1680,7 +1680,7 @@ function CTLD:_GetUnitCapabilities(Unit) self:T(self.lid .. " _GetUnitCapabilities") local _unit = Unit -- Wrapper.Unit#UNIT local unittype = _unit:GetTypeName() - local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities + local capabilities = self.UnitTypeCapabilities[unittype] -- #CTLD.UnitTypeCapabilities if not capabilities or capabilities == {} then -- e.g. ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0}, capabilities = {} @@ -1871,7 +1871,7 @@ function CTLD:_PreloadCrates(Group, Unit, Cargo, NumberOfCrates) local unitname = unit:GetName() -- see if this heli can load crates local unittype = unit:GetTypeName() - local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities local cancrates = capabilities.crates -- #boolean local cratelimit = capabilities.cratelimit -- #number if not cancrates then @@ -2308,7 +2308,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack) end -- avoid crate spam - local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities local canloadcratesno = capabilities.cratelimit local loaddist = self.CrateDistance or 35 local nearcrates, numbernearby = self:_FindCratesNearby(Group,Unit,loaddist,true) @@ -2601,8 +2601,8 @@ function CTLD:_LoadCratesNearby(Group, Unit) local unitname = unit:GetName() -- see if this heli can load crates local unittype = unit:GetTypeName() - local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities - --local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities + --local capabilities = self.UnitTypeCapabilities[unittype] -- #CTLD.UnitTypeCapabilities local cancrates = capabilities.crates -- #boolean local cratelimit = capabilities.cratelimit -- #number local grounded = not self:IsUnitInAir(Unit) @@ -2753,7 +2753,7 @@ function CTLD:_GetMaxLoadableMass(Unit) if not Unit then return 0 end local loadable = 0 local loadedmass = self:_GetUnitCargoMass(Unit) - local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities local maxmass = capabilities.cargoweightlimit or 2000 -- max 2 tons loadable = maxmass - loadedmass return loadable @@ -2778,7 +2778,7 @@ function CTLD:_ListCargo(Group, Unit) self:T(self.lid .. " _ListCargo") local unitname = Unit:GetName() local unittype = Unit:GetTypeName() - local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities local trooplimit = capabilities.trooplimit -- #boolean local cratelimit = capabilities.cratelimit -- #number local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo @@ -3536,13 +3536,19 @@ function CTLD:_RefreshF10Menus() if _group then -- get chopper capabilities local unittype = _unit:GetTypeName() - local capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitCapabilities + local capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitTypeCapabilities local cantroops = capabilities.troops local cancrates = capabilities.crates -- top menu local topmenu = MENU_GROUP:New(_group,"CTLD",nil) - local toptroops = MENU_GROUP:New(_group,"Manage Troops",topmenu) - local topcrates = MENU_GROUP:New(_group,"Manage Crates",topmenu) + local toptroops = nil + local topcrates = nil + if cantroops then + toptroops = MENU_GROUP:New(_group,"Manage Troops",topmenu) + end + if cancrates then + topcrates = MENU_GROUP:New(_group,"Manage Crates",topmenu) + end local listmenu = MENU_GROUP_COMMAND:New(_group,"List boarded cargo",topmenu, self._ListCargo, self, _group, _unit) local invtry = MENU_GROUP_COMMAND:New(_group,"Inventory",topmenu, self._ListInventory, self, _group, _unit) local rbcns = MENU_GROUP_COMMAND:New(_group,"List active zone beacons",topmenu, self._ListRadioBeacons, self, _group, _unit) @@ -4339,7 +4345,7 @@ end -- @param #number Trooplimit Unit can carry number of troops. Default 0. -- @param #number Length Unit lenght (in metres) for the load radius. Default 20. -- @param #number Maxcargoweight Maxmimum weight in kgs this helo can carry. Default 500. - function CTLD:UnitCapabilities(Unittype, Cancrates, Cantroops, Cratelimit, Trooplimit, Length, Maxcargoweight) + function CTLD:SetUnitCapabilities(Unittype, Cancrates, Cantroops, Cratelimit, Trooplimit, Length, Maxcargoweight) self:T(self.lid .. " UnitCapabilities") local unittype = nil local unit = nil @@ -4353,13 +4359,13 @@ end end local length = 20 local maxcargo = 500 - local existingcaps = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities + local existingcaps = self.UnitTypeCapabilities[unittype] -- #CTLD.UnitTypeCapabilities if existingcaps then length = existingcaps.length or 20 maxcargo = existingcaps.cargoweightlimit or 500 end -- set capabilities - local capabilities = {} -- #CTLD.UnitCapabilities + local capabilities = {} -- #CTLD.UnitTypeCapabilities capabilities.type = unittype capabilities.crates = Cancrates or false capabilities.troops = Cantroops or false @@ -4367,10 +4373,26 @@ end capabilities.trooplimit = Trooplimit or 0 capabilities.length = Length or length capabilities.cargoweightlimit = Maxcargoweight or maxcargo - self.UnitTypes[unittype] = capabilities + self.UnitTypeCapabilities[unittype] = capabilities return self end + --- [Deprecated] - Function to add/adjust unittype capabilities. Has been replaced with `SetUnitCapabilities()` - pls use the new one going forward! + -- @param #CTLD self + -- @param #string Unittype The unittype to adjust. If passed as Wrapper.Unit#UNIT, it will search for the unit in the mission. + -- @param #boolean Cancrates Unit can load crates. Default false. + -- @param #boolean Cantroops Unit can load troops. Default false. + -- @param #number Cratelimit Unit can carry number of crates. Default 0. + -- @param #number Trooplimit Unit can carry number of troops. Default 0. + -- @param #number Length Unit lenght (in metres) for the load radius. Default 20. + -- @param #number Maxcargoweight Maxmimum weight in kgs this helo can carry. Default 500. + function CTLD:UnitCapabilities(Unittype, Cancrates, Cantroops, Cratelimit, Trooplimit, Length, Maxcargoweight) + self:I(self.lid.."This function been replaced with `SetUnitCapabilities()` - pls use the new one going forward!") + self:SetUnitCapabilities(Unittype, Cancrates, Cantroops, Cratelimit, Trooplimit, Length, Maxcargoweight) + return self + end + + --- (Internal) Check if a unit is hovering *in parameters*. -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit @@ -4523,7 +4545,7 @@ end local unittype = Unit:GetTypeName() local unitname = Unit:GetName() local Group = Unit:GetGroup() - local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities local cancrates = capabilities.crates -- #boolean local cratelimit = capabilities.cratelimit -- #number if cancrates then From 540861dd2551473cb18d1506dbb93a8e9a21630f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 18 Nov 2023 17:15:53 +0100 Subject: [PATCH 415/603] Advanced scoot and shoot --- Moose Development/Moose/Functional/Mantis.lua | 10 ++-- Moose Development/Moose/Functional/Shorad.lua | 50 ++++++++++++------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 60f99d8a2..ce95806b1 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -799,12 +799,16 @@ do -- @param #MANTIS self -- @param Core.Set#SET_ZONE ZoneSet Set of zones to be used. Units will move around to the next (random) zone between 100m and 3000m away. -- @param #number Number Number of closest zones to be considered, defaults to 3. + -- @param #boolean Random If true, use a random coordinate inside the next zone to scoot to. + -- @param #string Formation Formation to use, defaults to "Cone". See mission editor dropdown for options. -- @return #MANTIS self - function MANTIS:AddScootZones(ZoneSet, Number) + function MANTIS:AddScootZones(ZoneSet, Number, Random, Formation) self:T(self.lid .. " AddScootZones") self.SkateZones = ZoneSet self.SkateNumber = Number or 3 - self.shootandscoot = true + self.shootandscoot = true + self.ScootRandom = Random + self.ScootFormation = Formation or "Cone" return self end @@ -1810,7 +1814,7 @@ do self.Shorad.debug = self.debug end if self.shootandscoot and self.SkateZones and self.Shorad then - self.Shorad:AddScootZones(self.SkateZones,self.SkateNumber or 3) + self.Shorad:AddScootZones(self.SkateZones,self.SkateNumber or 3,self.ScootRandom,self.ScootFormation) end self:__Status(-math.random(1,10)) return self diff --git a/Moose Development/Moose/Functional/Shorad.lua b/Moose Development/Moose/Functional/Shorad.lua index 3ba433296..a03e61731 100644 --- a/Moose Development/Moose/Functional/Shorad.lua +++ b/Moose Development/Moose/Functional/Shorad.lua @@ -41,10 +41,14 @@ -- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles -- @field #number DefenseLowProb Default 70, minimum detection limit -- @field #number DefenseHighProb Default 90, maximum detection limit --- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green. --- @field #boolean shootandscoot --- @field #number SkateNumber --- @field Core.Set#SET_ZONE SkateZones +-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green +-- @field #boolean shootandscoot If true, shoot and scoot between zones +-- @field #number SkateNumber Number of zones to consider +-- @field Core.Set#SET_ZONE SkateZones Zones in this set are considered +-- @field #number minscootdist Min distance of the next zone +-- @field #number maxscootdist Max distance of the next zone +-- @field #boolean scootrandomcoord If true, use a random coordinate in the zone and not the center +-- @field #string scootformation Formation to take for scooting, e.g. "Vee" or "Cone" -- @extends Core.Base#BASE @@ -77,14 +81,15 @@ -- -- `myshorad = SHORAD:New("RedShorad", "Red SHORAD", SamSet, 25000, 600, "red")` -- --- ## Customize options +-- ## Customization options -- --- * SHORAD:SwitchDebug(debug) --- * SHORAD:SwitchHARMDefense(onoff) --- * SHORAD:SwitchAGMDefense(onoff) --- * SHORAD:SetDefenseLimits(low,high) --- * SHORAD:SetActiveTimer(seconds) --- * SHORAD:SetDefenseRadius(meters) +-- * myshorad:SwitchDebug(debug) +-- * myshorad:SwitchHARMDefense(onoff) +-- * myshorad:SwitchAGMDefense(onoff) +-- * myshorad:SetDefenseLimits(low,high) +-- * myshorad:SetActiveTimer(seconds) +-- * myshorad:SetDefenseRadius(meters) +-- * myshorad:AddScootZones(ZoneSet,Number,Random,Formation) -- -- @field #SHORAD SHORAD = { @@ -107,6 +112,9 @@ SHORAD = { shootandscoot = false, SkateNumber = 3, SkateZones = nil, + minscootdist = 100, + minscootdist = 3000, + scootrandomcoord = false, } ----------------------------------------------------------------------- @@ -174,7 +182,7 @@ do self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin self.UseEmOnOff = true -- Decide if we are using Emission on/off (default) or AlarmState red/green if UseEmOnOff == false then self.UseEmOnOff = UseEmOnOff end - self:I("*** SHORAD - Started Version 0.3.2") + self:I("*** SHORAD - Started Version 0.3.4") -- Set the string id for output to DCS.log file. self.lid=string.format("SHORAD %s | ", self.name) self:_InitState() @@ -219,12 +227,16 @@ do -- @param #SHORAD self -- @param Core.Set#SET_ZONE ZoneSet Set of zones to be used. Units will move around to the next (random) zone between 100m and 3000m away. -- @param #number Number Number of closest zones to be considered, defaults to 3. + -- @param #boolean Random If true, use a random coordinate inside the next zone to scoot to. + -- @param #string Formation Formation to use, defaults to "Cone". See mission editor dropdown for options. -- @return #SHORAD self - function SHORAD:AddScootZones(ZoneSet, Number) + function SHORAD:AddScootZones(ZoneSet, Number, Random, Formation) self:T(self.lid .. " AddScootZones") self.SkateZones = ZoneSet self.SkateNumber = Number or 3 - self.shootandscoot = true + self.shootandscoot = true + self.scootrandomcoord = Random + self.scootformation = Formation or "Cone" return self end @@ -613,8 +625,8 @@ do function SHORAD:onafterShootAndScoot(From,Event,To,Shorad) self:T( { From,Event,To } ) local possibleZones = {} - local mindist = 100 - local maxdist = 3000 + local mindist = self.minscootdist or 100 + local maxdist = self.maxscootdist or 3000 if Shorad and Shorad:IsAlive() then local NowCoord = Shorad:GetCoordinate() for _,_zone in pairs(self.SkateZones.Set) do @@ -630,7 +642,11 @@ do if rand == 0 then rand = 1 end self:T(self.lid .. " ShootAndScoot to zone "..rand) local ToCoordinate = possibleZones[rand]:GetCoordinate() - Shorad:RouteGroundTo(ToCoordinate,20,"Cone",1) + if self.scootrandomcoord then + ToCoordinate = possibleZones[rand]:GetRandomCoordinate(nil,nil,{land.SurfaceType.LAND,land.SurfaceType.ROAD}) + end + local formation = self.scootformation or "Cone" + Shorad:RouteGroundTo(ToCoordinate,20,formation,1) end end return self From f922b94eb1d5b772c524373a7b1a4c314ff7e970 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 21 Nov 2023 10:13:29 +0100 Subject: [PATCH 416/603] xx --- Moose Development/Moose/Sound/SRS.lua | 50 ++++++--------------------- 1 file changed, 10 insertions(+), 40 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 4ffa93ca4..7652c3128 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -377,9 +377,7 @@ function MSRS:New(PathToSRS, Frequency, Modulation, Volume, AltBackend) return self:_NewAltBackend(Backend) end - local success = self:LoadConfigFile(nil,nil,self.ConfigLoaded) - - if (not success) and (not self.ConfigLoaded) then + if not self.ConfigLoaded then -- If no AltBackend table, the proceed with default initialisation self:SetPath(PathToSRS) @@ -446,7 +444,7 @@ function MSRS:SetPath(Path) end -- Debug output. - self:I(string.format("SRS path=%s", self:GetPath())) + self:T(string.format("SRS path=%s", self:GetPath())) end return self end @@ -1128,7 +1126,6 @@ end -- @param #MSRS self -- @param #string Path Path to config file, defaults to "C:\Users\\Saved Games\DCS\Config" -- @param #string Filename File to load, defaults to "Moose_MSRS.lua" --- @param #boolean ConfigLoaded - if true, skip the loading -- @return #boolean success -- @usage -- 0) Benefits: Centralize configuration of SRS, keep paths and keys out of the mission source code, making it safer and easier to move missions to/between servers, @@ -1190,46 +1187,17 @@ end -- atis:SetSRS(nil,nil,nil,MSRS.Voices.Google.Standard.en_US_Standard_H) -- --Start ATIS -- atis:Start() -function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) +function MSRS:LoadConfigFile(Path,Filename) local path = Path or lfs.writedir()..MSRS.ConfigFilePath local file = Filename or MSRS.ConfigFileName or "Moose_MSRS.lua" + local pathandfile = path..file + local filexsists = UTILS.FileExists(pathandfile) - if UTILS.CheckFileExists(path,file) and not ConfigLoaded then + if filexsists and not MSRS.ConfigLoaded then assert(loadfile(path..file))() -- now we should have a global var MSRS_Config if MSRS_Config then - --[[ - -- Moose MSRS default Config - MSRS_Config = { - Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- adjust as needed - Port = 5002, -- adjust as needed - Frequency = {127,243}, -- must be a table, 1..n entries! - Modulation = {0,0}, -- must be a table, 1..n entries, one for each frequency! - Volume = 1.0, - Coalition = 0, -- 0 = Neutral, 1 = Red, 2 = Blue - Coordinate = {0,0,0}, -- x,y,alt - optional - Culture = "en-GB", - Gender = "male", - Google = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- path to google json key file - optional - Label = "MSRS", - Voice = "Microsoft Hazel Desktop", - -- gRPC (optional) - GRPC = { -- see https://github.com/DCS-gRPC/rust-server - coalition = "blue", -- blue, red, neutral - DefaultProvider = "gcloud", -- win, gcloud, aws, or azure, some of the values below depend on your cloud provider - gcloud = { - key = "", -- for gRPC Google API key - --secret = "", -- needed for aws - --region = "",-- needed for aws - defaultVoice = MSRS.Voices.Google.Standard.en_GB_Standard_F, - }, - win = { - defaultVoice = "Hazel", - }, - } - } - --]] if self then self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" self.port = MSRS_Config.Port or 5002 @@ -1281,8 +1249,9 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded) end end env.info("MSRS - Sucessfully loaded default configuration from disk!",false) - else - env.info("MSRS - Cannot load default configuration from disk!",false) + end + if not filexsists then + env.info("MSRS - Cannot find default configuration file!",false) return false end @@ -2013,6 +1982,7 @@ function MSRSQUEUE:_CheckRadioQueue(delay) end +MSRS.LoadConfigFile() ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 9014e27edcfb05132a2b184383141e06c8c6c811 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 21 Nov 2023 13:21:02 +0100 Subject: [PATCH 417/603] xxx --- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- Moose Development/Moose/Sound/SRS.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index c01f75299..28110a7ec 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1579,7 +1579,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.ClusterRadius = 0.5 self.TargetRadius = 500 - self.ClientFilter = ClientFilter or "" + self.ClientFilter = ClientFilter --or "" self.TargetQueue = FIFO:New() -- Utilities.FiFo#FIFO self.TaskQueue = FIFO:New() -- Utilities.FiFo#FIFO diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 7652c3128..7377de207 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1170,7 +1170,7 @@ end -- This will populate variables for the MSRS raw class and all instances you create with e.g. `mysrs = MSRS:New()` -- Optionally you can also load this per **single instance** if so needed, i.e. -- --- mysrs:LoadConfig(Path,Filename) +-- mysrs:LoadConfigFile(Path,Filename) -- -- 4) Use the config in your code like so, variable names are basically the same as in the config file, but all lower case, examples: -- From ed851ebc86951928af44dbe4df4a1cc580264193 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 22 Nov 2023 17:52:37 +0100 Subject: [PATCH 418/603] SRS --- Moose Development/Moose/Sound/SRS.lua | 54 ++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 7377de207..12460f737 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -50,6 +50,7 @@ -- @field #string ConfigFileName Name of the standard config file -- @field #string ConfigFilePath Path to the standard config file -- @field #boolean ConfigLoaded +-- @field #string ttsprovider Default provider TTS backend, e.g. "Google" or "Microsoft", default is Microsoft -- @extends Core.Base#BASE --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -127,6 +128,10 @@ -- -- Use @{#MSRS.SetVolume} to define the SRS volume. Defaults to 1.0. Allowed values are between 0.0 and 1.0, from silent to loudest. -- +-- ## Config file for many variables, auto-loaded by Moose +-- +-- See @{#MSRS.LoadConfigFile} for details on how to set this up. +-- -- ## Set DCS-gRPC as an alternative to 'DCS-SR-ExternalAudio.exe' for TTS -- -- Use @{#MSRS.SetDefaultBackendGRPC} to enable [DCS-gRPC](https://github.com/DCS-gRPC/rust-server) as an alternate backend for transmitting text-to-speech over SRS. @@ -191,11 +196,12 @@ MSRS = { ConfigFileName = "Moose_MSRS.lua", ConfigFilePath = "Config\\", ConfigLoaded = false, + ttsprovider = "Microsoft", } --- MSRS class version. -- @field #string version -MSRS.version="0.1.2" +MSRS.version="0.1.3" --- Voices -- @type MSRS.Voices @@ -672,7 +678,7 @@ function MSRS:SetCoordinate(Coordinate) return self end ---- Use google text-to-speech. +--- Use google text-to-speech credentials. Also sets Google as default TTS provider. -- @param #MSRS self -- @param #string PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". Can also be the Google API key. -- @return #MSRS self @@ -686,13 +692,14 @@ function MSRS:SetGoogle(PathToCredentials) self.GRPCOptions.DefaultProvider = "gcloud" self.GRPCOptions.gcloud.key = PathToCredentials + self.ttsprovider = "Google" end return self end ---- Use google text-to-speech. +--- gRPC Backend: Use google text-to-speech set the API key. -- @param #MSRS self -- @param #string APIKey API Key, usually a string of length 40 with characters and numbers. -- @return #MSRS self @@ -706,6 +713,22 @@ function MSRS:SetGoogleAPIKey(APIKey) return self end +--- Use Google text-to-speech as default. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetTTSProviderGoogle() + self.ttsprovider = "Google" + return self +end + +--- Use Microsoft text-to-speech as default. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetTTSProviderMicrosoft() + self.ttsprovider = "Microsoft" + return self +end + --- Print SRS STTS help to DCS log file. -- @param #MSRS self -- @return #MSRS self @@ -1112,7 +1135,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp end -- Set google. - if self.google then + if self.google and self.ttsprovider == "Google" then command=command..string.format(' --ssml -G "%s"', self.google) end @@ -1135,18 +1158,19 @@ end -- -- -- Moose MSRS default Config -- MSRS_Config = { --- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- adjust as needed +-- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- adjust as needed, note double \\ -- Port = 5002, -- adjust as needed -- Frequency = {127,243}, -- must be a table, 1..n entries! -- Modulation = {0,0}, -- must be a table, 1..n entries, one for each frequency! --- Volume = 1.0, +-- Volume = 1.0, -- 0.0 to 1.0 -- Coalition = 0, -- 0 = Neutral, 1 = Red, 2 = Blue --- Coordinate = {0,0,0}, -- x,y,alt - optional +-- Coordinate = {0,0,0}, -- x,y,altitude - optional, all in meters -- Culture = "en-GB", -- Gender = "male", --- Google = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- path to google json key file - optional +-- Google = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- path to google json key file - optional. -- Label = "MSRS", -- Voice = "Microsoft Hazel Desktop", +-- Provider = "Microsoft", -- this is the default TTS provider, e.g. "Google" or "Microsoft" -- -- gRPC (optional) -- GRPC = { -- see https://github.com/DCS-gRPC/rust-server -- coalition = "blue", -- blue, red, neutral @@ -1163,9 +1187,13 @@ end -- } -- } -- --- 3) Load the config into the MSRS raw class before you do anything else: +-- 3) The config file is automatically loaded when Moose starts. YOu can also load the config into the MSRS raw class manually before you do anything else: -- -- MSRS.LoadConfigFile() -- Note the "." here +-- +-- Optionally, your might want to provide a specific path and filename: +-- +-- MSRS.LoadConfigFile(nil,MyPath,MyFilename) -- Note the "." here -- -- This will populate variables for the MSRS raw class and all instances you create with e.g. `mysrs = MSRS:New()` -- Optionally you can also load this per **single instance** if so needed, i.e. @@ -1210,6 +1238,9 @@ function MSRS:LoadConfigFile(Path,Filename) self.culture = MSRS_Config.Culture or "en-GB" self.gender = MSRS_Config.Gender or "male" self.google = MSRS_Config.Google + if MSRS_Config.Provider then + self.ttsprovider = MSRS_Config.Provider + end self.Label = MSRS_Config.Label or "MSRS" self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel if MSRS_Config.GRPC then @@ -1234,6 +1265,9 @@ function MSRS:LoadConfigFile(Path,Filename) MSRS.culture = MSRS_Config.Culture or "en-GB" MSRS.gender = MSRS_Config.Gender or "male" MSRS.google = MSRS_Config.Google + if MSRS_Config.Provider then + self.ttsprovider = MSRS_Config.Provider + end MSRS.Label = MSRS_Config.Label or "MSRS" MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel if MSRS_Config.GRPC then @@ -1248,7 +1282,7 @@ function MSRS:LoadConfigFile(Path,Filename) MSRS.ConfigLoaded = true end end - env.info("MSRS - Sucessfully loaded default configuration from disk!",false) + env.info("MSRS - Successfully loaded default configuration from disk!",false) end if not filexsists then env.info("MSRS - Cannot find default configuration file!",false) From fc32252db7f9564119541eb803af3093686592e7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 22 Nov 2023 18:35:04 +0100 Subject: [PATCH 419/603] xx --- Moose Development/Moose/AI/AI_A2G_Dispatcher.lua | 6 +++--- Moose Development/Moose/Sound/SRS.lua | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua index 4dc1329c8..0a4089ce7 100644 --- a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua @@ -1351,14 +1351,14 @@ do -- AI_A2G_DISPATCHER -- 1. the **distance of the closest airbase to target**, being smaller than the **Defend Radius**. -- 2. the **distance to any defense reference point**. -- - -- The **default** defense radius is defined as **400000** or **40km**. Override the default defense radius when the era of the warfare is early, or, + -- The **default** defense radius is defined as **40000** or **40km**. Override the default defense radius when the era of the warfare is early, or, -- when you don't want to let the AI_A2G_DISPATCHER react immediately when a certain border or area is not being crossed. -- -- Use the method @{#AI_A2G_DISPATCHER.SetDefendRadius}() to set a specific defend radius for all squadrons, -- **the Defense Radius is defined for ALL squadrons which are operational.** -- -- @param #AI_A2G_DISPATCHER self - -- @param #number DefenseRadius (Optional, Default = 200000) The defense radius to engage detected targets from the nearest capable and available squadron airbase. + -- @param #number DefenseRadius (Optional, Default = 20000) The defense radius to engage detected targets from the nearest capable and available squadron airbase. -- @return #AI_A2G_DISPATCHER -- @usage -- @@ -1373,7 +1373,7 @@ do -- AI_A2G_DISPATCHER -- function AI_A2G_DISPATCHER:SetDefenseRadius( DefenseRadius ) - self.DefenseRadius = DefenseRadius or 100000 + self.DefenseRadius = DefenseRadius or 40000 self.Detection:SetAcceptRange( self.DefenseRadius ) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 12460f737..537825b1b 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1266,7 +1266,7 @@ function MSRS:LoadConfigFile(Path,Filename) MSRS.gender = MSRS_Config.Gender or "male" MSRS.google = MSRS_Config.Google if MSRS_Config.Provider then - self.ttsprovider = MSRS_Config.Provider + MSRS.ttsprovider = MSRS_Config.Provider end MSRS.Label = MSRS_Config.Label or "MSRS" MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel From a89c96e3c4420414f677b8072bd7b6997d5b1bd2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 23 Nov 2023 17:00:38 +0100 Subject: [PATCH 420/603] fixes --- Moose Development/Moose/Core/ClientMenu.lua | 8 ++++---- .../Moose/Functional/ATC_Ground.lua | 19 +++++++++++++++++-- Moose Development/Moose/Sound/SRS.lua | 8 +++++++- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 1ec4f01a6..525b3533d 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -396,7 +396,7 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.1.3", + version = "0.1.4", name = nil, clientset = nil, menutree = {}, @@ -439,18 +439,18 @@ function CLIENTMENUMANAGER:_EventHandler(EventData) --self:I(self.lid.."_EventHandler: "..tostring(EventData.IniPlayerName)) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then self:T(self.lid.."Leave event for player: "..tostring(EventData.IniPlayerName)) - local Client = _DATABASE:FindClient( EventData.IniPlayerName ) + local Client = _DATABASE:FindClient( EventData.IniUnitName ) if Client then self:ResetMenu(Client) end elseif (EventData.id == EVENTS.PlayerEnterAircraft) and EventData.IniCoalition == self.Coalition then if EventData.IniPlayerName and EventData.IniGroup then - if (not self.clientset:IsIncludeObject(_DATABASE:FindClient( EventData.IniPlayerName ))) then + if (not self.clientset:IsIncludeObject(_DATABASE:FindClient( EventData.IniUnitName ))) then self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName) return self end --self:I(self.lid.."Join event for player: "..EventData.IniPlayerName) - local player = _DATABASE:FindClient( EventData.IniPlayerName ) + local player = _DATABASE:FindClient( EventData.IniUnitName ) self:Propagate(player) end elseif EventData.id == EVENTS.PlayerEnterUnit then diff --git a/Moose Development/Moose/Functional/ATC_Ground.lua b/Moose Development/Moose/Functional/ATC_Ground.lua index ca7e2ae45..67a361bb4 100644 --- a/Moose Development/Moose/Functional/ATC_Ground.lua +++ b/Moose Development/Moose/Functional/ATC_Ground.lua @@ -426,8 +426,15 @@ ATC_GROUND_UNIVERSAL = { --- Creates a new ATC\_GROUND\_UNIVERSAL object. This works on any map. -- @param #ATC_GROUND_UNIVERSAL self --- @param AirbaseList (Optional) A table of Airbase Names. +-- @param AirbaseList A table of Airbase Names. Leave empty to cover **all** airbases of the map. -- @return #ATC_GROUND_UNIVERSAL self +-- @usage +-- -- define monitoring for one airbase +-- local atc=ATC_GROUND_UNIVERSAL:New({AIRBASE.Syria.Gecitkale}) +-- -- set kick speed +-- atc:SetKickSpeed(UTILS.KnotsToMps(20)) +-- -- start monitoring evey 10 secs +-- atc:Start(10) function ATC_GROUND_UNIVERSAL:New(AirbaseList) -- Inherits from BASE @@ -442,6 +449,13 @@ function ATC_GROUND_UNIVERSAL:New(AirbaseList) self.AirbaseList = AirbaseList + if not self.AirbaseList then + self.AirbaseList = {} + for _name,_ in pairs(_DATABASE.AIRBASES) do + self.AirbaseList[_name]=_name + end + end + self.SetClient = SET_CLIENT:New():FilterCategories( "plane" ):FilterStart() @@ -462,6 +476,7 @@ function ATC_GROUND_UNIVERSAL:New(AirbaseList) self.Airbases[AirbaseName].Monitor = true end + self.SetClient:ForEachClient( -- @param Wrapper.Client#CLIENT Client function( Client ) @@ -840,7 +855,7 @@ end --- Start SCHEDULER for ATC_GROUND_UNIVERSAL object. -- @param #ATC_GROUND_UNIVERSAL self --- @param RepeatScanSeconds Time in second for defining occurency of alerts. +-- @param RepeatScanSeconds Time in second for defining schedule of alerts. -- @return #ATC_GROUND_UNIVERSAL self function ATC_GROUND_UNIVERSAL:Start( RepeatScanSeconds ) RepeatScanSeconds = RepeatScanSeconds or 0.05 diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 537825b1b..f24fb6055 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1216,7 +1216,13 @@ end -- --Start ATIS -- atis:Start() function MSRS:LoadConfigFile(Path,Filename) - + if not lfs then + env.info("MSRS - Config load, lfs is not available!",false) + return + end + if not os then + env.info("MSRS - Config load, os is not available!",false) + end local path = Path or lfs.writedir()..MSRS.ConfigFilePath local file = Filename or MSRS.ConfigFileName or "Moose_MSRS.lua" local pathandfile = path..file From 454a6a8350124e3bd260504eae96981d8fd08a89 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 23 Nov 2023 18:14:40 +0100 Subject: [PATCH 421/603] SPAWN --- Moose Development/Moose/Core/Spawn.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index eaa07948a..c1a845c62 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3283,6 +3283,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers local CallsignLen = CallsignName:len() + SpawnTemplate.units[UnitID].callsign[2] = UnitID SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] else SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex From 9eb05fa447540683540f07c3bf3e8cabfb235f96 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 23 Nov 2023 18:45:25 +0100 Subject: [PATCH 422/603] ATC_GROUND fix for scheduler --- .../Moose/Functional/ATC_Ground.lua | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Moose Development/Moose/Functional/ATC_Ground.lua b/Moose Development/Moose/Functional/ATC_Ground.lua index 67a361bb4..eaae1450e 100644 --- a/Moose Development/Moose/Functional/ATC_Ground.lua +++ b/Moose Development/Moose/Functional/ATC_Ground.lua @@ -53,7 +53,7 @@ function ATC_GROUND:New( Airbases, AirbaseList ) -- Inherits from BASE local self = BASE:Inherit( self, BASE:New() ) -- #ATC_GROUND - self:E( { self.ClassName, Airbases } ) + self:T( { self.ClassName, Airbases } ) self.Airbases = Airbases self.AirbaseList = AirbaseList @@ -260,7 +260,7 @@ function ATC_GROUND:_AirbaseMonitor() local IsOnGround = Client:InAir() == false for AirbaseID, AirbaseMeta in pairs( self.Airbases ) do - self:E( AirbaseID, AirbaseMeta.KickSpeed ) + self:T( AirbaseID, AirbaseMeta.KickSpeed ) if AirbaseMeta.Monitor == true and Client:IsInZone( AirbaseMeta.ZoneBoundary ) then @@ -273,7 +273,7 @@ function ATC_GROUND:_AirbaseMonitor() if IsOnGround then local Taxi = Client:GetState( self, "Taxi" ) - self:E( Taxi ) + self:T( Taxi ) if Taxi == false then local Velocity = VELOCITY:New( AirbaseMeta.KickSpeed or self.KickSpeed ) Client:Message( "Welcome to " .. AirbaseID .. ". The maximum taxiing speed is " .. @@ -439,7 +439,7 @@ function ATC_GROUND_UNIVERSAL:New(AirbaseList) -- Inherits from BASE local self = BASE:Inherit( self, BASE:New() ) -- #ATC_GROUND - self:E( { self.ClassName } ) + self:T( { self.ClassName } ) self.Airbases = {} @@ -696,9 +696,9 @@ end -- @param #ATC_GROUND_UNIVERSAL self -- @return #ATC_GROUND_UNIVERSAL self function ATC_GROUND_UNIVERSAL:_AirbaseMonitor() - + self:I("_AirbaseMonitor") self.SetClient:ForEachClient( - -- @param Wrapper.Client#CLIENT Client + --- @param Wrapper.Client#CLIENT Client function( Client ) if Client:IsAlive() then @@ -706,7 +706,7 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor() local IsOnGround = Client:InAir() == false for AirbaseID, AirbaseMeta in pairs( self.Airbases ) do - self:E( AirbaseID, AirbaseMeta.KickSpeed ) + self:T( AirbaseID, AirbaseMeta.KickSpeed ) if AirbaseMeta.Monitor == true and Client:IsInZone( AirbaseMeta.ZoneBoundary ) then @@ -723,7 +723,7 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor() if IsOnGround then local Taxi = Client:GetState( self, "Taxi" ) - self:E( Taxi ) + self:T( Taxi ) if Taxi == false then local Velocity = VELOCITY:New( AirbaseMeta.KickSpeed or self.KickSpeed ) Client:Message( "Welcome to " .. AirbaseID .. ". The maximum taxiing speed is " .. @@ -859,7 +859,7 @@ end -- @return #ATC_GROUND_UNIVERSAL self function ATC_GROUND_UNIVERSAL:Start( RepeatScanSeconds ) RepeatScanSeconds = RepeatScanSeconds or 0.05 - self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) + self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, RepeatScanSeconds ) return self end @@ -999,7 +999,7 @@ end -- @return nothing function ATC_GROUND_CAUCASUS:Start( RepeatScanSeconds ) RepeatScanSeconds = RepeatScanSeconds or 0.05 - self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) + self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, RepeatScanSeconds ) end @@ -1138,7 +1138,7 @@ end -- @return nothing function ATC_GROUND_NEVADA:Start( RepeatScanSeconds ) RepeatScanSeconds = RepeatScanSeconds or 0.05 - self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) + self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, RepeatScanSeconds ) end --- @@ -1295,7 +1295,7 @@ end -- @return nothing function ATC_GROUND_NORMANDY:Start( RepeatScanSeconds ) RepeatScanSeconds = RepeatScanSeconds or 0.05 - self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) + self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, RepeatScanSeconds ) end --- @@ -1438,7 +1438,7 @@ end -- @return nothing function ATC_GROUND_PERSIANGULF:Start( RepeatScanSeconds ) RepeatScanSeconds = RepeatScanSeconds or 0.05 - self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) + self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, RepeatScanSeconds ) end @@ -1562,5 +1562,5 @@ end -- @return nothing function ATC_GROUND_MARIANAISLANDS:Start( RepeatScanSeconds ) RepeatScanSeconds = RepeatScanSeconds or 0.05 - self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds ) + self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, RepeatScanSeconds ) end From 14ff3028e3accc6baaeccdb6acee64deec720a7f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 25 Nov 2023 14:49:06 +0100 Subject: [PATCH 423/603] xxx --- Moose Development/Moose/Core/Spawn.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index c1a845c62..62a24cf8c 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3289,6 +3289,26 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex end end + -- Link16 + local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft + if AddProps then + if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16+UnitID-1 + if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 < 10000 then + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("0%d",SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) + end + end + -- VoiceCallsignNumber + if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber then + SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber+UnitID-1 + end + --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) + -- FlightLead + if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then + SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead = UnitID == 1 and true or false + end + --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) + end end self:T3( { "Template:", SpawnTemplate } ) From ac4702b57cb6a9e365dc4f173ee0c80fdc505693 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 25 Nov 2023 18:26:43 +0100 Subject: [PATCH 424/603] xxx --- Moose Development/Moose/Core/Spawn.lua | 106 ++++++++++++++++++-- Moose Development/Moose/Utilities/Enums.lua | 8 ++ Moose Development/Moose/Utilities/Utils.lua | 28 ++++++ 3 files changed, 135 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 62a24cf8c..6ae5e1bc1 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -320,7 +320,7 @@ function SPAWN:New( SpawnTemplatePrefix ) self.AIOnOff = true -- The AI is on by default when spawning a group. self.SpawnUnControlled = false self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - self.DelayOnOff = false -- No intial delay when spawning the first group. + self.DelayOnOff = false -- No initial delay when spawning the first group. self.SpawnGrouping = nil -- No grouping. self.SpawnInitLivery = nil -- No special livery. self.SpawnInitSkill = nil -- No special skill. @@ -332,6 +332,7 @@ function SPAWN:New( SpawnTemplatePrefix ) self.SpawnInitModexPostfix = nil self.SpawnInitAirbase = nil self.TweakedTemplate = false -- Check if the user is using self made template. + self.SpawnRandomCallsign = false self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -1099,6 +1100,14 @@ function SPAWN:InitRandomizeZones( SpawnZoneTable ) return self end +--- [AIR/Fighter only!] This method randomizes the callsign for a new group. +-- @param #SPAWN self +-- @return #SPAWN self +function SPAWN:InitRandomizeCallsign() + self.SpawnRandomCallsign = true + return self +end + --- This method sets a spawn position for the group that is different from the location of the template. -- @param #SPAWN self -- @param Core.Point#COORDINATE Coordinate The position to spawn from @@ -3275,10 +3284,58 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end -- Callsign + + if self.SpawnRandomCallsign and SpawnTemplate.units[1].callsign then + if type( SpawnTemplate.units[1].callsign ) ~= "number" then + -- change callsign + local min = 1 + local max = 8 + local ctable = CALLSIGN.Aircraft + if string.find(SpawnTemplate.units[1].type, "A-10",1,true) then + max = 12 + end + if string.find(SpawnTemplate.units[1].type, "18",1,true) then + min = 9 + max = 20 + ctable = CALLSIGN.F18 + end + if string.find(SpawnTemplate.units[1].type, "16",1,true) then + min = 9 + max = 20 + ctable = CALLSIGN.F16 + end + if SpawnTemplate.units[1].type == "F-15E" then + min = 9 + max = 18 + ctable = CALLSIGN.F15E + end + local callsignnr = math.random(min,max) + local callsignname = "Enfield" + for name, value in pairs(ctable) do + if value==callsignnr then + callsignname = name + end + end + for UnitID = 1, #SpawnTemplate.units do + SpawnTemplate.units[UnitID].callsign[1] = callsignnr + SpawnTemplate.units[UnitID].callsign[2] = UnitID + SpawnTemplate.units[UnitID].callsign[3] = "1" + SpawnTemplate.units[UnitID].callsign["name"] = tostring(callsignname)..tostring(UnitID).."1" + UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + end + else + -- Ruskis + for UnitID = 1, #SpawnTemplate.units do + SpawnTemplate.units[UnitID].callsign = math.random(1,999) + end + end + end + for UnitID = 1, #SpawnTemplate.units do local Callsign = SpawnTemplate.units[UnitID].callsign if Callsign then if type( Callsign ) ~= "number" then -- blue callsign + UTILS.PrintTableToLog(Callsign,1) Callsign[2] = ((SpawnIndex - 1) % 10) + 1 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers @@ -3293,21 +3350,56 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft if AddProps then if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then - SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16+UnitID-1 - if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 < 10000 then - SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("0%d",SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) + -- 4 digit octal with leading 0 + if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) ~= nil then + local octal = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 + local decimal = UTILS.OctalToDecimal(octal)+UnitID-1 + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",UTILS.DecimalToOctal(decimal)) + else -- ED bug - chars in here + local STN = math.floor(UTILS.RandomGaussian(4088/2,nil,1000,4088)) + STN = STN+UnitID-1 + local OSTN = UTILS.DecimalToOctal(STN) + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",OSTN) + end + end + -- A10CII + if SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN then + -- 3 digit octal with leading 0 + if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then + local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN + local decimal = UTILS.OctalToDecimal(octal)+UnitID-1 + SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",UTILS.DecimalToOctal(decimal)) + else -- ED bug - chars in here + local STN = math.floor(UTILS.RandomGaussian(504/2,nil,100,504)) + STN = STN+UnitID-1 + local OSTN = UTILS.DecimalToOctal(STN) + SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",OSTN) end end -- VoiceCallsignNumber if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber then - SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber+UnitID-1 + SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] end - --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) + -- VoiceCallsignLabel + if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel then + local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string + CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers + local label = "NY" -- Navy One exception + if not string.find(CallsignName," ") then + label = string.upper(string.match(CallsignName,"^%a")..string.match(CallsignName,"%a$")) + end + SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel = label + end + UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) -- FlightLead if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead = UnitID == 1 and true or false end - --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) + -- A10CII + if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.SADL and SpawnTemplate.units[UnitID].datalinks.SADL.settings then + SpawnTemplate.units[UnitID].datalinks.SADL.settings.flightLead = UnitID == 1 and true or false + end + UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) end end diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index e37b1c405..beab1f0fb 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -566,6 +566,14 @@ ENUMS.ReportingName = } } +--- Enums for Link16 transmit power +-- @type ENUMS.Link16Power +ENUMS.Link16Power = { + none = 0, + low = 1, + medium = 2, + high = 3, +} --- Enums for the STORAGE class for stores - which need to be in "" diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 50412a1e6..e30f6fd07 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -3602,3 +3602,31 @@ function table.find_key_value_pair(tbl, key, value) return nil end +--- Convert a decimal to octal +-- @param #number Number the number to convert +-- @return #number Octal +function UTILS.DecimalToOctal(Number) + if Number < 8 then return Number end + local number = tonumber(Number) + local octal = "" + local n=1 + while number > 7 do + local number1 = number%8 + octal = string.format("%d",number1)..octal + local number2 = math.abs(number/8) + if number2 < 8 then + octal = string.format("%d",number2)..octal + end + number = number2 + n=n+1 + end + return tonumber(octal) +end + +--- Convert an octal to decimal +-- @param #number Number the number to convert +-- @return #number Decimal +function UTILS.OctalToDecimal(Number) + return tonumber(Number,8) +end + From b0c294b59bc20eed8440ce5f036cfbf8e583fce3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 25 Nov 2023 18:44:07 +0100 Subject: [PATCH 425/603] -- noise --- Moose Development/Moose/Core/Spawn.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 6ae5e1bc1..3e33aea63 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3321,7 +3321,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 SpawnTemplate.units[UnitID].callsign[2] = UnitID SpawnTemplate.units[UnitID].callsign[3] = "1" SpawnTemplate.units[UnitID].callsign["name"] = tostring(callsignname)..tostring(UnitID).."1" - UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) end else -- Ruskis @@ -3335,7 +3335,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local Callsign = SpawnTemplate.units[UnitID].callsign if Callsign then if type( Callsign ) ~= "number" then -- blue callsign - UTILS.PrintTableToLog(Callsign,1) + -- UTILS.PrintTableToLog(Callsign,1) Callsign[2] = ((SpawnIndex - 1) % 10) + 1 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers @@ -3390,7 +3390,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel = label end - UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) -- FlightLead if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead = UnitID == 1 and true or false @@ -3399,7 +3399,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.SADL and SpawnTemplate.units[UnitID].datalinks.SADL.settings then SpawnTemplate.units[UnitID].datalinks.SADL.settings.flightLead = UnitID == 1 and true or false end - UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) end end From f6af379be6a78f004da84b31a4ba30b0fa45b360 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 26 Nov 2023 16:59:22 +0100 Subject: [PATCH 426/603] #UNIT * Added `GetSTN()` to obtain Link16 info from a unit --- Moose Development/Moose/Wrapper/Unit.lua | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 7ad0998ac..0b3e3300f 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -1659,3 +1659,36 @@ function UNIT:GetSkill() local skill = _DATABASE.Templates.Units[name].Template.skill or "Random" return skill end + +--- Get Link16 STN or SADL TN and other datalink info from Unit, if any. +-- @param #UNIT self +-- @return #string STN STN or TN Octal as string, or nil if not set/capable. +-- @return #string VCL Voice Callsign Label or nil if not set/capable. +-- @return #string VCN Voice Callsign Number or nil if not set/capable. +-- @return #string Lead If true, unit is Flight Lead, else false or nil. +function UNIT:GetSTN() + self:F2(self.UnitName) + local STN = nil -- STN/TN + local VCL = nil -- VoiceCallsignLabel + local VCN = nil -- VoiceCallsignNumber + local FGL = false -- FlightGroupLeader + local template = self:GetTemplate() + if template.AddPropAircraft then + if template.AddPropAircraft.STN_L16 then + STN = template.AddPropAircraft.STN_L16 + elseif template.AddPropAircraft.SADL_TN then + STN = template.AddPropAircraft.SADL_TN + end + VCN = template.AddPropAircraft.VoiceCallsignNumber + VCL = template.AddPropAircraft.VoiceCallsignLabel + end + if template.datalinks and template.datalinks.Link16 and template.datalinks.Link16.settings then + FGL = template.datalinks.Link16.settings.flightLead + end + -- A10CII + if template.datalinks and template.datalinks.SADL and template.datalinks.SADL.settings then + FGL = template.datalinks.SADL.settings.flightLead + end + + return STN, VCL, VCN, FGL +end From 73a62d3a1a477a61751c9c8199aec302cefb5e0f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 27 Nov 2023 16:46:47 +0100 Subject: [PATCH 427/603] #GROUP STNs --- Moose Development/Moose/Wrapper/Group.lua | 35 ++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index f3d8ccc53..05a1f94ca 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2922,7 +2922,7 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) return callsign end ---- +--- Set a GROUP to act as recovery tanker -- @param #GROUP self -- @param Wrapper.Group#GROUP CarrierGroup. -- @param #number Speed Speed in knots. @@ -2948,3 +2948,36 @@ function GROUP:SetAsRecoveryTanker(CarrierGroup,Speed,ToKIAS,Altitude,Delay,Last return self end + +--- Get a list of Link16 S/TN data from a GROUP. Can (as of Nov 2023) be obtained from F-18, F-16, F-15E (not the user flyable one) and A-10C-II groups. +-- @param #GROUP self +-- @return #table Table of data entries, indexed by unit name, each entry is a table containing STN, VCL (voice call label), VCN (voice call number), and Lead (#boolean, if true it's the flight lead) +-- @return #string Report Formatted report of all data +function GROUP:GetGroupSTN() + local tSTN = {} -- table + local units = self:GetUnits() + local gname = self:GetName() + gname = string.gsub(gname,"(#%d+)$","") + local report = REPORT:New() + report:Add("Link16 S/TN Report") + report:Add("Group: "..gname) + report:Add("==================") + for _,_unit in pairs(units) do + local unit = _unit -- Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + local STN, VCL, VCN, Lead = unit:GetSTN() + local name = unit:GetName() + tSTN[name] = { + STN=STN, + VCL=VCL, + VCN=VCN, + Lead=Lead, + } + local lead = Lead == true and "(*)" or "" + report:Add(string.format("| %s%s %s %s",tostring(VCL),tostring(VCN),tostring(STN),lead)) + end + end + report:Add("==================") + local text = report:Text() + return tSTN,text +end From b33df5374f6ee65310023a0959da203db432b2a2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 28 Nov 2023 10:36:28 +0100 Subject: [PATCH 428/603] #PLAYERRECCE * Bug fix for clock view calculation * Ensure lasing is switched off when using the menu * Create SPOT more often --- Moose Development/Moose/Ops/PlayerRecce.lua | 49 +++++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index c8cad0bdd..70853e14b 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -104,7 +104,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.21", + version = "0.0.22", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, @@ -469,8 +469,10 @@ function PLAYERRECCE:_GetClockDirection(unit, target) local _playerPosition = unit:GetCoordinate() -- get position of helicopter local _targetpostions = target:GetCoordinate() -- get position of downed pilot local _heading = unit:GetHeading() -- heading + --self:I("Heading = ".._heading) local DirectionVec3 = _playerPosition:GetDirectionVec3( _targetpostions ) local Angle = _playerPosition:GetAngleDegrees( DirectionVec3 ) + --self:I("Angle = "..Angle) local clock = 12 local hours = 0 if _heading and Angle then @@ -478,10 +480,13 @@ function PLAYERRECCE:_GetClockDirection(unit, target) --if angle == 0 then angle = 360 end clock = _heading-Angle hours = (clock/30)*-1 + --self:I("hours = "..hours) clock = 12+hours clock = UTILS.Round(clock,0) if clock > 12 then clock = clock-12 end - end + if clock == 0 then clock = 12 end + end + --self:I("Clock ="..clock) return clock end @@ -912,32 +917,41 @@ function PLAYERRECCE:_LaseTarget(client,targetset) else laser = self.LaserSpots[playername] end + -- old target if self.LaserTarget[playername] then -- still looking at target? local target=self.LaserTarget[playername] -- Ops.Target#TARGET local oldtarget = target:GetObject() --or laser.Target - --self:I("Targetstate: "..target:GetState()) - --self:I("Laser State: "..tostring(laser:IsLasing())) - if not oldtarget or targetset:IsNotInSet(oldtarget) or target:IsDead() or target:IsDestroyed() then + self:T("Targetstate: "..target:GetState()) + self:T("Laser State: "..tostring(laser:IsLasing())) + if (not oldtarget) or targetset:IsNotInSet(oldtarget) or target:IsDead() or target:IsDestroyed() then -- lost LOS or dead laser:LaseOff() if target:IsDead() or target:IsDestroyed() or target:GetLife() < 2 then self:__Shack(-1,client,oldtarget) - self.LaserTarget[playername] = nil + --self.LaserTarget[playername] = nil else self:__TargetLOSLost(-1,client,oldtarget) - self.LaserTarget[playername] = nil + --self.LaserTarget[playername] = nil end - end - if oldtarget and (not laser:IsLasing()) then - --self:I("Switching laser back on ..") + self.LaserTarget[playername] = nil + oldtarget = nil + self.LaserSpots[playername] = nil + elseif oldtarget and laser and (not laser:IsLasing()) then + --laser:LaseOff() + self:T("Switching laser back on ..") local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688 local lasingtime = self.lasingtime or 60 --local targettype = target:GetTypeName() laser:LaseOn(oldtarget,lasercode,lasingtime) --self:__TargetLasing(-1,client,oldtarget,lasercode,lasingtime) + else + -- we should not be here... + self:T("Target alive and laser is on!") + --self.LaserSpots[playername] = nil end - elseif not laser:IsLasing() and target then + -- new target + elseif (not laser:IsLasing()) and target then local relativecam = self.LaserRelativePos[client:GetTypeName()] laser:SetRelativeStartPosition(relativecam) local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688 @@ -945,7 +959,7 @@ function PLAYERRECCE:_LaseTarget(client,targetset) --local targettype = target:GetTypeName() laser:LaseOn(target,lasercode,lasingtime) self.LaserTarget[playername] = TARGET:New(target) - self.LaserTarget[playername].TStatus = 9 + --self.LaserTarget[playername].TStatus = 9 self:__TargetLasing(-1,client,target,lasercode,lasingtime) end return self @@ -1027,6 +1041,13 @@ function PLAYERRECCE:_SwitchLasing(client,group,playername) MESSAGE:New("Lasing is now ON",10,self.Name or "FACA"):ToClient(client) else self.AutoLase[playername] = false + if self.LaserSpots[playername] then + local laser = self.LaserSpots[playername] -- Core.Spot#SPOT + if laser:IsLasing() then + laser:LaseOff() + end + self.LaserSpots[playername] = nil + end MESSAGE:New("Lasing is now OFF",10,self.Name or "FACA"):ToClient(client) end if self.ClientMenus[playername] then @@ -1681,7 +1702,7 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername) local text2tts = string.format("All stations, FACA %s on station at %s!",callsign, coordtext) text2tts = self:_GetTextForSpeech(text2tts) if self.debug then - self:I(text2.."\n"..text2tts) + self:T(text2.."\n"..text2tts) end if self.UseSRS then local grp = Client:GetGroup() @@ -1720,7 +1741,7 @@ function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername) local texttts = string.format("All stations, FACA %s leaving station at %s, good bye!",callsign, coordtext) texttts = self:_GetTextForSpeech(texttts) if self.debug then - self:I(text.."\n"..texttts) + self:T(text.."\n"..texttts) end local text1 = "Going home!" if self.UseSRS then From 37dab7b8bdac92ae680f3af215ec8cfc97b9e093 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 29 Nov 2023 16:42:55 +0100 Subject: [PATCH 429/603] color --- Moose Development/Moose/Ops/OpsZone.lua | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsZone.lua b/Moose Development/Moose/Ops/OpsZone.lua index 01b7a3b19..780b389c0 100644 --- a/Moose Development/Moose/Ops/OpsZone.lua +++ b/Moose Development/Moose/Ops/OpsZone.lua @@ -1483,11 +1483,11 @@ function OPSZONE:_GetZoneColor() local color={0,0,0} if self.ownerCurrent==coalition.side.NEUTRAL then - color={1, 1, 1} + color=self.ZoneOwnerNeutral or {1, 1, 1} elseif self.ownerCurrent==coalition.side.BLUE then - color={0, 0, 1} + color=self.ZoneOwnerBlue or {0, 0, 1} elseif self.ownerCurrent==coalition.side.RED then - color={1, 0, 0} + color=self.ZoneOwnerRed or {1, 0, 0} else end @@ -1495,6 +1495,21 @@ function OPSZONE:_GetZoneColor() return color end +--- Set custom RGB color of zone depending on current owner. +-- @param #OPSZONE self +-- @param #table Neutral Color is a table of RGB values 0..1 for Red, Green, and Blue respectively, e.g. {1,0,0} for red. +-- @param #table Blue Color is a table of RGB values 0..1 for Red, Green, and Blue respectively, e.g. {0,1,0} for green. +-- @param #table Red Color is a table of RGB values 0..1 for Red, Green, and Blue respectively, e.g. {0,0,1} for blue. +-- @return #OPSZONE self +function OPSZONE:SetZoneColor(Neutral, Blue, Red) + + self.ZoneOwnerNeutral = Neutral or {1, 1, 1} + self.ZoneOwnerBlue = Blue or {0, 0, 1} + self.ZoneOwnerRed = Red or {1, 0, 0} + + return self +end + --- Update marker on the F10 map. -- @param #OPSZONE self function OPSZONE:_UpdateMarker() From 2488c0dd05c0feb8f354ab51e32f0e331585d51b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 29 Nov 2023 17:36:57 +0100 Subject: [PATCH 430/603] #SPAWN * Link16 Team Members --- Moose Development/Moose/Core/Spawn.lua | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 3e33aea63..f80dbd41b 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3324,7 +3324,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) end else - -- Ruskis + -- Russkis for UnitID = 1, #SpawnTemplate.units do SpawnTemplate.units[UnitID].callsign = math.random(1,999) end @@ -3402,8 +3402,25 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) end end + -- Link16 team members + for UnitID = 1, #SpawnTemplate.units do + if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.network then + local team = {} + local isF16 = string.find(SpawnTemplate.units[UnitID].type,"F-16",1,true) and true or false + for ID = 1, #SpawnTemplate.units do + local member = {} + member.missionUnitId = ID + if isF16 then + member.TDOA = true + end + table.insert(team,member) + end + SpawnTemplate.units[UnitID].datalinks.Link16.network.teamMembers = team + end + end self:T3( { "Template:", SpawnTemplate } ) + --UTILS.PrintTableToLog(SpawnTemplate,1) return SpawnTemplate end From d7c7a1a1dd040140264924a23c7bcc92dcc0f828 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Dec 2023 16:28:04 +0100 Subject: [PATCH 431/603] xxx --- Moose Development/Moose/Core/Point.lua | 2 +- Moose Development/Moose/Ops/FlightControl.lua | 6 +++++- Moose Development/Moose/Ops/PlayerRecce.lua | 4 ++-- Moose Development/Moose/Utilities/Utils.lua | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 79cf7dffa..64d5aa9d8 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2565,7 +2565,7 @@ do -- COORDINATE Offset=Offset or 2 - -- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each Coordinate. + -- Measurement of visibility should not be from the ground, so Adding a hypothetical 2 meters to each Coordinate. local FromVec3 = self:GetVec3() FromVec3.y = FromVec3.y + Offset diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index bab56059a..8403e0c24 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -2811,7 +2811,11 @@ function FLIGHTCONTROL:_PlayerInfoATIS(groupname) -- Radio message. self:TransmissionPilot(rtext, flight) - self:TransmissionTower(srstxt,flight,10) + if self.atis then + self:TransmissionTower(srstxt,flight,10) + else + self:TransmissionTower(text,flight,10) + end else self:E(self.lid..string.format("Cannot find flight group %s.", tostring(groupname))) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 70853e14b..ec3a57954 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -714,8 +714,8 @@ function PLAYERRECCE:_GetViewZone(unit, vheading, minview, maxview, angle, camon local heading2 = (vheading-90)%360 self:T({heading1,heading2}) local startpos = startp:Translate(minview,vheading) - local pos1 = startpos:Translate(10,heading1) - local pos2 = startpos:Translate(10,heading2) + local pos1 = startpos:Translate(12.5,heading1) + local pos2 = startpos:Translate(12.5,heading2) local pos3 = pos1:Translate(maxview,vheading) local pos4 = pos2:Translate(maxview,vheading) local array = {} diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index f8fbfe2eb..7147ef789 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1051,9 +1051,9 @@ function UTILS.BeaufortScale(speed) return bn,bd end ---- Split string at seperators. C.f. [split-string-in-lua](http://stackoverflow.com/questions/1426954/split-string-in-lua). +--- Split string at separators. C.f. [split-string-in-lua](http://stackoverflow.com/questions/1426954/split-string-in-lua). -- @param #string str Sting to split. --- @param #string sep Speparator for split. +-- @param #string sep Separator for split. -- @return #table Split text. function UTILS.Split(str, sep) local result = {} From 4e19c91be5c5d402d2177a1087a73fd486341973 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Sun, 3 Dec 2023 09:26:39 +0100 Subject: [PATCH 432/603] Update Event.lua (#2055) Fix for playername in weapon target --- Moose Development/Moose/Core/Event.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index b5122c04e..d3a105631 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -1348,7 +1348,8 @@ function EVENT:onEvent( Event ) Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit! - Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName() + Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon.getPlayerName and Event.Weapon:getPlayerName() + --Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName() Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() From 1f4721b10e6c7a6271c8ddb6059bca0807914cb4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 3 Dec 2023 15:34:39 +0100 Subject: [PATCH 433/603] #clarifications --- Moose Development/Moose/Core/Set.lua | 10 +++++++++- Moose Development/Moose/Functional/Range.lua | 12 ++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index ee0917406..fec517906 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1065,8 +1065,15 @@ do self:FilterActive( false ) return self + + --- Filter the set once + -- @function [parent=#SET_GROUP] FilterOnce + -- @param #SET_GROUP self + -- @return #SET_GROUP self + + end - + --- Get a *new* set that only contains alive groups. -- @param #SET_GROUP self -- @return #SET_GROUP Set of alive groups. @@ -1976,6 +1983,7 @@ do --- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search. -- @param #SET_GROUP self -- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined. + -- @param #table Coalitions (Optional) Table of coalition #number entries to filter for. -- @return Wrapper.Group#GROUP The closest group (if any). -- @return #number Distance in meters to the closest group. function SET_GROUP:GetClosestGroup(Coordinate, Coalitions) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index e9005ace0..e238914a9 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1234,7 +1234,7 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, return self end ---- (SRS) Set range control frequency and voice. +--- (SRS) Set range control frequency and voice. Use `RANGE:SetSRS()` once first before using this function. -- @param #RANGE self -- @param #number frequency Frequency in MHz. Default 256 MHz. -- @param #number modulation Modulation, defaults to radio.modulation.AM. @@ -1244,6 +1244,10 @@ end -- @param #string relayunitname Name of the unit used for transmission location. -- @return #RANGE self function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender, relayunitname ) + if not self.instructmsrs then + self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeControl!") + return self + end self.rangecontrolfreq = frequency or 256 self.controlmsrs:SetFrequencies(self.rangecontrolfreq) self.controlmsrs:SetModulations(modulation or radio.modulation.AM) @@ -1259,7 +1263,7 @@ function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender return self end ---- (SRS) Set range instructor frequency and voice. +--- (SRS) Set range instructor frequency and voice. Use `RANGE:SetSRS()` once first before using this function. -- @param #RANGE self -- @param #number frequency Frequency in MHz. Default 305 MHz. -- @param #number modulation Modulation, defaults to radio.modulation.AM. @@ -1269,6 +1273,10 @@ end -- @param #string relayunitname Name of the unit used for transmission location. -- @return #RANGE self function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gender, relayunitname ) + if not self.instructmsrs then + self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeInstructor!") + return self + end self.instructorfreq = frequency or 305 self.instructmsrs:SetFrequencies(self.instructorfreq) self.instructmsrs:SetModulations(modulation or radio.modulation.AM) From 58fa533f79b168cc10f4428dd65df6b6e4d6201e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 6 Dec 2023 08:43:43 +0100 Subject: [PATCH 434/603] xxx --- Moose Development/Moose/Core/Spawn.lua | 4 +- Moose Development/Moose/Modules_local.lua | 1 + Moose Development/Moose/Ops/AirWing.lua | 97 ++ Moose Development/Moose/Ops/EasyA2G.lua | 1510 +++++++++++++++++ Moose Development/Moose/Ops/Intelligence.lua | 9 +- .../Moose/Wrapper/Controllable.lua | 2 +- 6 files changed, 1617 insertions(+), 6 deletions(-) create mode 100644 Moose Development/Moose/Ops/EasyA2G.lua diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index f80dbd41b..388b9aafe 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3377,11 +3377,11 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end end -- VoiceCallsignNumber - if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber then + if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber and type( Callsign ) ~= "number" then SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] end -- VoiceCallsignLabel - if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel then + if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel and type( Callsign ) ~= "number" then local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers local label = "NY" -- Navy One exception diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 46265b420..e7f671abe 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -111,6 +111,7 @@ __Moose.Include( 'Ops\\Operation.lua' ) __Moose.Include( 'Ops\\FlightControl.lua' ) __Moose.Include( 'Ops\\PlayerRecce.lua' ) __Moose.Include( 'Ops\\EasyGCICAP.lua' ) +__Moose.Include( 'Ops\\EasyA2G.lua' ) __Moose.Include( 'AI\\AI_Balancer.lua' ) __Moose.Include( 'AI\\AI_Air.lua' ) diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 9d918e5d9..367823997 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -36,12 +36,14 @@ -- @field Core.Set#SET_ZONE zonesetAWACS Set of AWACS zones. -- @field Core.Set#SET_ZONE zonesetRECON Set of RECON zones. -- @field #number nflightsCAP Number of CAP flights constantly in the air. +-- @field #number nflightsCAS Number of CAP flights constantly in the air. -- @field #number nflightsAWACS Number of AWACS flights constantly in the air. -- @field #number nflightsTANKERboom Number of TANKER flights with BOOM constantly in the air. -- @field #number nflightsTANKERprobe Number of TANKER flights with PROBE constantly in the air. -- @field #number nflightsRescueHelo Number of Rescue helo flights constantly in the air. -- @field #number nflightsRecon Number of Recon flights constantly in the air. -- @field #table pointsCAP Table of CAP points. +-- @field #table pointsCAS Table of CAS points. -- @field #table pointsTANKER Table of Tanker points. -- @field #table pointsAWACS Table of AWACS points. -- @field #table pointsRecon Table of RECON points. @@ -126,6 +128,7 @@ AIRWING = { payloads = {}, payloadcounter = 0, pointsCAP = {}, + pointsCAS = {}, pointsTANKER = {}, pointsAWACS = {}, pointsRecon = {}, @@ -226,6 +229,7 @@ function AIRWING:New(warehousename, airwingname) -- Defaults: self.nflightsCAP=0 + self.nflightsCAS=0 self.nflightsAWACS=0 self.nflightsRecon=0 self.nflightsTANKERboom=0 @@ -703,6 +707,24 @@ function AIRWING:SetNumberCAP(n) return self end +--- Set number of CAS flights constantly carried out. +-- @param #AIRWING self +-- @param #number n Number of flights. Default 1. +-- @return #AIRWING self +function AIRWING:SetNumberCAS(n) + self.nflightsCAS=n or 1 + return self +end + +--- Set CAS flight formation. +-- @param #AIRWING self +-- @param #number Formation Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). +-- @return #AIRWING self +function AIRWING:SetCASFormation(Formation) + self.casFormation = Formation + return self +end + --- Set CAP flight formation. -- @param #AIRWING self -- @param #number Formation Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). @@ -855,6 +877,37 @@ function AIRWING:AddPatrolPointCAP(Coordinate, Altitude, Speed, Heading, LegLeng return self end +--- Add a patrol Point for CAS missions. +-- @param #AIRWING self +-- @param Core.Zone#ZONE_BASE Zone Zone to patrol. +-- @param #number Altitude Orbit altitude in feet. +-- @param #number Speed Orbit speed in knots. +-- @param #number RangeMax Max Range in NM. +-- @param Core.Set#SET_ZONE NoEngageZoneSet (Optional) Non engagement zone set +-- @param #table TargetTypes (Optional) Types of target attributes that will be engaged. See DCS enum attributes. Default {"Helicopters", "Ground Units", "Light armed ships"}. +-- @return #AIRWING self +function AIRWING:AddPatrolPointCAS(Zone, Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes) + + local patrolpoint={} --#AIRWING.PatrolData + patrolpoint.type = "CAS" + patrolpoint.zone = Zone + patrolpoint.altitude=Altitude or math.random(10,20)*1000 + patrolpoint.speed=Speed or 250 + patrolpoint.noccupied=0 + patrolpoint.NoEngageZoneSet=NoEngageZoneSet + patrolpoint.TargetTypes=TargetTypes + patrolpoint.RangeMax=RangeMax or 100 + + if self.markpoints then + patrolpoint.marker=MARKER:New(Coordinate, "New Patrol Point"):ToAll() + AIRWING.UpdatePatrolPointMarker(patrolpoint) + end + + table.insert(self.pointsCAS, patrolpoint) + + return self +end + --- Add a patrol Point for RECON missions. -- @param #AIRWING self -- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. @@ -1014,6 +1067,9 @@ function AIRWING:onafterStatus(From, Event, To) -- Check CAP missions. self:CheckCAP() + + -- Check CAP missions. + self:CheckCAS() -- Check TANKER missions. self:CheckTANKER() @@ -1178,6 +1234,47 @@ function AIRWING:CheckCAP() return self end +--- Check how many CAS missions are assigned and add number of missing missions. +-- @param #AIRWING self +-- @return #AIRWING self +function AIRWING:CheckCAS() + + local Ncap=0 + + + -- Count CAP missions. + for _,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + + if mission:IsNotOver() and (mission.type==AUFTRAG.Type.CASENHANCED or mission.type == AUFTRAG.Type.PATROLRACETRACK) and mission.patroldata then + Ncap=Ncap+1 + end + + end + + for i=1,self.nflightsCAS-Ncap do + + local patrol=self:_GetPatrolData(self.pointsCAS) + + local altitude=patrol.altitude+500*patrol.noccupied + + local missionCAS = nil -- Ops.Auftrag#AUFTRAG + + missionCAS=AUFTRAG:NewCASENHANCED(patrol.zone,altitude,patrol.speed,patrol.RangeMax,patrol.NoEngageZoneSet,patrol.TargetTypes) + + missionCAS.patroldata=patrol + + patrol.noccupied=patrol.noccupied+1 + + if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end + + self:AddMission(missionCAS) + + end + + return self +end + --- Check how many RECON missions are assigned and add number of missing missions. -- @param #AIRWING self -- @return #AIRWING self diff --git a/Moose Development/Moose/Ops/EasyA2G.lua b/Moose Development/Moose/Ops/EasyA2G.lua new file mode 100644 index 000000000..8082ca350 --- /dev/null +++ b/Moose Development/Moose/Ops/EasyA2G.lua @@ -0,0 +1,1510 @@ +------------------------------------------------------------------------- +-- Easy A2G Class, based on OPS classes +------------------------------------------------------------------------- +-- Documentation +-- +-- https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Ops.EasyA2G.html +-- +------------------------------------------------------------------------- +-- Date: December 2023 +------------------------------------------------------------------------- +-- +--- **Ops** - Easy A2G Manager +-- +-- === +-- +-- **Main Features:** +-- +-- * Automatically create and manage A2G CAS/BAI/SEAD defenses using an AirWing and Squadrons for one coalition +-- * Easy set-up +-- * Add additional AirWings on other airbases +-- * Each wing can have more than one Squadron - tasking to Squadrons is done on a random basis per AirWing +-- * Create borders and zones of engagement +-- * Detection can be ground based and/or via AWACS +-- +-- === +-- +-- ### AUTHOR: **applevangelist** +-- +-- @module Ops.EasyA2G +-- @image AI_Air_To_Ground_Dispatching.JPG + + +--- EASYA2G Class +-- @type EASYA2G +-- @field #string ClassName +-- @field #number overhead +-- @field #number engagerange +-- @field #number casgrouping +-- @field #string airbasename +-- @field Wrapper.Airbase#AIRBASE airbase +-- @field #number coalition +-- @field #string alias +-- @field #table wings +-- @field Ops.Intelligence#INTEL Intel +-- @field #number resurrection +-- @field #number casspeed +-- @field #number casalt +-- @field #number casdir +-- @field #number casleg +-- @field #number maxinterceptsize +-- @field #number missionrange +-- @field #number noaltert5 +-- @field #table ManagedAW +-- @field #table ManagedSQ +-- @field #table ManagedCP +-- @field #table ManagedTK +-- @field #table ManagedEWR +-- @field #table ManagedREC +-- @field #number MaxAliveMissions +-- @field #boolean debug +-- @field #number repeatsonfailure +-- @field Core.Set#SET_ZONE GoZoneSet +-- @field Core.Set#SET_ZONE NoGoZoneSet +-- @field #boolean Monitor +-- @field #boolean TankerInvisible +-- @field #number CasFormation +-- @extends Core.Fsm#FSM + +--- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown. +-- +-- === +-- +-- # The EASYA2G Concept +-- +-- The idea of this class is partially to make the OPS classes easier operational for an A2G defense network, and to replace the legacy AI_A2G_Dispatcher system - not to it's +-- full extent, but make a basic system work very quickly. +-- +-- # Setup +-- +-- ## Basic understanding +-- +-- The basics are, there is **one** and only **one** AirWing per airbase. Each AirWing has **at least** one Squadron, who will do CAS, SEAS and BAI tasks. Squadrons will be randomly chosen for the task at hand. +-- Each AirWing has **at least** one Recon Point that it manages. Recon Points will be covered by the AirWing automatically as long as airframes are available. Detected intruders will be assigned to **one** +-- AirWing based on proximity (that is, if you have more than one). +-- +-- ## Assignment of tasks for intruders +-- +-- Either a A2G Plane or a newly spawned plane will take care of the intruders. Standard overhead is 0.20, i.e. a group of 5 intrudes will +-- be managed by 1 plane from the assigned AirWing. There is an maximum missions limitation per AirWing, so we do not spam the skies. +-- +-- ## Basic set-up code +-- +-- ### Prerequisites +-- +-- You have to put a STATIC object on the airbase with the UNIT name according to the name of the airbase. E.g. for Kuitaisi this has to have the name Kutaisi. This object symbolizes the AirWing HQ. +-- Next put a late activated template group for your A2G Squadron on the map. Last, put a zone on the map for the CAP operations, let's name it "Blue Zone 1". Size of the zone plays no role. +-- Put an EW radar system on the map and name it aptly, like "Blue EWR". +-- +-- ### Code it +-- +-- -- Set up a basic system for the blue side, we'll reside on Kutaisi, and use GROUP objects with "Blue EWR" in the name as EW Radar Systems. +-- local mywing = EASYA2G:New("Blue A2G Operations",AIRBASE.Caucasus.Kutaisi,"blue","Blue EWR") +-- +-- -- Add a Recon patrol point belonging to our airbase, we'll be at 30k ft doing 400 kn, initial direction 90 degrees (East), leg 20NM +-- mywing:AddPatrolPointCAP(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone 1"):GetCoordinate(),30000,400,90,20) +-- +-- -- Add a Squadron with template "Blue Sq1 M2000c", 20 airframes, skill good, Modex starting with 102 and skin "Vendee Jeanne" +-- mywing:AddSquadron("Blue Sq1 M2000c","CAP Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.GOOD,102,"ec1.5_Vendee_Jeanne_clean") +-- +-- -- Add a couple of zones +-- -- We'll defend our border +-- mywing:AddAcceptZone(ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) )) +-- -- We'll attack intruders also here +-- mywing:AddAcceptZone(ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" ))) +-- -- We'll leave the reds alone on their turf +-- mywing:AddRejectZone(ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) )) +-- +-- -- Optional - Draw the borders on the map so we see what's going on +-- -- Set up borders on map +-- local BlueBorder = ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) ) +-- BlueBorder:DrawZone(-1,{0,0,1},1,FillColor,FillAlpha,1,true) +-- local BlueNoGoZone = ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" )) +-- BlueNoGoZone:DrawZone(-1,{1,1,0},1,FillColor,FillAlpha,2,true) +-- local BlueNoGoZone2 = ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) ) +-- BlueNoGoZone2:DrawZone(-1,{1,0,0},1,FillColor,FillAlpha,4,true) +-- +-- ### Add a second airwing with squads and own CAP point (optional) +-- +-- -- Set this up at Sukhumi +-- mywing:AddAirwing(AIRBASE.Caucasus.Sukhumi_Babushara,"Blue A2G Sukhumi") +-- -- Recon Point "Blue Zone 2" +-- mywing:AddPatrolPointCAP(AIRBASE.Caucasus.Sukhumi_Babushara,ZONE:FindByName("Blue Zone 2"):GetCoordinate(),30000,400,90,20) +-- +-- -- This one has two squadrons to choose from +-- mywing:AddSquadron("Blue Sq3 F16","CAP Sukhumi II",AIRBASE.Caucasus.Sukhumi_Babushara,20,AI.Skill.GOOD,402,"JASDF 6th TFS 43-8526 Skull Riders") +-- mywing:AddSquadron("Blue Sq2 F15","CAP Sukhumi I",AIRBASE.Caucasus.Sukhumi_Babushara,20,AI.Skill.GOOD,202,"390th Fighter SQN") +-- +-- ### Add a tanker (optional) +-- +-- -- **Note** If you need different tanker types, i.e. Boom and Drogue, set them up at different AirWings! +-- -- Add a tanker point +-- mywing:AddPatrolPointTanker(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone Tanker"):GetCoordinate(),20000,280,270,50) +-- -- Add an AWACS squad - Radio 251 AM, TACAN 51Y +-- mywing:AddTankerSquadron("Blue Tanker","Tanker Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.EXCELLENT,602,nil,251,radio.modulation.AM,51) +-- +-- ### Add an AWACS (optional) +-- +-- -- Add an AWACS point +-- mywing:AddPatrolPointAwacs(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone AWACS"):GetCoordinate(),25000,300,270,50) +-- -- Add a tanker squad - Radio 251 AM, TACAN 51Y +-- mywing:AddAWACSSquadron("Blue AWACS","AWACS Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.AVERAGE,702,nil,271,radio.modulation.AM) +-- +-- # Fine-Tuning +-- +-- ## Change Defaults +-- +-- * @{#EASYA2G.SetDefaultResurrection}: Set how many seconds the AirWing stays inoperable after the AirWing STATIC HQ ist destroyed, default 900 secs. +-- * @{#EASYA2G.SetDefaultCAPSpeed}: Set how many knots the CAP flights should do (will be altitude corrected), default 300 kn. +-- * @{#EASYA2G.SetDefaultCAPAlt}: Set at which altitude (ASL) the CAP planes will fly, default 25,000 ft. +-- * @{#EASYA2G.SetDefaultCAPDirection}: Set the initial direction from the CAP point the planes will fly in degrees, default is 90°. +-- * @{#EASYA2G.SetDefaultCAPLeg}: Set the length of the CAP leg, default is 15 NM. +-- * @{#EASYA2G.SetDefaultCAPGrouping}: Set how many planes will be spawned per mission (CVAP/GCI), defaults to 2. +-- * @{#EASYA2G.SetDefaultMissionRange}: Set how many NM the planes can go from the home base, defaults to 100. +-- * @{#EASYA2G.SetDefaultNumberAlter5Standby}: Set how many planes will be spawned on cold standby (Alert5), default 2. +-- * @{#EASYA2G.SetDefaultEngageRange}: Set max engage range for CAP flights if they detect intruders, defaults to 50. +-- * @{#EASYA2G.SetMaxAliveMissions}: Set max parallel missions can be done (CAP+GCI+Alert5+Tanker+AWACS), defaults to 8. +-- * @{#EASYA2G.SetDefaultRepeatOnFailure}: Set max repeats on failure for intercepting/killing intruders, defaults to 3. +-- * @{#EASYA2G.SetTankerAndAWACSInvisible}: Set Tanker and AWACS to be invisible to enemy AI eyes. Is set to `true` by default. +-- +-- ## Debug and Monitor +-- +-- mywing.debug = true -- log information +-- mywing.Monitor = true -- show some statistics on screen +-- +-- +-- @field #EASYA2G +EASYA2G = { + ClassName = "EASYA2G", + overhead = 0.2, + casgrouping = 2, + airbasename = nil, + airbase = nil, + coalition = "blue", + alias = nil, + wings = {}, + Intel = nil, + resurrection = 900, + casspeed = 250, + casalt = 10000, + casdir = 45, + casleg = 15, + maxinterceptsize = 2, + missionrange = 100, + noaltert5 = 0, + ManagedAW = {}, + ManagedSQ = {}, + ManagedCP = {}, + ManagedTK = {}, + ManagedEWR = {}, + ManagedREC = {}, + MaxAliveMissions = 10, + debug = true, + engagerange = 50, + repeatsonfailure = 10, + GoZoneSet = nil, + NoGoZoneSet = nil, + Monitor = true, + TankerInvisible = true, + CasFormation = nil, +} + +--- Internal Squadron data type +-- @type EASYA2G.Squad +-- @field #string TemplateName +-- @field #string SquadName +-- @field #string AirbaseName +-- @field #number AirFrames +-- @field #string Skill +-- @field #string Modex +-- @field #string Livery +-- @field #boolean Tanker +-- @field #boolean AWACS +-- @field #boolean RECON +-- @field #number Frequency +-- @field #number Modulation +-- @field #number TACAN + +--- Internal Wing data type +-- @type EASYA2G.Wing +-- @field #string AirbaseName +-- @field #string Alias +-- @field #string CapZoneName + +--- Internal CasPoint data type +-- @type EASYA2G.CasPoint +-- @field #string AirbaseName +-- @field Core.Point#COORDINATE Coordinate +-- @field #number Altitude +-- @field #number Speed +-- @field #number Heading +-- @field #number LegLength + +--- EASYA2G class version. +-- @field #string version +EASYA2G.version="0.0.9" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: TBD + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new GCICAP Manager +-- @param #EASYA2G self +-- @param #string Alias +-- @param #string AirbaseName +-- @param #string Coalition +-- @param #string EWRName +-- @return #EASYA2G self +function EASYA2G:New(Alias, AirbaseName, Coalition, EWRName) + -- Inherit everything from FSM class. + local self=BASE:Inherit(self, FSM:New()) -- #EASYA2G + + -- defaults + self.alias = Alias or AirbaseName.." A2G Wing" + self.coalitionname = string.lower(Coalition) or "blue" + self.coalition = self.coaltitionname == "blue" and coalition.side.BLUE or coalition.side.RED + self.wings = {} + self.EWRName = EWRName or self.coalitionname.." EWR" + --self.CapZoneName = CapZoneName + self.airbasename = AirbaseName + self.airbase = AIRBASE:FindByName(self.airbasename) + self.GoZoneSet = SET_ZONE:New() + self.NoGoZoneSet = SET_ZONE:New() + self.resurrection = 900 + self.casspeed = 250 + self.casalt = 10000 + self.casdir = 90 + self.casleg = 15 + self.casgrouping = 2 + self.missionrange = 100 + self.noaltert5 = 2 + self.MaxAliveMissions = 8 + self.engagerange = 50 + self.repeatsonfailure = 3 + self.Monitor = false + self.TankerInvisible = true + self.CasFormation = ENUMS.Formation.FixedWing.BomberElement.Group + + -- Set some string id for output to DCS.log file. + self.lid=string.format("EASYA2G %s | ", self.alias) + + -- Add FSM transitions. + -- From State --> Event --> To State + self:SetStartState("Stopped") + self:AddTransition("Stopped", "Start", "Running") + self:AddTransition("Running", "Stop", "Stopped") + self:AddTransition("*", "Status", "*") + + self:AddAirwing(self.airbasename,self.alias,self.CapZoneName) + + self:I(self.lid.."Created new instance (v"..self.version..")") + + self:__Start(math.random(6,12)) + + return self +end + +------------------------------------------------------------------------- +-- Functions +------------------------------------------------------------------------- + +--- Set CAP formation. +-- @param #EASYA2G self +-- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group +-- @return #EASYA2G self +function EASYA2G:SetA2GFormation(Formation) + self.CasFormation = Formation + return self +end + +--- Set Tanker and AWACS to be invisible to enemy AI eyes +-- @param #EASYA2G self +-- @param #boolean Switch Set to true or false, by default this is set to true already +-- @return #EASYA2G self +function EASYA2G:SetTankerAndAWACSInvisible(Switch) + self:T(self.lid.."SetTankerAndAWACSInvisible") + self.TankerInvisible = Switch + return self +end + +--- Set Maximum of alive missions to stop airplanes spamming the map +-- @param #EASYA2G self +-- @param #number Maxiumum Maxmimum number of parallel missions allowed. Count is Cap-Missions + Intercept-Missions + Alert5-Missionsm default is 6 +-- @return #EASYA2G self +function EASYA2G:SetMaxAliveMissions(Maxiumum) + self:T(self.lid.."SetDefaultResurrection") + self.MaxAliveMissions = Maxiumum or 8 + return self +end + +--- Add default time to resurrect Airwing building if destroyed +-- @param #EASYA2G self +-- @param #number Seconds Seconds, defaults to 900 +-- @return #EASYA2G self +function EASYA2G:SetDefaultResurrection(Seconds) + self:T(self.lid.."SetDefaultResurrection") + self.resurrection = Seconds or 900 + return self +end + +--- Add default repeat attempts if an Intruder intercepts fails. +-- @param #EASYA2G self +-- @param #number Retries Retries, defaults to 3 +-- @return #EASYA2G self +function EASYA2G:SetDefaultRepeatOnFailure(Retries) + self:T(self.lid.."SetDefaultRepeatOnFailure") + self.repeatsonfailure = Retries or 3 + return self +end + +--- Set default A2G Speed in knots +-- @param #EASYA2G self +-- @param #number Speed Speed defaults to 250 +-- @return #EASYA2G self +function EASYA2G:SetDefaultA2GSpeed(Speed) + self:T(self.lid.."SetDefaultSpeed") + self.casspeed = Speed or 250 + return self +end + +--- Set default A2G Altitude in feet +-- @param #EASYA2G self +-- @param #number Altitude Altitude defaults to 25000 +-- @return #EASYA2G self +function EASYA2G:SetDefaultA2GAlt(Altitude) + self:T(self.lid.."SetDefaultAltitude") + self.casalt = Altitude or 25000 + return self +end + +--- Set default A2G leg initial direction in degrees +-- @param #EASYA2G self +-- @param #number Direction Direction defaults to 90 (East) +-- @return #EASYA2G self +function EASYA2G:SetDefaultA2GDirection(Direction) + self:T(self.lid.."SetDefaultDirection") + self.casdir = Direction or 90 + return self +end + +--- Set default leg length in NM +-- @param #EASYA2G self +-- @param #number Leg Leg defaults to 15 +-- @return #EASYA2G self +function EASYA2G:SetDefaultA2GLeg(Leg) + self:T(self.lid.."SetDefaultLeg") + self.casleg = Leg or 15 + return self +end + +--- Set default grouping, i.e. how many airplanes per A2G point +-- @param #EASYA2G self +-- @param #number Grouping Grouping defaults to 2 +-- @return #EASYA2G self +function EASYA2G:SetDefaultA2GGrouping(Grouping) + self:T(self.lid.."SetDefaultA2GGrouping") + self.casgrouping = Grouping or 2 + return self +end + +--- Set default range planes can fly from their homebase in NM +-- @param #EASYA2G self +-- @param #number Range Range defaults to 100 NM +-- @return #EASYA2G self +function EASYA2G:SetDefaultMissionRange(Range) + self:T(self.lid.."SetDefaultMissionRange") + self.missionrange = Range or 100 + return self +end + +--- Set default number of airframes standing by for intercept tasks (visible on the airfield) +-- @param #EASYA2G self +-- @param #number Airframes defaults to 2 +-- @return #EASYA2G selfAirframes +function EASYA2G:SetDefaultNumberAlter5Standby(Airframes) + self:T(self.lid.."SetDefaultNumberAlter5Standby") + self.noaltert5 = math.abs(Airframes) or 2 + return self +end + +--- Set default engage range for intruders detected by A2G flights in NM. +-- @param #EASYA2G self +-- @param #number Range defaults to 50NM +-- @return #EASYA2G selfAirframes +function EASYA2G:SetDefaultEngageRange(Range) + self:T(self.lid.."SetDefaultNumberAlter5Standby") + self.engagerange = Range or 50 + return self +end + +--- Add an AirWing to the manager +-- @param #EASYA2G self +-- @param #string Airbasename +-- @param #string Alias +-- @return #EASYA2G self +function EASYA2G:AddAirwing(Airbasename, Alias) + self:T(self.lid.."AddAirwing "..Airbasename) + + -- Create Airwing data entry + local AWEntry = {} -- #EASYA2G.Wing + AWEntry.AirbaseName = Airbasename + AWEntry.Alias = Alias + --AWEntry.CapZoneName = CapZoneName + + self.ManagedAW[Airbasename] = AWEntry + + return self +end + +--- (Internal) Create actual AirWings from the list +-- @param #EASYA2G self +-- @return #EASYA2G self +function EASYA2G:_CreateAirwings() + self:T(self.lid.."_CreateAirwings") + for airbase,data in pairs(self.ManagedAW) do + local wing = data -- #EASYA2G.Wing + local afb = wing.AirbaseName + local alias = wing.Alias + --local cz = wing.CapZoneName + self:_AddAirwing(airbase,alias) + end + return self +end + +--- (internal) Create and add another AirWing to the manager +-- @param #EASYA2G self +-- @param #string Airbasename +-- @param #string Alias +-- @return #EASYA2G self +function EASYA2G:_AddAirwing(Airbasename, Alias) + self:T(self.lid.."_AddAirwing "..Airbasename) + + local CasFormation = self.CasFormation + + -- Create Airwing + local CAP_Wing = AIRWING:New(Airbasename,Alias) + CAP_Wing:SetVerbosityLevel(0) + CAP_Wing:SetReportOff() + CAP_Wing:SetMarker(true) + CAP_Wing:SetAirbase(AIRBASE:FindByName(Airbasename)) + CAP_Wing:SetRespawnAfterDestroyed() + CAP_Wing:SetNumberCAS(self.casgrouping) + --CAP_Wing:SetCapCloseRaceTrack(true) + if CasFormation then + CAP_Wing:SetCASFormation(CasFormation) + end + if #self.ManagedTK > 0 then + CAP_Wing:SetNumberTankerBoom(1) + CAP_Wing:SetNumberTankerProbe(1) + end + if #self.ManagedEWR > 0 then + CAP_Wing:SetNumberAWACS(1) + end + if #self.ManagedREC > 0 then + CAP_Wing:SetNumberRecon(1) + end + --local PatrolCoordinateKutaisi = ZONE:New(CapZoneName):GetCoordinate() + --CAP_Wing:AddPatrolPointCAP(PatrolCoordinateKutaisi,self.casalt,UTILS.KnotsToAltKIAS(self.casspeed,self.casalt),self.casdir,self.casleg) + CAP_Wing:SetTakeoffHot() + CAP_Wing:SetLowFuelThreshold(0.3) + CAP_Wing.RandomAssetScore = math.random(50,100) + CAP_Wing:Start() + + local Intel = self.Intel + + local TankerInvisible = self.TankerInvisible + + function CAP_Wing:OnAfterFlightOnMission(From, Event, To, Flightgroup, Mission) + local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP + --flightgroup:SetDespawnAfterLanding() + flightgroup:SetDespawnAfterHolding() + flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename)) + flightgroup:GetGroup():CommandEPLRS(true,5) + if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS and Mission.type ~= AUFTRAG.Type.RECON then + flightgroup:SetDetection(true) + flightgroup:SetEngageDetectedOn(self.engagerange,nil,self.GoZoneSet,self.NoGoZoneSet) + flightgroup:SetOutOfAGMRTB(switch) + if CasFormation then + flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CasFormation) + end + end + if Mission.type == AUFTRAG.Type.TANKER or Mission.type == AUFTRAG.Type.AWACS or Mission.type == AUFTRAG.Type.RECON then + if TankerInvisible then + flightgroup:GetGroup():SetCommandInvisible(true) + end + if Mission.type == AUFTRAG.Type.RECON then + flightgroup:SetDetection(true) + end + end + flightgroup:GetGroup():OptionROTEvadeFire() + flightgroup:SetFuelLowRTB(true) + Intel:AddAgent(flightgroup) + function flightgroup:OnAfterHolding(From,Event,To) + self:ClearToLand(5) + end + + end + + if self.noaltert5 > 0 then + local alert = AUFTRAG:NewALERT5(AUFTRAG.Type.CASENHANCED) + alert:SetRequiredAssets(self.noaltert5) + alert:SetRepeat(99) + CAP_Wing:AddMission(alert) + end + + self.wings[Airbasename] = { CAP_Wing, AIRBASE:FindByName(Airbasename):GetZone(), Airbasename } + + return self +end + +--- Add a A2G patrol point to a Wing +-- @param #EASYA2G self +-- @param #string AirbaseName Name of the Wing's airbase +-- @param Core.Point#COORDINATE Coordinate. +-- @param #number Altitude Defaults to 25000 feet ASL. +-- @param #number Speed Defaults to 300 knots TAS. +-- @param #number Heading Defaults to 90 degrees (East). +-- @param #number LegLength Defaults to 15 NM. +-- @return #EASYA2G self +function EASYA2G:AddPatrolPointA2G(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) + self:T(self.lid.."AddPatrolPointA2G "..Coordinate:ToStringLLDDM()) + local EntryCAP = {} -- #EASYA2G.CasPoint + EntryCAP.AirbaseName = AirbaseName + EntryCAP.Coordinate = Coordinate + EntryCAP.Altitude = Altitude or 25000 + EntryCAP.Speed = Speed or 300 + EntryCAP.Heading = Heading or 90 + EntryCAP.LegLength = LegLength or 15 + self.ManagedCP[#self.ManagedCP+1] = EntryCAP + if self.debug then + local mark = MARKER:New(Coordinate,self.lid.."Patrol Point"):ToAll() + end + return self +end + +--- Add a RECON patrol point to a Wing +-- @param #EASYA2G self +-- @param #string AirbaseName Name of the Wing's airbase +-- @param Core.Point#COORDINATE Coordinate. +-- @param #number Altitude Defaults to 25000 feet. +-- @param #number Speed Defaults to 300 knots. +-- @param #number Heading Defaults to 90 degrees (East). +-- @param #number LegLength Defaults to 15 NM. +-- @return #EASYA2G self +function EASYA2G:AddPatrolPointRecon(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) + self:T(self.lid.."AddPatrolPointRecon "..Coordinate:ToStringLLDDM()) + local EntryCAP = {} -- #EASYA2G.CasPoint + EntryCAP.AirbaseName = AirbaseName + EntryCAP.Coordinate = Coordinate + EntryCAP.Altitude = Altitude or 25000 + EntryCAP.Speed = Speed or 300 + EntryCAP.Heading = Heading or 90 + EntryCAP.LegLength = LegLength or 15 + self.ManagedREC[#self.ManagedREC+1] = EntryCAP + if self.debug then + local mark = MARKER:New(Coordinate,self.lid.."Patrol Point Recon"):ToAll() + end + return self +end + +--- Add a TANKER patrol point to a Wing +-- @param #EASYA2G self +-- @param #string AirbaseName Name of the Wing's airbase +-- @param Core.Point#COORDINATE Coordinate. +-- @param #number Altitude Defaults to 25000 feet. +-- @param #number Speed Defaults to 300 knots. +-- @param #number Heading Defaults to 90 degrees (East). +-- @param #number LegLength Defaults to 15 NM. +-- @return #EASYA2G self +function EASYA2G:AddPatrolPointTanker(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) + self:T(self.lid.."AddPatrolPointTanker "..Coordinate:ToStringLLDDM()) + local EntryCAP = {} -- #EASYA2G.CasPoint + EntryCAP.AirbaseName = AirbaseName + EntryCAP.Coordinate = Coordinate + EntryCAP.Altitude = Altitude or 25000 + EntryCAP.Speed = Speed or 300 + EntryCAP.Heading = Heading or 90 + EntryCAP.LegLength = LegLength or 15 + self.ManagedTK[#self.ManagedTK+1] = EntryCAP + if self.debug then + local mark = MARKER:New(Coordinate,self.lid.."Patrol Point Tanker"):ToAll() + end + return self +end + +--- Add an AWACS patrol point to a Wing +-- @param #EASYA2G self +-- @param #string AirbaseName Name of the Wing's airbase +-- @param Core.Point#COORDINATE Coordinate. +-- @param #number Altitude Defaults to 25000 feet. +-- @param #number Speed Defaults to 300 knots. +-- @param #number Heading Defaults to 90 degrees (East). +-- @param #number LegLength Defaults to 15 NM. +-- @return #EASYA2G self +function EASYA2G:AddPatrolPointAwacs(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength) + self:T(self.lid.."AddPatrolPointAwacs "..Coordinate:ToStringLLDDM()) + local EntryCAP = {} -- #EASYA2G.CasPoint + EntryCAP.AirbaseName = AirbaseName + EntryCAP.Coordinate = Coordinate + EntryCAP.Altitude = Altitude or 25000 + EntryCAP.Speed = Speed or 300 + EntryCAP.Heading = Heading or 90 + EntryCAP.LegLength = LegLength or 15 + self.ManagedEWR[#self.ManagedEWR+1] = EntryCAP + if self.debug then + local mark = MARKER:New(Coordinate,self.lid.."Patrol Point AWACS"):ToAll() + end + return self +end + +--- (Internal) Set actual Tanker Points from the list +-- @param #EASYA2G self +-- @return #EASYA2G self +function EASYA2G:_SetTankerPatrolPoints() + self:T(self.lid.."_SetTankerPatrolPoints") + for _,_data in pairs(self.ManagedTK) do + local data = _data --#EASYA2G.CasPoint + local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING + local Coordinate = data.Coordinate + local Altitude = data.Altitude + local Speed = data.Speed + local Heading = data.Heading + local LegLength = data.LegLength + Wing:AddPatrolPointTANKER(Coordinate,Altitude,Speed,Heading,LegLength) + end + + return self +end + +--- (Internal) Set actual Awacs Points from the list +-- @param #EASYA2G self +-- @return #EASYA2G self +function EASYA2G:_SetAwacsPatrolPoints() + self:T(self.lid.."_SetAwacsPatrolPoints") + for _,_data in pairs(self.ManagedEWR) do + local data = _data --#EASYA2G.CasPoint + local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING + local Coordinate = data.Coordinate + local Altitude = data.Altitude + local Speed = data.Speed + local Heading = data.Heading + local LegLength = data.LegLength + Wing:AddPatrolPointAWACS(Coordinate,Altitude,Speed,Heading,LegLength) + end + + return self +end + +--- (Internal) Set actual PatrolPoints from the list +-- @param #EASYA2G self +-- @return #EASYA2G self +function EASYA2G:_SetA2GPatrolPoints() + self:T(self.lid.."_SetA2GPatrolPoints") + for _,_data in pairs(self.ManagedCP) do + local data = _data --#EASYA2G.CasPoint + local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING + local Coordinate = data.Coordinate + local Altitude = data.Altitude + local Speed = data.Speed + local Heading = data.Heading + local LegLength = data.LegLength + local caszone = ZONE_RADIUS:New("EasyA2G-"..math.random(1,100000),Coordinate:GetVec2(),5000,false) + Wing:AddPatrolPointCAS(caszone,Altitude,Speed,100,self.NoGoZoneSet) + end + + return self +end + +--- (Internal) Set actual PatrolPoints from the list +-- @param #EASYA2G self +-- @return #EASYA2G self +function EASYA2G:_SetReconPatrolPoints() + self:T(self.lid.."_SetReconPatrolPoints") + for _,_data in pairs(self.ManagedREC) do + local data = _data --#EASYA2G.CasPoint + local Wing = self.wings[data.AirbaseName][1] -- Ops.AirWing#AIRWING + local Coordinate = data.Coordinate + local Altitude = data.Altitude + local Speed = data.Speed + local Heading = data.Heading + local LegLength = data.LegLength + Wing:AddPatrolPointRecon(Coordinate,Altitude,Speed,Heading,LegLength) + end + + return self +end + +--- (Internal) Create actual Squadrons from the list +-- @param #EASYA2G self +-- @return #EASYA2G self +function EASYA2G:_CreateSquads() + self:T(self.lid.."_CreateSquads") + for name,data in pairs(self.ManagedSQ) do + local squad = data -- #EASYA2G.Squad + local SquadName = name + local TemplateName = squad.TemplateName + local AirbaseName = squad.AirbaseName + local AirFrames = squad.AirFrames + local Skill = squad.Skill + local Modex = squad.Modex + local Livery = squad.Livery + local Frequency = squad.Frequency + local Modulation = squad.Modulation + local TACAN = squad.TACAN + if squad.Tanker then + self:_AddTankerSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation,TACAN) + elseif squad.AWACS then + self:_AddAWACSSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation) + elseif squad.RECON then + self:_AddReconSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery) + elseif squad.BOMBING then + self:_AddSquadronBOMBING(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation) + elseif squad.SEAD then + self:_AddSquadronSEAD(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation) + elseif squad.ANTISHIP then + self:_AddSquadronANTISHIP(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation) + else + self:_AddSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery) + end + end + return self +end + +--- Add a CAS Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYA2G self +function EASYA2G:AddSquadronCAS(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:T(self.lid.."AddSquadronCAS "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYA2G.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 402 + EntrySQ.Livery = Livery + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + +--- Add a SEAD Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYA2G self +function EASYA2G:AddSquadronSEAD(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:T(self.lid.."AddSquadronSEAD "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYA2G.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 402 + EntrySQ.Livery = Livery + EntrySQ.SEAD= true + self.CanSEAD = true + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + +--- Add an ANTISHIP Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYA2G self +function EASYA2G:AddSquadronANTISHIP(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:T(self.lid.."AddSquadronANTISHIP "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYA2G.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 402 + EntrySQ.Livery = Livery + EntrySQ.ANTISHIP = true + self.CanAntiShip = true + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + +--- Add a BOMBING Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYA2G self +function EASYA2G:AddSquadronBOMBING(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:T(self.lid.."AddSquadronBOMBING "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYA2G.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 402 + EntrySQ.Livery = Livery + EntrySQ.BOMBING = true + self.CanBombing = true + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + +--- Add a Recon Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYA2G self +function EASYA2G:AddReconSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:T(self.lid.."AddReconSquadron "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYA2G.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 402 + EntrySQ.Livery = Livery + EntrySQ.RECON = true + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + +--- Add a Tanker Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio Frequency to be used. +-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM +-- @param #number TACAN (optional) TACAN channel, e.g. 71, resulting in Channel 71Y +-- @return #EASYA2G self +function EASYA2G:AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation, TACAN) + self:T(self.lid.."AddTankerSquadron "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYA2G.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 602 + EntrySQ.Livery = Livery + EntrySQ.Frequency = Frequency + EntrySQ.Modulation = Livery + EntrySQ.TACAN = TACAN + EntrySQ.Tanker = true + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + +--- Add an AWACS Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio Frequency to be used. +-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM +-- @return #EASYA2G self +function EASYA2G:AddAWACSSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation) + self:T(self.lid.."AddAWACSSquadron "..SquadName) + -- Add Squadron Data + local EntrySQ = {} -- #EASYA2G.Squad + EntrySQ.TemplateName = TemplateName + EntrySQ.SquadName = SquadName + EntrySQ.AirbaseName = AirbaseName + EntrySQ.AirFrames = AirFrames or 20 + EntrySQ.Skill = Skill or AI.Skill.AVERAGE + EntrySQ.Modex = Modex or 702 + EntrySQ.Livery = Livery + EntrySQ.Frequency = Frequency + EntrySQ.Modulation = Livery + EntrySQ.AWACS = true + + self.ManagedSQ[SquadName] = EntrySQ + + return self +end + +--- (Internal) Add a Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio Frequency to be used. +-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM +-- @return #EASYA2G self +function EASYA2G:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation) + self:T(self.lid.."_AddSquadron "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.CASENHANCED, AUFTRAG.Type.BAI, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CASENHANCED, AUFTRAG.Type.BAI, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5},75) + + return self +end + +--- (Internal) Add a SEAD Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio Frequency to be used. +-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM +-- @return #EASYA2G self +function EASYA2G:_AddSquadronSEAD(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation) + self:T(self.lid.."_AddSquadronSEAD "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.SEAD, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.SEAD, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5},75) + + return self +end + +--- (Internal) Add an ANTISHIP Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio Frequency to be used. +-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM +-- @return #EASYA2G self +function EASYA2G:_AddSquadronANTISHIP(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation) + self:T(self.lid.."_AddSquadronANTISHIP "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.ANTISHIP, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.ANTISHIP, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5},75) + + return self +end + +--- (Internal) Add an BOMBING Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio Frequency to be used. +-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM +-- @return #EASYA2G self +function EASYA2G:_AddSquadronBOMBING(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation) + self:T(self.lid.."_AddSquadronBOMBING "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.BOMBING, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.BOMBING, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5},75) + + return self +end + +--- (Internal) Add a Recon Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @return #EASYA2G self +function EASYA2G:_AddReconSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery) + self:T(self.lid.."_AddReconSquadron "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.RECON}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.RECON},75) + + return self +end + +--- (Internal) Add a Tanker Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio frequency of the Tanker +-- @param #number Modulation (Optional) Radio modulation of the Tanker +-- @param #number TACAN (Optional) TACAN Channel to be used, will always be an "Y" channel +-- @return #EASYA2G self +function EASYA2G:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation, TACAN) + self:T(self.lid.."_AddTankerSquadron "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.TANKER}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + Squadron_One:SetRadio(Frequency,Modulation) + Squadron_One:AddTacanChannel(TACAN,TACAN) + + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.TANKER},75) + + return self +end + +--- (Internal) Add a AWACS Squadron to an Airwing of the manager +-- @param #EASYA2G self +-- @param #string TemplateName Name of the group template. +-- @param #string SquadName Squadron name - must be unique! +-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi +-- @param #number AirFrames Number of available airframes, e.g. 20. +-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE +-- @param #string Modex (optional) Modex to be used,e.g. 402. +-- @param #string Livery (optional) Livery name to be used. +-- @param #number Frequency (optional) Radio frequency of the AWACS +-- @param #number Modulation (Optional) Radio modulation of the AWACS +-- @return #EASYA2G self +function EASYA2G:_AddAWACSSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation) + self:T(self.lid.."_AddAWACSSquadron "..SquadName) + -- Add Squadrons + local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName) + Squadron_One:AddMissionCapability({AUFTRAG.Type.AWACS}) + --Squadron_One:SetFuelLowRefuel(true) + Squadron_One:SetFuelLowThreshold(0.3) + Squadron_One:SetTurnoverTime(10,20) + Squadron_One:SetModex(Modex) + Squadron_One:SetLivery(Livery) + Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE) + Squadron_One:SetMissionRange(self.missionrange) + Squadron_One:SetRadio(Frequency,Modulation) + local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING + + wing:AddSquadron(Squadron_One) + wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.AWACS},75) + + return self +end + +--- Add a zone to the accepted zones set. +-- @param #EASYA2G self +-- @param Core.Zone#ZONE_BASE Zone +-- @return #EASYA2G self +function EASYA2G:AddAcceptZone(Zone) + self:T(self.lid.."AddAcceptZone0") + self.GoZoneSet:AddZone(Zone) + return self +end + +--- Add a zone to the rejected zones set. +-- @param #EASYA2G self +-- @param Core.Zone#ZONE_BASE Zone +-- @return #EASYA2G self +function EASYA2G:AddRejectZone(Zone) + self:T(self.lid.."AddRejectZone") + self.NoGoZoneSet:AddZone(Zone) + return self +end + +--- (Internal) Start detection. +-- @param #EASYA2G self +-- @return #EASYA2G self +function EASYA2G:_StartIntel() + self:T(self.lid.."_StartIntel") + -- Border GCI Detection + local BlueAir_DetectionSetGroup = SET_GROUP:New() + BlueAir_DetectionSetGroup:FilterPrefixes( { self.EWRName } ) + BlueAir_DetectionSetGroup:FilterStart() + + -- Intel type detection + local BlueIntel = INTEL:New(BlueAir_DetectionSetGroup,self.coalitionname, self.EWRName) + BlueIntel:SetClusterAnalysis(false,false,false) + BlueIntel:SetForgetTime(300) + BlueIntel:SetAcceptZones(self.GoZoneSet) + BlueIntel:SetRejectZones(self.NoGoZoneSet) + BlueIntel:SetVerbosity(0) + BlueIntel:SetDetectStatics(true) + BlueIntel:SetFilterCategory({Unit.Category.GROUND_UNIT, Unit.Category.SHIP, Unit.Category.STRUCTURE}) + --BlueIntel:SetDetectionTypes(false,true,true,true,false,true) + BlueIntel:Start() + + if self.debug then + BlueIntel.debug = true + end + + -- Here, we'll decide if we need to launch an A2G flight, and from where + + local overhead = self.overhead + local casspeed = self.casspeed + 100 + local casalt = self.casalt + local maxsize = self.maxinterceptsize + local repeatsonfailure = self.repeatsonfailure + + local wings = self.wings + local ctlpts = self.ManagedCP + local MaxAliveMissions = self.MaxAliveMissions * self.casgrouping + local nogozoneset = self.NoGoZoneSet + + function BlueIntel:OnAfterNewContact(From,Event,To,Contact) + -- Aircraft? + if Contact.ctype == INTEL.Ctype.AIRCRAFT then return end + -- Threatlevel 0..10 + --local contact = self:GetHighestThreatContact(Cluster) + local contact = Contact -- Ops.Intelligence#INTEL.Contact + local name = contact.groupname --#string + local threat = contact.threatlevel --#number + --local position = self:CalcClusterFuturePosition(Cluster,300) + local position = Contact.position + -- calculate closest zone + local bestdistance = 2000*1000 -- 2000km + local targetairwing = nil -- Ops.AirWing#AIRWING + local targetawname = "" -- #string + local clustersize = contact.group.ClassName == "GROUP" and contact.group:CountAliveUnits() or 1 + local wingsize = math.abs(overhead * (clustersize+1)) + if wingsize > maxsize then wingsize = maxsize end + -- existing mission, and if so - done? + local retrymission = true + if Contact.mission and (not Contact.mission:IsOver()) then + retrymission = false + end + if (retrymission) and (wingsize >= 1) then + MESSAGE:New(string.format("**** %s A2G need wingsize %d", UTILS.GetCoalitionName(self.coalition), wingsize),15,"A2G"):ToAllIf(self.debug):ToLog() + for _,_data in pairs (wings) do + local airwing = _data[1] -- Ops.AirWing#AIRWING + local zone = _data[2] -- Core.Zone#ZONE + local zonecoord = zone:GetCoordinate() + local name = _data[3] -- #string + local distance = position:DistanceFromPointVec2(zonecoord) + local airframes = airwing:CountAssets(true) + if distance < bestdistance and airframes >= wingsize then + bestdistance = distance + targetairwing = airwing + targetawname = name + end + end + for _,_data in pairs (ctlpts) do + --local airwing = _data[1] -- Ops.AirWing#AIRWING + --local zone = _data[2] -- Core.Zone#ZONE + --local zonecoord = zone:GetCoordinate() + --local name = _data[3] -- #string + + local data = _data -- #EASYA2G.CasPoint + local name = data.AirbaseName + local zonecoord = data.Coordinate + local airwing = wings[name][1] + + local distance = position:DistanceFromPointVec2(zonecoord) + local airframes = airwing:CountAssets(true) + if distance < bestdistance and airframes >= wingsize then + bestdistance = distance + targetairwing = airwing -- Ops.AirWing#AIRWING + targetawname = name + end + end + local text = string.format("Closest Airwing is %s", targetawname) + local m = MESSAGE:New(text,10,"A2G"):ToAllIf(self.debug):ToLog() + -- Do we have a matching airwing? + if targetairwing then + local AssetCount = targetairwing:CountAssetsOnMission(MissionTypes,Cohort) + -- Enough airframes on mission already? + self:T(self.lid.." Assets on Mission "..AssetCount) + if AssetCount <= MaxAliveMissions then + local repeats = repeatsonfailure + local caszone = ZONE_RADIUS:New("EasyA2G-"..math.random(1,100000),contact.position:GetVec2(),5000,false) + --local InterceptAuftrag = AUFTRAG:NewCASENHANCED(caszone,casalt,casspeed,100,nogozoneset) + local prio = 30 + local urgent = false + local InterceptAuftrag = AUFTRAG:NewBAI(contact.group,casalt) + if contact.isStatic and self.CanBombing then + InterceptAuftrag = AUFTRAG:NewBOMBING(contact.group,casalt) + prio = 50 + elseif contact.isship and self.CanAntiShip then + InterceptAuftrag = AUFTRAG:NewANTISHIP(contact.group,casalt) + urgent = true + elseif self.CanSEAD and (contact.group:HasAttribute("RADAR_BAND1_FOR_ARM") or contact.group:HasAttribute("RADAR_BAND2_FOR_ARM") or contact.group:HasAttribute("Optical Tracker")) then + InterceptAuftrag = AUFTRAG:NewSEAD(contact.group,casalt) + prio = 1 + urgent = true + end + --local InterceptAuftrag = AUFTRAG:NewBAI(contact,casalt) + InterceptAuftrag:SetMissionRange(100) + InterceptAuftrag:SetPriority(prio,urgent) + InterceptAuftrag:SetRequiredAssets(wingsize) + InterceptAuftrag:SetRepeatOnFailure(repeats) + InterceptAuftrag:SetMissionSpeed(UTILS.KnotsToAltKIAS(casspeed,casalt)) + InterceptAuftrag:SetMissionAltitude(casalt) + + if nogozoneset:Count() > 0 then + InterceptAuftrag:AddConditionSuccess( + function(group,zoneset) + local success = false + if group and group:IsAlive() then + local coord = group:GetCoordinate() + if coord and zoneset:IsCoordinateInZone(coord) then + success = true + end + end + return success + end, + contact.group, + nogozoneset + ) + end + + targetairwing:AddMission(InterceptAuftrag) + Contact.mission = InterceptAuftrag + end + else + MESSAGE:New("**** Not enough airframes available or max mission limit reached!",15,"A2G"):ToAllIf(self.debug):ToLog() + end + end + end +self.Intel = BlueIntel +return self +end + +------------------------------------------------------------------------- +-- FSM Functions +------------------------------------------------------------------------- + +--- (Internal) FSM Function onafterStart +-- @param #EASYA2G self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #EASYA2G self +function EASYA2G:onafterStart(From,Event,To) + self:T({From,Event,To}) + self:_StartIntel() + self:_CreateAirwings() + self:_CreateSquads() + self:_SetA2GPatrolPoints() + self:_SetTankerPatrolPoints() + self:_SetAwacsPatrolPoints() + self:_SetReconPatrolPoints() + self:__Status(-10) + return self +end + +--- (Internal) FSM Function onbeforeStatus +-- @param #EASYA2G self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #EASYA2G self +function EASYA2G:onbeforeStatus(From,Event,To) + self:T({From,Event,To}) + if self:GetState() == "Stopped" then return false end + return self +end + +--- (Internal) FSM Function onafterStatus +-- @param #EASYA2G self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #EASYA2G self +function EASYA2G:onafterStatus(From,Event,To) + self:T({From,Event,To}) + -- Gather Some Stats + local function counttable(tbl) + local count = 0 + for _,_data in pairs(tbl) do + count = count + 1 + end + return count + end + local wings = counttable(self.ManagedAW) + local squads = counttable(self.ManagedSQ) + local caps = counttable(self.ManagedCP) + local assets = 0 + local instock = 0 + local capmission = 0 + --local interceptmission = 0 + local reconmission = 0 + --local awacsmission = 0 + --local tankermission = 0 + local seadmission = 0 + local bombmission = 0 + local shipmission = 0 + for _,_wing in pairs(self.wings) do + local count = _wing[1]:CountAssetsOnMission(MissionTypes,Cohort) + local count2 = _wing[1]:CountAssets(true,MissionTypes,Attributes) + capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.CASENHANCED,AUFTRAG.Type.BAI}) + --interceptmission = interceptmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.INTERCEPT}) + reconmission = reconmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.RECON}) + --awacsmission = awacsmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.AWACS}) + --tankermission = tankermission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.TANKER}) + seadmission = seadmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.SEAD}) + bombmission = bombmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.BOMBING}) + shipmission = shipmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.ANTISHIP}) + assets = assets + count + instock = instock + count2 + end + if self.Monitor then + local threatcount = #self.Intel.Contacts or 0 + local text = "GCICAP "..self.alias + text = text.."\nWings: "..wings.."\nSquads: "..squads.."\nCasPoints: "..caps.."\nAssets on Mission: "..assets.."\nAssets in Stock: "..instock + text = text.."\nThreats: "..threatcount + text = text.."\nMissions: "..capmission+reconmission+seadmission+bombmission+shipmission + --text = text.."\n - Intercept: "..interceptmission + --text = text.."\n - AWACS: "..awacsmission + --text = text.."\n - TANKER: "..tankermission + text = text.."\n - Recon: "..reconmission + text = text.."\n - CAS/BAI: "..capmission + if self.CanSEAD then + text = text.."\n - SEAD: "..seadmission + end + if self.CanBombing then + text = text.."\n - Bombing: "..bombmission + end + if self.CanAntiShip then + text = text.."\n - Anti-Ship: "..shipmission + end + MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug) + end + self:__Status(30) + return self +end + +--- (Internal) FSM Function onafterStop +-- @param #EASYA2G self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #EASYA2G self +function EASYA2G:onafterStop(From,Event,To) + self:T({From,Event,To}) + self.Intel:Stop() + return self +end diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index 1dbaee076..ed1f575ab 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -1080,7 +1080,7 @@ function INTEL:CreateDetectedItems(DetectedGroups, DetectedStatics, RecceDetecti end --- (Internal) Return the detected target groups of the controllable as a @{SET_GROUP}. --- The optional parametes specify the detection methods that can be applied. +-- The optional parameters specify the detection methods that can be applied. -- If no detection method is given, the detection will use all the available methods by default. -- @param #INTEL self -- @param Wrapper.Unit#UNIT Unit The unit detecting. @@ -1093,11 +1093,14 @@ end -- @param #boolean DetectRWR (Optional) If *false*, do not include targets detected by RWR. -- @param #boolean DetectDLINK (Optional) If *false*, do not include targets detected by data link. function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) - + --self:T(self.lid.."GetDetectedUnits "..Unit:GetName()) -- Get detected DCS units. local reccename = Unit:GetName() + local detectedtargets=Unit:GetDetectedTargets(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) - + + --UTILS.PrintTableToLog(detectedtargets,1) + for DetectionObjectID, Detection in pairs(detectedtargets or {}) do local DetectedObject=Detection.object -- DCS#Object diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index e013df17b..b7ac2150b 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2937,7 +2937,7 @@ function CONTROLLABLE:CopyRoute( Begin, End, Randomize, Radius ) end --- Return the detected targets of the controllable. --- The optional parametes specify the detection methods that can be applied. +-- The optional parameters specify the detection methods that can be applied. -- If no detection method is given, the detection will use all the available methods by default. -- @param #CONTROLLABLE self -- @param #boolean DetectVisual (optional) From e8c7e62900956db56a317b9f995f09c572fd1852 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 11:20:20 +0100 Subject: [PATCH 435/603] xx --- Moose Development/Moose/Core/Set.lua | 41 ++++++----- Moose Development/Moose/Ops/PlayerRecce.lua | 79 +++++++++++++-------- 2 files changed, 73 insertions(+), 47 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index fec517906..c9cbc9f3a 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -419,7 +419,11 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetRandom() - local tablemax = table.maxn(self.Index) + local tablemax = 0 + for _,_ind in pairs(self.Index) do + tablemax = tablemax + 1 + end + --local tablemax = table.maxn(self.Index) local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] self:T3( { RandomItem } ) return RandomItem @@ -561,10 +565,12 @@ do -- SET_BASE return self end - --- Iterate the SET_BASE while identifying the nearest object from a @{Core.Point#POINT_VEC2}. + --- Iterate the SET_BASE while identifying the nearest object in the set from a @{Core.Point#POINT_VEC2}. -- @param #SET_BASE self - -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set. + -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#COORDINATE} or @{Core.Point#POINT_VEC2} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set. -- @return Core.Base#BASE The closest object. + -- @usage + -- myset:FindNearestObjectFromPointVec2( ZONE:New("Test Zone"):GetCoordinate() ) function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ) self:F2( PointVec2 ) @@ -2849,15 +2855,14 @@ do -- SET_UNIT function SET_UNIT:GetCoordinate() local Coordinate = nil - local unit = self:GetRandom() + local unit = self:GetFirst() if self:Count() == 1 and unit then return unit:GetCoordinate() end if unit then - local Coordinate = unit:GetCoordinate() - --self:F({Coordinate:GetVec3()}) - - + Coordinate = unit:GetCoordinate() + self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) + local x1 = Coordinate.x local x2 = Coordinate.x local y1 = Coordinate.y @@ -2868,19 +2873,19 @@ do -- SET_UNIT local AvgHeading = nil local MovingCount = 0 - for UnitName, UnitData in pairs( self:GetAliveSet() ) do + for UnitName, UnitData in pairs( self.Set) do local Unit = UnitData -- Wrapper.Unit#UNIT - local Coordinate = Unit:GetCoordinate() + local Coord = Unit:GetCoordinate() - x1 = (Coordinate.x < x1) and Coordinate.x or x1 - x2 = (Coordinate.x > x2) and Coordinate.x or x2 - y1 = (Coordinate.y < y1) and Coordinate.y or y1 - y2 = (Coordinate.y > y2) and Coordinate.y or y2 - z1 = (Coordinate.y < z1) and Coordinate.z or z1 - z2 = (Coordinate.y > z2) and Coordinate.z or z2 + x1 = (Coord.x < x1) and Coord.x or x1 + x2 = (Coord.x > x2) and Coord.x or x2 + y1 = (Coord.y < y1) and Coord.y or y1 + y2 = (Coord.y > y2) and Coord.y or y2 + z1 = (Coord.y < z1) and Coord.z or z1 + z2 = (Coord.y > z2) and Coord.z or z2 - local Velocity = Coordinate:GetVelocity() + local Velocity = Coord:GetVelocity() if Velocity ~= 0 then MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity local Heading = Coordinate:GetHeading() @@ -2897,7 +2902,7 @@ do -- SET_UNIT Coordinate:SetHeading( AvgHeading ) Coordinate:SetVelocity( MaxVelocity ) - self:F( { Coordinate = Coordinate } ) + self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) end return Coordinate diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index ec3a57954..9c1cb9eb6 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -79,6 +79,7 @@ -- @field Utilities.FiFo#FIFO TargetCache -- @field #boolean smokeownposition -- @field #table SmokeOwn +-- @field #boolean smokeaveragetargetpos -- @extends Core.Fsm#FSM --- @@ -104,7 +105,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.22", + version = "0.1.23", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, @@ -130,8 +131,9 @@ PLAYERRECCE = { ReferencePoint = nil, TForget = 600, TargetCache = nil, - smokeownposition = true, + smokeownposition = false, SmokeOwn = {}, + smokeaveragetargetpos = false, } --- @@ -1109,9 +1111,8 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) self:T(self.lid.."_SmokeTargets") local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT - cameraset:AddSet(visualset) - if cameraset:CountAlive() > 0 then + if cameraset:CountAlive() > 0 or visualset:CountAlive() > 0 then self:__TargetsSmoked(-1,client,playername,cameraset) else return self @@ -1126,29 +1127,31 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) -- laser targer gets extra smoke if laser and laser.Target and laser.Target:IsAlive() then laser.Target:GetCoordinate():Smoke(lasersmoke) - if cameraset:IsInSet(laser.Target) then - cameraset:Remove(laser.Target:GetName(),true) + end + + local coord = visualset:GetCoordinate() + if coord and self.smokeaveragetargetpos then + coord:SetAtLandheight() + coord:Smoke(medsmoke) + else + -- smoke everything + for _,_unit in pairs(visualset.Set) do + local unit = _unit --Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + local coord = unit:GetCoordinate() + local threat = unit:GetThreatLevel() + if coord then + local color = lowsmoke + if threat > 7 then + color = highsmoke + elseif threat > 2 then + color = medsmoke + end + coord:Smoke(color) + end + end end end - - local coordinate = nil - local setthreat = 0 - -- smoke everything else - if cameraset:CountAlive() > 1 then - local coordinate = cameraset:GetCoordinate() - local setthreat = cameraset:CalculateThreatLevelA2G() - end - - if coordinate then - local color = lowsmoke - if setthreat > 7 then - color = medsmoke - elseif setthreat > 2 then - color = lowsmoke - end - coordinate:Smoke(color) - end - if self.SmokeOwn[playername] then local cc = client:GetVec2() -- don't smoke mid-air @@ -1189,15 +1192,15 @@ function PLAYERRECCE:_FlareTargets(client,group,playername) -- smoke everything else for _,_unit in pairs(cameraset.Set) do local unit = _unit --Wrapper.Unit#UNIT - if unit then + if unit and unit:IsAlive() then local coord = unit:GetCoordinate() local threat = unit:GetThreatLevel() if coord then local color = lowsmoke if threat > 7 then - color = medsmoke + color = highsmoke elseif threat > 2 then - color = lowsmoke + color = medsmoke end coord:Flare(color) end @@ -1546,7 +1549,7 @@ end -- @param #PLAYERRECCE self -- @return #PLAYERRECCE self function PLAYERRECCE:EnableSmokeOwnPosition() - self:T(self.lid.."ENableSmokeOwnPosition") + self:T(self.lid.."EnableSmokeOwnPosition") self.smokeownposition = true return self end @@ -1560,6 +1563,24 @@ function PLAYERRECCE:DisableSmokeOwnPosition() return self end +--- [User] Enable smoking of average target positions, instead of all targets visible. Loses smoke per threatlevel -- each is med threat. Default is - smoke all positions. +-- @param #PLAYERRECCE self +-- @return #PLAYERRECCE self +function PLAYERRECCE:EnableSmokeAverageTargetPosition() + self:T(self.lid.."ENableSmokeOwnPosition") + self.smokeaveragetargetpos = true + return self +end + +--- [User] Disable smoking of average target positions, instead of all targets visible. Default is - smoke all positions. +-- @param #PLAYERRECCE self +-- @return #PLAYERRECCE +function PLAYERRECCE:DisableSmokeAverageTargetPosition() + self:T(self.lid.."DisableSmokeAverageTargetPosition") + self.smokeaveragetargetpos = false + return self +end + --- [Internal] Get text for text-to-speech. -- Numbers are spaced out, e.g. "Heading 180" becomes "Heading 1 8 0 ". -- @param #PLAYERRECCE self From 26c5f7bf795edd6081b2ca231da84c65c5ecd94d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 12:15:27 +0100 Subject: [PATCH 436/603] xxx --- Moose Development/Moose/Core/Message.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 4f4a40e50..8b6da3cdd 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -368,7 +368,7 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings ) if CoalitionSide then if self.MessageDuration ~= 0 then self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration ) - trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) + trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) end end From 99e7d6ae92dac467a04c55b9b7a621ad26235c2e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 13:31:32 +0100 Subject: [PATCH 437/603] #CTLD * Spawn dropped troops in a nice circle 5m (hover: 1.5m) left of the helo --- Moose Development/Moose/Ops/CTLD.lua | 52 +++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index fac6173bf..31a6cb76c 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1228,7 +1228,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.43" +CTLD.version="1.0.44" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3010,6 +3010,35 @@ function CTLD:IsHercules(Unit) end end + +--- (Internal) Function to set troops positions of a template to a nice circle +-- @param #CTLD self +-- @param Core.Point#COORDINATE Coordinate Start coordinate to use +-- @param #number Radius Radius to be used +-- @param #number Heading Heading starting with +-- @param #string Template The group template name +-- @return #table Positions The positions table +function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template) + local Positions = {} + local template = _DATABASE:GetGroupTemplate(Template) + UTILS.PrintTableToLog(template) + local numbertroops = #template.units + local newcenter = Coordinate:Translate(Radius,((Heading+270)%360)) + for i=1,360,math.floor(360/numbertroops) do + local phead = ((Heading+270+i)%360) + local post = newcenter:Translate(Radius,phead) + local pos1 = post:GetVec2() + local p1t = { + x = pos1.x, + y = pos1.y, + heading = phead, + } + table.insert(Positions,p1t) + end + UTILS.PrintTableToLog(Positions) + return Positions +end + --- (Internal) Function to unload troops from heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group @@ -3061,14 +3090,29 @@ function CTLD:_UnloadTroops(Group, Unit) zoneradius = Unit:GetVelocityMPS() or 100 end local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor) - local randomcoord = zone:GetRandomCoordinate(10,30*factor):GetVec2() + local randomcoord = zone:GetRandomCoordinate(10,30*factor) --:GetVec2() + local heading = Group:GetHeading() or 0 + -- Spawn troops left from us, closer when hovering, further off when landed + if hoverunload or grounded then + randomcoord = Group:GetCoordinate() + -- slightly left from us + local Angle = (heading+270)%360 + local offset = hoverunload and 1.5 or 5 + randomcoord:Translate(offset,Angle,nil,true) + end + local tempcount = 0 for _,_template in pairs(temptable) do self.TroopCounter = self.TroopCounter + 1 + tempcount = tempcount+1 local alias = string.format("%s-%d", _template, math.random(1,100000)) + local rad = 2.5+tempcount + local Positions = self:_GetUnitPositions(randomcoord,rad,heading,_template) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) - :InitRandomizeUnits(true,20,2) + --:InitRandomizeUnits(true,20,2) + --:InitHeading(heading) :InitDelayOff() - :SpawnFromVec2(randomcoord) + :InitSetUnitAbsolutePositions(Positions) + :SpawnFromVec2(randomcoord:GetVec2()) self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type) end -- template loop cargo:SetWasDropped(true) From a1999157311d0d30315d7279dd629e93e37cd6e8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 16:09:57 +0100 Subject: [PATCH 438/603] xxx --- Moose Development/Moose/Core/Zone.lua | 465 ++++++++++---------- Moose Development/Moose/Utilities/Utils.lua | 23 +- 2 files changed, 253 insertions(+), 235 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 98fd0c22f..09ef67c2f 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2001,234 +2001,6 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer ) end ---- ZONE_OVAL created from a center point, major axis, minor axis, and angle. --- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @type ZONE_OVAL --- @extends Core.Zone#ZONE_BASE - ---- ## ZONE_OVAL class, extends @{#ZONE_BASE} --- --- The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle. --- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties. --- --- @field #ZONE_OVAL -ZONE_OVAL = { - ClassName = "OVAL", - ZoneName="", - MajorAxis = nil, - MinorAxis = nil, - Angle = 0, - DrawPoly = nil -- let's just use a ZONE_POLYGON to draw the ZONE_OVAL on the map -} - ---- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle. ---- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @param #table vec2 The center point of the oval --- @param #number major_axis The major axis of the oval --- @param #number minor_axis The minor axis of the oval --- @param #number angle The angle of the oval --- @return #ZONE_OVAL The new oval -function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle) - self = BASE:Inherit(self, ZONE_BASE:New()) - self.ZoneName = name - self.CenterVec2 = vec2 - self.MajorAxis = major_axis - self.MinorAxis = minor_axis - self.Angle = angle or 0 - - _DATABASE:AddZone(name, self) - - return self -end - ---- Constructor to create a ZONE_OVAL instance, taking the name of a drawing made with the draw tool in the Mission Editor. ---- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @param #ZONE_OVAL self --- @param #string DrawingName The name of the drawing in the Mission Editor --- @return #ZONE_OVAL self -function ZONE_OVAL:NewFromDrawing(DrawingName) - self = BASE:Inherit(self, ZONE_BASE:New(DrawingName)) - for _, layer in pairs(env.mission.drawings.layers) do - for _, object in pairs(layer["objects"]) do - if string.find(object["name"], DrawingName, 1, true) then - if object["polygonMode"] == "oval" then - self.CenterVec2 = { x = object["mapX"], y = object["mapY"] } - self.MajorAxis = object["r1"] - self.MinorAxis = object["r2"] - self.Angle = object["angle"] - - end - end - end - end - - _DATABASE:AddZone(DrawingName, self) - - return self -end - ---- Gets the major axis of the oval. --- @param #ZONE_OVAL self --- @return #number The major axis of the oval -function ZONE_OVAL:GetMajorAxis() - return self.MajorAxis -end - ---- Gets the minor axis of the oval. --- @param #ZONE_OVAL self --- @return #number The minor axis of the oval -function ZONE_OVAL:GetMinorAxis() - return self.MinorAxis -end - ---- Gets the angle of the oval. --- @param #ZONE_OVAL self --- @return #number The angle of the oval -function ZONE_OVAL:GetAngle() - return self.Angle -end - ---- Returns a the center point of the oval --- @param #ZONE_OVAL self --- @return #table The center Vec2 -function ZONE_OVAL:GetVec2() - return self.CenterVec2 -end - ---- Checks if a point is contained within the oval. --- @param #ZONE_OVAL self --- @param #table point The point to check --- @return #bool True if the point is contained, false otherwise -function ZONE_OVAL:IsVec2InZone(vec2) - local cos, sin = math.cos, math.sin - local dx = vec2.x - self.CenterVec2.x - local dy = vec2.y - self.CenterVec2.y - local rx = dx * cos(self.Angle) + dy * sin(self.Angle) - local ry = -dx * sin(self.Angle) + dy * cos(self.Angle) - return rx * rx / (self.MajorAxis * self.MajorAxis) + ry * ry / (self.MinorAxis * self.MinorAxis) <= 1 -end - ---- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval. --- @param #ZONE_OVAL self --- @return #table The bounding box of the oval -function ZONE_OVAL:GetBoundingSquare() - local min_x = self.CenterVec2.x - self.MajorAxis - local min_y = self.CenterVec2.y - self.MinorAxis - local max_x = self.CenterVec2.x + self.MajorAxis - local max_y = self.CenterVec2.y + self.MinorAxis - - return { - {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} - } -end - ---- Find points on the edge of the oval --- @param #ZONE_OVAL self --- @param #number num_points How many points should be found. More = smoother shape --- @return #table Points on he edge -function ZONE_OVAL:PointsOnEdge(num_points) - num_points = num_points or 40 - local points = {} - local dtheta = 2 * math.pi / num_points - - for i = 0, num_points - 1 do - local theta = i * dtheta - local x = self.CenterVec2.x + self.MajorAxis * math.cos(theta) * math.cos(self.Angle) - self.MinorAxis * math.sin(theta) * math.sin(self.Angle) - local y = self.CenterVec2.y + self.MajorAxis * math.cos(theta) * math.sin(self.Angle) + self.MinorAxis * math.sin(theta) * math.cos(self.Angle) - table.insert(points, {x = x, y = y}) - end - - return points -end - ---- Returns a random Vec2 within the oval. --- @param #ZONE_OVAL self --- @return #table The random Vec2 -function ZONE_OVAL:GetRandomVec2() - local theta = math.rad(self.Angle) - - local random_point = math.sqrt(math.random()) --> uniformly - --local random_point = math.random() --> more clumped around center - local phi = math.random() * 2 * math.pi - local x_c = random_point * math.cos(phi) - local y_c = random_point * math.sin(phi) - local x_e = x_c * self.MajorAxis - local y_e = y_c * self.MinorAxis - local rx = (x_e * math.cos(theta) - y_e * math.sin(theta)) + self.CenterVec2.x - local ry = (x_e * math.sin(theta) + y_e * math.cos(theta)) + self.CenterVec2.y - - return {x=rx, y=ry} -end - ---- Define a random @{Core.Point#POINT_VEC2} within the zone. --- @param #ZONE_OVAL self --- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -function ZONE_OVAL:GetRandomPointVec2() - return POINT_VEC2:NewFromVec2(self:GetRandomVec2()) -end - ---- Define a random @{Core.Point#POINT_VEC2} within the zone. --- @param #ZONE_OVAL self --- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -function ZONE_OVAL:GetRandomPointVec3() - return POINT_VEC2:NewFromVec3(self:GetRandomVec2()) -end - ---- Draw the zone on the F10 map. ---- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @param #ZONE_OVAL self --- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. --- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. --- @param #number Alpha Transparency [0,1]. Default 1. --- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work --- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work --- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. --- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. --- @return #ZONE_OVAL self -function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) - Coalition = Coalition or self:GetDrawCoalition() - - -- Set draw coalition. - self:SetDrawCoalition(Coalition) - - Color = Color or self:GetColorRGB() - Alpha = Alpha or 1 - - -- Set color. - self:SetColor(Color, Alpha) - - FillColor = FillColor or self:GetFillColorRGB() - if not FillColor then - UTILS.DeepCopy(Color) - end - FillAlpha = FillAlpha or self:GetFillColorAlpha() - if not FillAlpha then - FillAlpha = 0.15 - end - - LineType = LineType or 1 - - -- Set fill color -----------> has fill color worked in recent versions of DCS? - -- doing something like - -- - -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") - -- - -- doesn't seem to fill in the shape for an n-sided polygon - self:SetFillColor(FillColor, FillAlpha) - - self.DrawPoly = ZONE_POLYGON:NewFromPointsArray(self.ZoneName, self:PointsOnEdge(80)) - self.DrawPoly:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) -end - ---- Remove drawing from F10 map --- @param #ZONE_OVAL self -function ZONE_OVAL:UndrawZone() - if self.DrawPoly then - self.DrawPoly:UndrawZone() - end -end - - --- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Triangle.lua --- This triangle "zone" is not really to be used on its own, it only serves as building blocks for --- ZONE_POLYGON to accurately find a point inside a polygon; as well as getting the correct surface area of @@ -3108,9 +2880,13 @@ function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, C return self end +do -- Zone_Polygon + + --- -- @type ZONE_POLYGON -- @extends #ZONE_POLYGON_BASE +-- @extends #ZONE_BASE --- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon, OR by drawings made with the Draw tool @@ -3142,8 +2918,7 @@ end -- -- This class has been updated to use a accurate way of generating random points inside the polygon without having to use trial and error guesses. -- You can also get the surface area of the polygon now, handy if you want measure which coalition has the largest captured area, for example. - - +-- -- @field #ZONE_POLYGON ZONE_POLYGON = { ClassName="ZONE_POLYGON", @@ -3608,6 +3383,7 @@ function ZONE_POLYGON:IsNoneInZone() return self:CountScannedCoalitions() == 0 end +end do -- ZONE_ELASTIC @@ -3810,6 +3586,235 @@ do -- ZONE_ELASTIC end + + +--- ZONE_OVAL created from a center point, major axis, minor axis, and angle. +-- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @type ZONE_OVAL +-- @extends Core.Zone#ZONE_BASE + +--- ## ZONE_OVAL class, extends @{#ZONE_BASE} +-- +-- The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle. +-- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties. +-- +-- @field #ZONE_OVAL +ZONE_OVAL = { + ClassName = "OVAL", + ZoneName="", + MajorAxis = nil, + MinorAxis = nil, + Angle = 0, + DrawPoly = nil -- let's just use a ZONE_POLYGON to draw the ZONE_OVAL on the map +} + +--- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #table vec2 The center point of the oval +-- @param #number major_axis The major axis of the oval +-- @param #number minor_axis The minor axis of the oval +-- @param #number angle The angle of the oval +-- @return #ZONE_OVAL The new oval +function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle) + self = BASE:Inherit(self, ZONE_BASE:New()) + self.ZoneName = name + self.CenterVec2 = vec2 + self.MajorAxis = major_axis + self.MinorAxis = minor_axis + self.Angle = angle or 0 + + _DATABASE:AddZone(name, self) + + return self +end + +--- Constructor to create a ZONE_OVAL instance, taking the name of a drawing made with the draw tool in the Mission Editor. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #string DrawingName The name of the drawing in the Mission Editor +-- @return #ZONE_OVAL self +function ZONE_OVAL:NewFromDrawing(DrawingName) + self = BASE:Inherit(self, ZONE_BASE:New(DrawingName)) + for _, layer in pairs(env.mission.drawings.layers) do + for _, object in pairs(layer["objects"]) do + if string.find(object["name"], DrawingName, 1, true) then + if object["polygonMode"] == "oval" then + self.CenterVec2 = { x = object["mapX"], y = object["mapY"] } + self.MajorAxis = object["r1"] + self.MinorAxis = object["r2"] + self.Angle = object["angle"] + + end + end + end + end + + _DATABASE:AddZone(DrawingName, self) + + return self +end + +--- Gets the major axis of the oval. +-- @param #ZONE_OVAL self +-- @return #number The major axis of the oval +function ZONE_OVAL:GetMajorAxis() + return self.MajorAxis +end + +--- Gets the minor axis of the oval. +-- @param #ZONE_OVAL self +-- @return #number The minor axis of the oval +function ZONE_OVAL:GetMinorAxis() + return self.MinorAxis +end + +--- Gets the angle of the oval. +-- @param #ZONE_OVAL self +-- @return #number The angle of the oval +function ZONE_OVAL:GetAngle() + return self.Angle +end + +--- Returns a the center point of the oval +-- @param #ZONE_OVAL self +-- @return #table The center Vec2 +function ZONE_OVAL:GetVec2() + return self.CenterVec2 +end + +--- Checks if a point is contained within the oval. +-- @param #ZONE_OVAL self +-- @param #table point The point to check +-- @return #bool True if the point is contained, false otherwise +function ZONE_OVAL:IsVec2InZone(vec2) + local cos, sin = math.cos, math.sin + local dx = vec2.x - self.CenterVec2.x + local dy = vec2.y - self.CenterVec2.y + local rx = dx * cos(self.Angle) + dy * sin(self.Angle) + local ry = -dx * sin(self.Angle) + dy * cos(self.Angle) + return rx * rx / (self.MajorAxis * self.MajorAxis) + ry * ry / (self.MinorAxis * self.MinorAxis) <= 1 +end + +--- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval. +-- @param #ZONE_OVAL self +-- @return #table The bounding box of the oval +function ZONE_OVAL:GetBoundingSquare() + local min_x = self.CenterVec2.x - self.MajorAxis + local min_y = self.CenterVec2.y - self.MinorAxis + local max_x = self.CenterVec2.x + self.MajorAxis + local max_y = self.CenterVec2.y + self.MinorAxis + + return { + {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} + } +end + +--- Find points on the edge of the oval +-- @param #ZONE_OVAL self +-- @param #number num_points How many points should be found. More = smoother shape +-- @return #table Points on he edge +function ZONE_OVAL:PointsOnEdge(num_points) + num_points = num_points or 40 + local points = {} + local dtheta = 2 * math.pi / num_points + + for i = 0, num_points - 1 do + local theta = i * dtheta + local x = self.CenterVec2.x + self.MajorAxis * math.cos(theta) * math.cos(self.Angle) - self.MinorAxis * math.sin(theta) * math.sin(self.Angle) + local y = self.CenterVec2.y + self.MajorAxis * math.cos(theta) * math.sin(self.Angle) + self.MinorAxis * math.sin(theta) * math.cos(self.Angle) + table.insert(points, {x = x, y = y}) + end + + return points +end + +--- Returns a random Vec2 within the oval. +-- @param #ZONE_OVAL self +-- @return #table The random Vec2 +function ZONE_OVAL:GetRandomVec2() + local theta = math.rad(self.Angle) + + local random_point = math.sqrt(math.random()) --> uniformly + --local random_point = math.random() --> more clumped around center + local phi = math.random() * 2 * math.pi + local x_c = random_point * math.cos(phi) + local y_c = random_point * math.sin(phi) + local x_e = x_c * self.MajorAxis + local y_e = y_c * self.MinorAxis + local rx = (x_e * math.cos(theta) - y_e * math.sin(theta)) + self.CenterVec2.x + local ry = (x_e * math.sin(theta) + y_e * math.cos(theta)) + self.CenterVec2.y + + return {x=rx, y=ry} +end + +--- Define a random @{Core.Point#POINT_VEC2} within the zone. +-- @param #ZONE_OVAL self +-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. +function ZONE_OVAL:GetRandomPointVec2() + return POINT_VEC2:NewFromVec2(self:GetRandomVec2()) +end + +--- Define a random @{Core.Point#POINT_VEC2} within the zone. +-- @param #ZONE_OVAL self +-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. +function ZONE_OVAL:GetRandomPointVec3() + return POINT_VEC2:NewFromVec3(self:GetRandomVec2()) +end + +--- Draw the zone on the F10 map. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work +-- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work +-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. +-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @return #ZONE_OVAL self +function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) + Coalition = Coalition or self:GetDrawCoalition() + + -- Set draw coalition. + self:SetDrawCoalition(Coalition) + + Color = Color or self:GetColorRGB() + Alpha = Alpha or 1 + + -- Set color. + self:SetColor(Color, Alpha) + + FillColor = FillColor or self:GetFillColorRGB() + if not FillColor then + UTILS.DeepCopy(Color) + end + FillAlpha = FillAlpha or self:GetFillColorAlpha() + if not FillAlpha then + FillAlpha = 0.15 + end + + LineType = LineType or 1 + + -- Set fill color -----------> has fill color worked in recent versions of DCS? + -- doing something like + -- + -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") + -- + -- doesn't seem to fill in the shape for an n-sided polygon + self:SetFillColor(FillColor, FillAlpha) + + self.DrawPoly = ZONE_POLYGON:NewFromPointsArray(self.ZoneName, self:PointsOnEdge(80)) + self.DrawPoly:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) +end + +--- Remove drawing from F10 map +-- @param #ZONE_OVAL self +function ZONE_OVAL:UndrawZone() + if self.DrawPoly then + self.DrawPoly:UndrawZone() + end +end + do -- ZONE_AIRBASE --- diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 7147ef789..64839cbe1 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -443,22 +443,35 @@ end --- Print a table to log in a nice format -- @param #table table The table to print --- @param #number indent Number of idents +-- @param #number indent Number of indents +-- @return #string text Text created on the fly of the log output function UTILS.PrintTableToLog(table, indent) + local text = "\n" if not table then env.warning("No table passed!") - return + return nil end if not indent then indent = 0 end for k, v in pairs(table) do + if string.find(k," ") then k='"'..k..'"'end if type(v) == "table" then env.info(string.rep(" ", indent) .. tostring(k) .. " = {") - UTILS.PrintTableToLog(v, indent + 1) - env.info(string.rep(" ", indent) .. "}") + text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n" + text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n" + env.info(string.rep(" ", indent) .. "},") + text = text .. string.rep(" ", indent) .. "},\n" else - env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v)) + local value + if tostring(v) == "true" or tostring(v) == "false" or tonumber(v) ~= nil then + value=v + else + value = '"'..tostring(v)..'"' + end + env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n") + text = text .. string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n" end end + return text end --- Returns table in a easy readable string representation. From 6fbe981ce147c73ac4412c8615016616976b32df Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 13:01:32 +0100 Subject: [PATCH 439/603] xxx --- Moose Development/Moose/Core/Set.lua | 88 +++++++++---------- .../Moose/Functional/Detection.lua | 3 +- 2 files changed, 42 insertions(+), 49 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index c9cbc9f3a..43fbd8fce 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2854,58 +2854,50 @@ do -- SET_UNIT -- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units. function SET_UNIT:GetCoordinate() - local Coordinate = nil - local unit = self:GetFirst() - if self:Count() == 1 and unit then - return unit:GetCoordinate() - end - if unit then - Coordinate = unit:GetCoordinate() - self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) - - local x1 = Coordinate.x - local x2 = Coordinate.x - local y1 = Coordinate.y - local y2 = Coordinate.y - local z1 = Coordinate.z - local z2 = Coordinate.z - local MaxVelocity = 0 - local AvgHeading = nil - local MovingCount = 0 - - for UnitName, UnitData in pairs( self.Set) do - - local Unit = UnitData -- Wrapper.Unit#UNIT - local Coord = Unit:GetCoordinate() - - x1 = (Coord.x < x1) and Coord.x or x1 - x2 = (Coord.x > x2) and Coord.x or x2 - y1 = (Coord.y < y1) and Coord.y or y1 - y2 = (Coord.y > y2) and Coord.y or y2 - z1 = (Coord.y < z1) and Coord.z or z1 - z2 = (Coord.y > z2) and Coord.z or z2 - - local Velocity = Coord:GetVelocity() - if Velocity ~= 0 then - MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity - local Heading = Coordinate:GetHeading() - AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading - MovingCount = MovingCount + 1 + local function GetSetVec3(units) + -- Init. + local x=0 + local y=0 + local z=0 + local n=0 + -- Loop over all units. + for _,unit in pairs(units) do + local vec3=nil --DCS#Vec3 + if unit and unit:IsAlive() then + vec3 = unit:GetVec3() + end + if vec3 then + -- Sum up posits. + x=x+vec3.x + y=y+vec3.y + z=z+vec3.z + -- Increase counter. + n=n+1 end end - - AvgHeading = AvgHeading and (AvgHeading / MovingCount) - - Coordinate.x = (x2 - x1) / 2 + x1 - Coordinate.y = (y2 - y1) / 2 + y1 - Coordinate.z = (z2 - z1) / 2 + z1 - Coordinate:SetHeading( AvgHeading ) - Coordinate:SetVelocity( MaxVelocity ) - - self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) + if n>0 then + -- Average. + local Vec3={x=x/n, y=y/n, z=z/n} --DCS#Vec3 + return Vec3 + end + return nil + end + + local Coordinate = nil + local Vec3 = GetSetVec3(self.Set) + if Vec3 then + Coordinate = COORDINATE:NewFromVec3(Vec3) end - return Coordinate + if Coordinate then + local heading = self:GetHeading() or 0 + local velocity = self:GetVelocity() or 0 + Coordinate:SetHeading( heading ) + Coordinate:SetVelocity( velocity ) + self:I(UTILS.PrintTableToLog(Coordinate)) + end + + return Coordinate end --- Get the maximum velocity of the SET_UNIT. diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 1d44ca079..3922cd36b 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -2435,7 +2435,8 @@ do -- DETECTION_TYPES end do -- DETECTION_AREAS - + + --- -- @type DETECTION_AREAS -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. From af3af14d77811f0587a5517c09762fc8cb535d15 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 13:51:16 +0100 Subject: [PATCH 440/603] # DETECTION_BASE * Added `SetRadarBlur(minheight,thresheight,thresblur)` --- .../Moose/Functional/Detection.lua | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 3922cd36b..2ec01a349 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -712,6 +712,22 @@ do -- DETECTION_BASE end end + -- Calculate radar blue probability + + if self.RadarBlur then + local minheight = self.RadarBlurMinHeight or 250 -- meters + local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group + local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall + local fheight = math.floor(math.random(1,10000)/100) + local fblur = math.floor(math.random(1,10000)/100) + local unit = UNIT:FindByName(DetectedObjectName) + if unit and unit:IsAlive() then + local AGL = unit:GetAltitude(true) + if AGL <= minheight and fheight > thresheight then DetectionAccepted = false end + if fblur > thresblur then DetectionAccepted = false end + end + end + -- Calculate additional probabilities if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.DistanceProbability then @@ -1011,7 +1027,21 @@ do -- DETECTION_BASE return self end - + + --- Method to make the radar detection less accurate, e.g. for WWII scenarios. + -- @param #DETECTION_BASE self + -- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground) + -- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance) + -- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found) + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur) + self.RadarBlur = true + self.RadarBlurMinHeight = minheight or 250 -- meters + self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group + self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall + return self + end + end do From e9266ed01c85d9e74db9c6f4114649b8cfafbe46 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 14:34:21 +0100 Subject: [PATCH 441/603] xx --- Moose Development/Moose/Core/Spawn.lua | 31 +++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 388b9aafe..185b638b9 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1108,6 +1108,22 @@ function SPAWN:InitRandomizeCallsign() return self end +--- [AIR only!] This method sets a specific callsign for a spawned group. Use for a group with one unit only! +-- @param #SPAWN self +-- @param #number ID ID of the callsign enumerator, e.g. CALLSIGN.Tanker.Texaco - - resulting in e.g. Texaco-2-1 +-- @param #string Name Name of this callsign as it cannot be determined from the ID because of the dependency on the task type of the plane, and the plane type. E.g. "Texaco" +-- @param #number Minor Minor number, i.e. the unit number within the group, e.g 2 - resulting in e.g. Texaco-2-1 +-- @param #number Major Major number, i.e. the group number of this name, e.g. 1 - resulting in e.g. Texaco-2-1 +-- @return #SPAWN self +function SPAWN:InitCallSign(ID,Name,Minor,Major) + self.SpawnInitCallSign = true + self.SpawnInitCallSignID = ID or 1 + self.SpawnInitCallSignMinor = Minor or 1 + self.SpawnInitCallSignMajor = Major or 1 + self.SpawnInitCallSignName = string.lower(Name) or "enfield" + return self +end + --- This method sets a spawn position for the group that is different from the location of the template. -- @param #SPAWN self -- @param Core.Point#COORDINATE Coordinate The position to spawn from @@ -3331,10 +3347,23 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end end + if self.SpawnInitCallSign then + for UnitID = 1, #SpawnTemplate.units do + local Callsign = SpawnTemplate.units[UnitID].callsign + if Callsign and type( Callsign ) ~= "number" then + SpawnTemplate.units[UnitID].callsign[1] = self.SpawnInitCallSignID + SpawnTemplate.units[UnitID].callsign[2] = self.SpawnInitCallSignMinor + SpawnTemplate.units[UnitID].callsign[3] = self.SpawnInitCallSignMajor + SpawnTemplate.units[UnitID].callsign["name"] = string.format("%s%d%d",self.SpawnInitCallSignName,self.SpawnInitCallSignMinor,self.SpawnInitCallSignMajor) + UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + end + end + end + for UnitID = 1, #SpawnTemplate.units do local Callsign = SpawnTemplate.units[UnitID].callsign if Callsign then - if type( Callsign ) ~= "number" then -- blue callsign + if type( Callsign ) ~= "number" and not self.SpawnInitCallSign then -- blue callsign -- UTILS.PrintTableToLog(Callsign,1) Callsign[2] = ((SpawnIndex - 1) % 10) + 1 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string From e60e8b37e6539454da2ed7d1fb5b02d43d60531b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 15:50:47 +0100 Subject: [PATCH 442/603] xx --- Moose Development/Moose/Core/Spawn.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 185b638b9..df588f14a 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1108,7 +1108,7 @@ function SPAWN:InitRandomizeCallsign() return self end ---- [AIR only!] This method sets a specific callsign for a spawned group. Use for a group with one unit only! +--- [BLUE AIR only!] This method sets a specific callsign for a spawned group. Use for a group with one unit only! -- @param #SPAWN self -- @param #number ID ID of the callsign enumerator, e.g. CALLSIGN.Tanker.Texaco - - resulting in e.g. Texaco-2-1 -- @param #string Name Name of this callsign as it cannot be determined from the ID because of the dependency on the task type of the plane, and the plane type. E.g. "Texaco" @@ -3355,7 +3355,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 SpawnTemplate.units[UnitID].callsign[2] = self.SpawnInitCallSignMinor SpawnTemplate.units[UnitID].callsign[3] = self.SpawnInitCallSignMajor SpawnTemplate.units[UnitID].callsign["name"] = string.format("%s%d%d",self.SpawnInitCallSignName,self.SpawnInitCallSignMinor,self.SpawnInitCallSignMajor) - UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) end end end @@ -3371,7 +3371,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local CallsignLen = CallsignName:len() SpawnTemplate.units[UnitID].callsign[2] = UnitID SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] - else + elseif type( Callsign ) == "number" then SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex end end From 8c57e9cb093090940cb860349df55fa2d5410f0e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 18:16:10 +0100 Subject: [PATCH 443/603] xxx --- Moose Development/Moose/Functional/Detection.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 2ec01a349..915b93543 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -715,6 +715,7 @@ do -- DETECTION_BASE -- Calculate radar blue probability if self.RadarBlur then + --BASE:I("RadarBlur") local minheight = self.RadarBlurMinHeight or 250 -- meters local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall @@ -723,8 +724,11 @@ do -- DETECTION_BASE local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - if AGL <= minheight and fheight > thresheight then DetectionAccepted = false end + --BASE:I("Unit "..DetectedObjectName.." is at "..AGL.."m.") + --BASE:I(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur)) if fblur > thresblur then DetectionAccepted = false end + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + --BASE:I("Detection Accepted = "..tostring(DetectionAccepted)) end end From ec7dd6154df1caf10aa28b139a84aab3014a896a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Dec 2023 11:58:34 +0100 Subject: [PATCH 444/603] xxx --- Moose Development/Moose/Functional/Detection.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index a33e7f7b0..68ff7c569 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -724,7 +724,7 @@ do -- DETECTION_BASE -- Calculate radar blue probability if self.RadarBlur then - BASE:I("RadarBlur") + MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose) local minheight = self.RadarBlurMinHeight or 250 -- meters local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall @@ -733,11 +733,11 @@ do -- DETECTION_BASE local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - BASE:I("Unit "..DetectedObjectName.." is at "..AGL.."m.") - BASE:I(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur)) + MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m.",10):ToLogIf(self.debug):ToAllIf(self.verbose) + MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose) if fblur > thresblur then DetectionAccepted = false end - if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end - BASE:I("Detection Accepted = "..tostring(DetectionAccepted)) + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose) end end From a50ee191e5a1f6b5046619d44f6c62415103e6d9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Dec 2023 14:38:19 +0100 Subject: [PATCH 445/603] xxx --- Moose Development/Moose/Functional/Detection.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 68ff7c569..1acd82ad2 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -728,12 +728,17 @@ do -- DETECTION_BASE local minheight = self.RadarBlurMinHeight or 250 -- meters local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall + local dist = math.floor(Distance) + if dist <= 20 then + thresheight = (((dist*dist)/400)*thresheight) + thresblur = (((dist*dist)/400)*thresblur) + end local fheight = math.floor(math.random(1,10000)/100) local fblur = math.floor(math.random(1,10000)/100) local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m.",10):ToLogIf(self.debug):ToAllIf(self.verbose) + MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m. Distance "..math.floor(Distance).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose) MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose) if fblur > thresblur then DetectionAccepted = false end if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end From 945a81b7455d292db62baac192ca286cbc9c24f6 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:17:30 +0100 Subject: [PATCH 446/603] Update Intelligence.lua --- Moose Development/Moose/Ops/Intelligence.lua | 62 ++++++++++++++++---- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index ed1f575ab..a9134e21f 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -512,6 +512,23 @@ function INTEL:SetFilterCategory(Categories) return self end +--- Method to make the radar detection less accurate, e.g. for WWII scenarios. +-- @param #INTEL self +-- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground) +-- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance) +-- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found) +-- @param #number closing Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20. +-- @return #INTEL self +function INTEL:SetRadarBlur(minheight,thresheight,thresblur,closing) + self.RadarBlur = true + self.RadarBlurMinHeight = minheight or 250 -- meters + self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group + self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall + self.RadarBlurClosing = closing or 20 -- 20km + self.RadarBlurClosingSquare = self.RadarBlurClosing * self.RadarBlurClosing + return self +end + --- Filter group categories. Valid categories are: -- -- * Group.Category.AIRPLANE @@ -1093,14 +1110,12 @@ end -- @param #boolean DetectRWR (Optional) If *false*, do not include targets detected by RWR. -- @param #boolean DetectDLINK (Optional) If *false*, do not include targets detected by data link. function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) - --self:T(self.lid.."GetDetectedUnits "..Unit:GetName()) + -- Get detected DCS units. local reccename = Unit:GetName() - + local detectedtargets=Unit:GetDetectedTargets(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) - - --UTILS.PrintTableToLog(detectedtargets,1) - + for DetectionObjectID, Detection in pairs(detectedtargets or {}) do local DetectedObject=Detection.object -- DCS#Object @@ -1117,11 +1132,39 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua if status then local unit=UNIT:FindByName(name) - + if unit and unit:IsAlive() then - DetectedUnits[name]=unit - RecceDetecting[name]=reccename - self:T(string.format("Unit %s detect by %s", name, reccename)) + local DetectionAccepted = true + if self.RadarBlur then + local reccecoord = Unit:GetCoordinate() + local coord = unit:GetCoordinate() + local dist = math.floor(coord:Get2DDistance(reccecoord)/1000) -- km + local AGL = unit:GetAltitude(true) + local minheight = self.RadarBlurMinHeight or 250 -- meters + local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group + local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall + --local dist = math.floor(Distance) + if dist <= self.RadarBlurClosing then + thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight) + thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur) + end + local fheight = math.floor(math.random(1,10000)/100) + local fblur = math.floor(math.random(1,10000)/100) + if fblur > thresblur then DetectionAccepted = false end + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + if self.debug or self.verbose > 1 then + MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m. Distance "..math.floor(Distance).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + end + end + + if DetectionAccepted then + DetectedUnits[name]=unit + RecceDetecting[name]=reccename + self:T(string.format("Unit %s detect by %s", name, reccename)) + end else if self.detectStatics then local static=STATIC:FindByName(name, false) @@ -1139,7 +1182,6 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua end end end - end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 42468f3505ed3516b20ebd4303c2edd245f836d4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 12 Dec 2023 10:54:01 +0100 Subject: [PATCH 447/603] xxx --- Moose Development/Moose/Cargo/CargoGroup.lua | 2 +- Moose Development/Moose/Core/Point.lua | 38 ++++++++++ Moose Development/Moose/Ops/Intelligence.lua | 80 ++++++++++++-------- 3 files changed, 88 insertions(+), 32 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 7f3f867b3..5ecfee881 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -271,7 +271,7 @@ do -- CARGO_GROUP -- @param Core.Event#EVENTDATA EventData function CARGO_GROUP:OnEventCargoDead( EventData ) - self:E(EventData) + --self:E(EventData) local Destroyed = false diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 9437bb426..d685645b3 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -3100,6 +3100,44 @@ do -- COORDINATE local MGRS = coord.LLtoMGRS( lat, lon ) return "MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy ) end + + --- Provides a COORDINATE from an MGRS String + -- @param #COORDINATE self + -- @param #string MGRSString MGRS String, e.g. "MGRS 37T DK 12345 12345" + -- @return #COORDINATE self + function COORDINATE:NewFromMGRSString( MGRSString ) + local myparts = UTILS.Split(MGRSString," ") + UTILS.PrintTableToLog(myparts,1) + local MGRS = { + UTMZone = myparts[2], + MGRSDigraph = myparts[3], + Easting = tonumber(myparts[4]), + Northing = tonumber(myparts[5]), + } + local lat, lon = coord.MGRStoLL(MGRS) + local point = coord.LLtoLO(lat, lon, 0) + local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) + return coord + end + + --- Provides a COORDINATE from an MGRS Coordinate + -- @param #COORDINATE self + -- @param #string UTMZone UTM Zone, e.g. "37T" + -- @param #string MGRSDigraph Digraph, e.g. "DK" + -- @param #number Easting Meters easting + -- @param #number Northing Meters northing + -- @return #COORDINATE self + function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing ) + local MGRS = { + UTMZone = UTMZone, + MGRSDigraph = MGRSDigraph, + Easting = Easting, + Northing = Northing, + } + local lat, lon = coord.MGRStoLL(MGRS) + local point = coord.LLtoLO(lat, lon, 0) + local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) + end --- Provides a coordinate string of the point, based on a coordinate format system: -- * Uses default settings in COORDINATE. diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index a9134e21f..1444dca05 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -529,6 +529,16 @@ function INTEL:SetRadarBlur(minheight,thresheight,thresblur,closing) return self end +--- Set the accept range in kilometers from each of the recce. Only object closer than this range will be detected. +-- @param #INTEL self +-- @param #number Range Range in kilometers +-- @return #INTEL self +function INTEL:SetAcceptRange(Range) + self.RadarAcceptRange = true + self.RadarAcceptRangeKilometers = Range or 75 + return self +end + --- Filter group categories. Valid categories are: -- -- * Group.Category.AIRPLANE @@ -1134,37 +1144,45 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua local unit=UNIT:FindByName(name) if unit and unit:IsAlive() then - local DetectionAccepted = true - if self.RadarBlur then - local reccecoord = Unit:GetCoordinate() - local coord = unit:GetCoordinate() - local dist = math.floor(coord:Get2DDistance(reccecoord)/1000) -- km - local AGL = unit:GetAltitude(true) - local minheight = self.RadarBlurMinHeight or 250 -- meters - local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group - local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall - --local dist = math.floor(Distance) - if dist <= self.RadarBlurClosing then - thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight) - thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur) - end - local fheight = math.floor(math.random(1,10000)/100) - local fblur = math.floor(math.random(1,10000)/100) - if fblur > thresblur then DetectionAccepted = false end - if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end - if self.debug or self.verbose > 1 then - MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose>1) - MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m. Distance "..math.floor(Distance).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose>1) - MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose>1) - MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose>1) - end - end - - if DetectionAccepted then - DetectedUnits[name]=unit - RecceDetecting[name]=reccename - self:T(string.format("Unit %s detect by %s", name, reccename)) - end + local DetectionAccepted = true + + if self.RadarAcceptRange then + local reccecoord = Unit:GetCoordinate() + local coord = unit:GetCoordinate() + local dist = math.floor(coord:Get2DDistance(reccecoord)/1000) -- km + if dist > self.RadarAcceptRangeKilometers then DetectionAccepted = false end + end + + if self.RadarBlur then + local reccecoord = Unit:GetCoordinate() + local coord = unit:GetCoordinate() + local dist = math.floor(coord:Get2DDistance(reccecoord)/1000) -- km + local AGL = unit:GetAltitude(true) + local minheight = self.RadarBlurMinHeight or 250 -- meters + local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group + local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall + --local dist = math.floor(Distance) + if dist <= self.RadarBlurClosing then + thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight) + thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur) + end + local fheight = math.floor(math.random(1,10000)/100) + local fblur = math.floor(math.random(1,10000)/100) + if fblur > thresblur then DetectionAccepted = false end + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + if self.debug or self.verbose > 1 then + MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + MESSAGE:New("Unit "..name.." is at "..math.floor(AGL).."m. Distance "..math.floor(dist).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + end + end + + if DetectionAccepted then + DetectedUnits[name]=unit + RecceDetecting[name]=reccename + self:T(string.format("Unit %s detect by %s", name, reccename)) + end else if self.detectStatics then local static=STATIC:FindByName(name, false) From f948a07d4468dfc4f8628d0088ec0e3b9f18fad7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 14 Dec 2023 11:10:12 +0100 Subject: [PATCH 448/603] xxx --- Moose Development/Moose/Core/Point.lua | 20 +++++++++++++------- Moose Development/Moose/Ops/OpsZone.lua | 6 +++--- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 9cf13ba3e..a3c83da1f 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -24,7 +24,8 @@ do -- COORDINATE - + + --- -- @type COORDINATE -- @field #string ClassName Name of the class -- @field #number x Component of the 3D vector. @@ -3107,13 +3108,16 @@ do -- COORDINATE -- @return #COORDINATE self function COORDINATE:NewFromMGRSString( MGRSString ) local myparts = UTILS.Split(MGRSString," ") - UTILS.PrintTableToLog(myparts,1) + local northing = tostring(myparts[5]) or "" + local easting = tostring(myparts[4]) or "" + if string.len(easting) < 5 then easting = easting..string.rep("0",5-string.len(easting)) end + if string.len(northing) < 5 then northing = northing..string.rep("0",5-string.len(northing)) end local MGRS = { UTMZone = myparts[2], MGRSDigraph = myparts[3], - Easting = tonumber(myparts[4]), - Northing = tonumber(myparts[5]), - } + Easting = easting, + Northing = northing, + } local lat, lon = coord.MGRStoLL(MGRS) local point = coord.LLtoLO(lat, lon, 0) local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) @@ -3124,10 +3128,12 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #string UTMZone UTM Zone, e.g. "37T" -- @param #string MGRSDigraph Digraph, e.g. "DK" - -- @param #number Easting Meters easting - -- @param #number Northing Meters northing + -- @param #string Easting Meters easting - string in order to allow for leading zeros, e.g. "01234". Should be 5 digits. + -- @param #string Northing Meters northing - string in order to allow for leading zeros, e.g. "12340". Should be 5 digits. -- @return #COORDINATE self function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing ) + if string.len(Easting) < 5 then Easting = Easting..string.rep("0",5-string.len(Easting) )end + if string.len(Northing) < 5 then Northing = Northing..string.rep("0",5-string.len(Northing) )end local MGRS = { UTMZone = UTMZone, MGRSDigraph = MGRSDigraph, diff --git a/Moose Development/Moose/Ops/OpsZone.lua b/Moose Development/Moose/Ops/OpsZone.lua index 780b389c0..ee83650aa 100644 --- a/Moose Development/Moose/Ops/OpsZone.lua +++ b/Moose Development/Moose/Ops/OpsZone.lua @@ -97,7 +97,7 @@ OPSZONE.ZoneType={ --- OPSZONE class version. -- @field #string version -OPSZONE.version="0.6.0" +OPSZONE.version="0.6.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -1277,7 +1277,7 @@ function OPSZONE:EvaluateZone() if Nblu>0 then - if not self:IsAttacked() then + if not self:IsAttacked() and self.Tnut>=self.threatlevelCapture then self:Attacked(coalition.side.BLUE) end @@ -1329,7 +1329,7 @@ function OPSZONE:EvaluateZone() if Nred>0 then - if not self:IsAttacked() then + if not self:IsAttacked() and self.Tnut>=self.threatlevelCapture then -- Red is attacking blue zone. self:Attacked(coalition.side.RED) end From af2517343dcf7a7f47bdaee24f9084a3860ab22c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 19 Dec 2023 10:15:30 +0100 Subject: [PATCH 449/603] #ZONE_POLYGON --- Moose Development/Moose/Core/Database.lua | 51 ++++++- Moose Development/Moose/Core/Zone.lua | 169 +++++++++++++++------- 2 files changed, 162 insertions(+), 58 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index f8470408c..ac290aa2f 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -449,10 +449,10 @@ do -- Zones and Pathlines -- Loop over layers. for layerID, layerData in pairs(env.mission.drawings.layers or {}) do - + -- Loop over objects in layers. for objectID, objectData in pairs(layerData.objects or {}) do - + -- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice) if objectData.polygonMode and (objectData.polygonMode=="free") and objectData.points and #objectData.points>=4 then @@ -488,10 +488,32 @@ do -- Zones and Pathlines -- Create new polygon zone. local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points) - + + --Zone.DrawID = objectID + -- Set color. Zone:SetColor({1, 0, 0}, 0.15) - + Zone:SetFillColor({1, 0, 0}, 0.15) + + if objectData.colorString then + -- eg colorString = 0xff0000ff + local color = string.gsub(objectData.colorString,"^0x","") + local r = tonumber(string.sub(color,1,2),16)/255 + local g = tonumber(string.sub(color,3,4),16)/255 + local b = tonumber(string.sub(color,5,6),16)/255 + local a = tonumber(string.sub(color,7,8),16)/255 + Zone:SetColor({r, g, b}, a) + end + if objectData.fillColorString then + -- eg fillColorString = 0xff00004b + local color = string.gsub(objectData.colorString,"^0x","") + local r = tonumber(string.sub(color,1,2),16)/255 + local g = tonumber(string.sub(color,3,4),16)/255 + local b = tonumber(string.sub(color,5,6),16)/255 + local a = tonumber(string.sub(color,7,8),16)/255 + Zone:SetFillColor({r, g, b}, a) + end + -- Store in DB. self.ZONENAMES[ZoneName] = ZoneName @@ -532,7 +554,26 @@ do -- Zones and Pathlines -- Set color. Zone:SetColor({1, 0, 0}, 0.15) - + + if objectData.colorString then + -- eg colorString = 0xff0000ff + local color = string.gsub(objectData.colorString,"^0x","") + local r = tonumber(string.sub(color,1,2),16)/255 + local g = tonumber(string.sub(color,3,4),16)/255 + local b = tonumber(string.sub(color,5,6),16)/255 + local a = tonumber(string.sub(color,7,8),16)/255 + Zone:SetColor({r, g, b}, a) + end + if objectData.fillColorString then + -- eg fillColorString = 0xff00004b + local color = string.gsub(objectData.colorString,"^0x","") + local r = tonumber(string.sub(color,1,2),16)/255 + local g = tonumber(string.sub(color,3,4),16)/255 + local b = tonumber(string.sub(color,5,6),16)/255 + local a = tonumber(string.sub(color,7,8),16)/255 + Zone:SetFillColor({r, g, b}, a) + end + -- Store in DB. self.ZONENAMES[ZoneName] = ZoneName diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 09ef67c2f..33302b4a0 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2020,7 +2020,7 @@ _ZONE_TRIANGLE = { Coords={}, CenterVec2={x=0, y=0}, SurfaceArea=0, - DrawIDs={} + DrawID={} } --- -- @param #_ZONE_TRIANGLE self @@ -2100,15 +2100,35 @@ function _ZONE_TRIANGLE:Draw(Coalition, Color, Alpha, FillColor, FillAlpha, Line for i=1, #self.Coords do local c1 = self.Coords[i] local c2 = self.Coords[i % #self.Coords + 1] - table.add(self.DrawIDs, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly)) + local id = c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly) + self.DrawID[#self.DrawID+1] = id end - return self.DrawIDs + local newID = self.Coords[1]:MarkupToAllFreeForm({self.Coords[2],self.Coords[3]},Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly) + self.DrawID[#self.DrawID+1] = newID + return self.DrawID +end + +--- Draw the triangle +-- @param #_ZONE_TRIANGLE self +-- @return #table of draw IDs +function _ZONE_TRIANGLE:Fill(Coalition, FillColor, FillAlpha, ReadOnly) + Coalition=Coalition or -1 + FillColor = FillColor + FillAlpha = FillAlpha + local newID = self.Coords[1]:MarkupToAllFreeForm({self.Coords[2],self.Coords[3]},Coalition,nil,nil,FillColor,FillAlpha,0,nil) + self.DrawID[#self.DrawID+1] = newID + return self.DrawID end --- -- @type ZONE_POLYGON_BASE -- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}. +-- @field #number SurfaceArea +-- @field #table DrawID +-- @field #table FillTriangles +-- @field #table _Triangles +-- @field #table Borderlines -- @extends #ZONE_BASE @@ -2133,9 +2153,11 @@ end -- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE = { ClassName="ZONE_POLYGON_BASE", - _Triangles={}, -- _ZONE_TRIANGLES + _Triangles={}, -- #table of #_ZONE_TRIANGLE SurfaceArea=0, - DrawID={} -- making a table out of the MarkID so its easier to draw an n-sided polygon, see ZONE_POLYGON_BASE:Draw() + DrawID={}, -- making a table out of the MarkID so its easier to draw an n-sided polygon, see ZONE_POLYGON_BASE:Draw() + FillTriangles = {}, + Borderlines = {}, } --- A 2D points array. @@ -2470,57 +2492,97 @@ end -- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work -- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work -- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. --- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.s -- @return #ZONE_POLYGON_BASE self function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, IncludeTriangles) - if self._.Polygon and #self._.Polygon >= 3 then - Coalition = Coalition or self:GetDrawCoalition() - - -- Set draw coalition. - self:SetDrawCoalition(Coalition) - - Color = Color or self:GetColorRGB() - Alpha = Alpha or 1 - - -- Set color. - self:SetColor(Color, Alpha) - - FillColor = FillColor or self:GetFillColorRGB() - if not FillColor then - UTILS.DeepCopy(Color) - end - FillAlpha = FillAlpha or self:GetFillColorAlpha() - if not FillAlpha then - FillAlpha = 0.15 - end - - -- Set fill color -----------> has fill color worked in recent versions of DCS? - -- doing something like - -- - -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") - -- - -- doesn't seem to fill in the shape for an n-sided polygon - self:SetFillColor(FillColor, FillAlpha) - - IncludeTriangles = IncludeTriangles or false - - -- just draw the triangles, we get the outline for free - if IncludeTriangles then - for _, triangle in pairs(self._Triangles) do - local draw_ids = triangle:Draw() - table.combine(self.DrawID, draw_ids) - end - -- draw outline only - else - local coords = self:GetVerticiesCoordinates() - for i = 1, #coords do - local c1 = coords[i] - local c2 = coords[i % #coords + 1] - table.add(self.DrawID, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly)) - end - end + + if self._.Polygon and #self._.Polygon >= 3 then + Coalition = Coalition or self:GetDrawCoalition() + + -- Set draw coalition. + self:SetDrawCoalition(Coalition) + + Color = Color or self:GetColorRGB() + Alpha = Alpha or self:GetColorAlpha() + + FillColor = FillColor or self:GetFillColorRGB() + FillAlpha = FillAlpha or self:GetFillColorAlpha() + + if FillColor then + self:ReFill(Color,Alpha) end - return self + + if Color then + self:ReDrawBorderline(Color,Alpha,LineType) + end + end + + return self +end + +--- Change/Re-fill a Polygon Zone +-- @param #ZONE_POLYGON_BASE self +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @return #ZONE_POLYGON_BASE self +function ZONE_POLYGON_BASE:ReFill(Color,Alpha) + local color = Color or self:GetFillColorRGB() or {1,0,0} + local alpha = Alpha or self:GetFillColorAlpha() or 1 + local coalition = self:GetDrawCoalition() or -1 + -- undraw if already filled + if #self.FillTriangles > 0 then + for _, triangle in pairs(self._Triangles) do + triangle:UndrawZone() + end + -- remove mark IDs + for _,_value in pairs(self.FillTriangles) do + table.remove_by_value(self.DrawID, _value) + end + self.FillTriangles = nil + self.FillTriangles = {} + end + -- refill + for _, triangle in pairs(self._Triangles) do + local draw_ids = triangle:Fill(coalition,color,alpha,nil) + self.FillTriangles = draw_ids + table.combine(self.DrawID, draw_ids) + end + return self +end + +--- Change/Re-draw the border of a Polygon Zone +-- @param #ZONE_POLYGON_BASE self +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. +-- @return #ZONE_POLYGON_BASE +function ZONE_POLYGON_BASE:ReDrawBorderline(Color, Alpha, LineType) + local color = Color or self:GetFillColorRGB() or {1,0,0} + local alpha = Alpha or self:GetFillColorAlpha() or 1 + local coalition = self:GetDrawCoalition() or -1 + local linetype = LineType or 1 + -- undraw if already drawn + if #self.Borderlines > 0 then + for _, MarkID in pairs(self.Borderlines) do + trigger.action.removeMark(MarkID) + end + -- remove mark IDs + for _,_value in pairs(self.Borderlines) do + table.remove_by_value(self.DrawID, _value) + end + self.Borderlines = nil + self.Borderlines = {} + end + -- Redraw border + local coords = self:GetVerticiesCoordinates() + for i = 1, #coords do + local c1 = coords[i] + local c2 = coords[i % #coords + 1] + local newID = c1:LineToAll(c2, coalition, color, alpha, linetype, nil) + self.DrawID[#self.DrawID+1]=newID + self.Borderlines[#self.Borderlines+1] = newID + end + return self end --- Get the surface area of this polygon @@ -2856,6 +2918,7 @@ function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, C Alpha = Alpha or 1 Segments = Segments or 10 Closed = Closed or false + local Limit local i = 1 local j = #self._.Polygon if (Closed) then From 831bde704236b770622effc83df8eecec8f56bb7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 20 Dec 2023 09:18:21 +0100 Subject: [PATCH 450/603] utils --- Moose Development/Moose/Utilities/Utils.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 64839cbe1..7955ddf6f 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -2146,17 +2146,17 @@ function UTILS.IsLoadingDoorOpen( unit_name ) return true end - if string.find(type_name, "Bell-47") then -- bell aint got no doors so always ready to load injured soldiers + if string.find(type_name, "Bell-47",1,true) then -- bell aint got no doors so always ready to load injured soldiers BASE:T(unit_name .. " door is open") return true end - if string.find(type_name, "UH-60L") and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then + if type_name == "UH-60L" and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then BASE:T(unit_name .. " cargo door is open") return true end - if string.find(type_name, "UH-60L" ) and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(400) == 1 ) then + if type_name == "UH-60L" and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(400) == 1 ) then BASE:T(unit_name .. " front door(s) are open") return true end From fc0c54c7fdc099311723cc82f1c24faa4aedd86f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 20 Dec 2023 10:08:26 +0100 Subject: [PATCH 451/603] xx --- Moose Development/Moose/Utilities/Utils.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 7955ddf6f..49739f2a6 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -2146,17 +2146,17 @@ function UTILS.IsLoadingDoorOpen( unit_name ) return true end - if string.find(type_name, "Bell-47",1,true) then -- bell aint got no doors so always ready to load injured soldiers + if type_name == "Bell-47" then -- bell aint got no doors so always ready to load injured soldiers BASE:T(unit_name .. " door is open") return true end - + if type_name == "UH-60L" and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then BASE:T(unit_name .. " cargo door is open") return true end - if type_name == "UH-60L" and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(400) == 1 ) then + if type_name == "UH-60L" and (unit:getDrawArgumentValue(38) > 0 or unit:getDrawArgumentValue(400) == 1 ) then BASE:T(unit_name .. " front door(s) are open") return true end From 63acca90fd6bff6ea2e01cb292ebc2379e336949 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 21 Dec 2023 11:06:31 +0100 Subject: [PATCH 452/603] xxx --- Moose Development/Moose/Core/Set.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 43fbd8fce..c81cec3fd 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -7809,7 +7809,7 @@ do -- SET_OPSGROUP local MGroupPrefix = false for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do - if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then --Not sure why "-" is replaced by "%-" ?! + if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then --Not sure why "-" is replaced by "%-" ?! - So we can still match group names with a dash in them MGroupPrefix = true end end From 668ba51ba2281c56c635b34fe5bf0e7553dc36e1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 23 Dec 2023 14:50:57 +0100 Subject: [PATCH 453/603] xxx --- Moose Development/Moose/Functional/Range.lua | 4 +++- Moose Development/Moose/Ops/FlightControl.lua | 20 +++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 8da132f14..5b4d52623 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1737,7 +1737,9 @@ end -- @param Core.Event#EVENTDATA EventData function RANGE:OnEventBirth( EventData ) self:F( { eventbirth = EventData } ) - + + if not EventData.IniPlayerName then return end + local _unitName = EventData.IniUnitName local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 8403e0c24..112834e53 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -4424,14 +4424,11 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Add parking guard in front of a parking aircraft. +--- [INTERNAL] Add parking guard in front of a parking aircraft - delayed for MP. -- @param #FLIGHTCONTROL self -- @param Wrapper.Unit#UNIT unit The aircraft. -function FLIGHTCONTROL:SpawnParkingGuard(unit) - - if unit and self.parkingGuard then - - -- Position of the unit. +function FLIGHTCONTROL:_SpawnParkingGuard(unit) + -- Position of the unit. local coordinate=unit:GetCoordinate() -- Parking spot. @@ -4478,6 +4475,17 @@ function FLIGHTCONTROL:SpawnParkingGuard(unit) else self:E(self.lid.."ERROR: Parking Guard already exists!") end +end + +--- Add parking guard in front of a parking aircraft. +-- @param #FLIGHTCONTROL self +-- @param Wrapper.Unit#UNIT unit The aircraft. +function FLIGHTCONTROL:SpawnParkingGuard(unit) + + if unit and self.parkingGuard then + + -- Schedule delay so in MP we get the heading of the client's plane + self:ScheduleOnce(1,FLIGHTCONTROL._SpawnParkingGuard,self,unit) end From e913e835964b1c6db1d90e391f4e9c6a34f813de Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Dec 2023 11:07:39 +0100 Subject: [PATCH 454/603] xxx --- Moose Development/Moose/Core/SpawnStatic.lua | 2 +- Moose Development/Moose/Wrapper/Static.lua | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 05ebdb40b..757461958 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -501,7 +501,7 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID) else self:T("Spawning Static") - self:T2({Template=Template}) + self:T2({Template=Template}) Static=coalition.addStaticObject(CountryID, Template) end diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index e9ca045e8..a6bd9c1d5 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -237,7 +237,7 @@ function STATIC:SpawnAt(Coordinate, Heading, Delay) end ---- Respawn the @{Wrapper.Unit} at the same location with the same properties. +--- Respawn the @{Wrapper.Static} at the same location with the same properties. -- This is useful to respawn a cargo after it has been destroyed. -- @param #STATIC self -- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently. @@ -249,7 +249,7 @@ function STATIC:ReSpawn(CountryID, Delay) else CountryID=CountryID or self:GetCountry() - + local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID) SpawnStatic:Spawn(nil, self.StaticName) @@ -271,8 +271,8 @@ function STATIC:ReSpawnAt(Coordinate, Heading, Delay) if Delay and Delay>0 then SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay) - else - + else + local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry()) SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName) From d010c3a9fa0e85b5802bc2ec51b757f1ef016039 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Dec 2023 12:14:18 +0100 Subject: [PATCH 455/603] New SRS fixes --- Moose Development/Moose/Core/Message.lua | 3 --- Moose Development/Moose/Functional/AICSAR.lua | 12 ++++++++---- Moose Development/Moose/Ops/Airboss.lua | 2 +- Moose Development/Moose/Ops/CSAR.lua | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 8b6da3cdd..fb27a6d1b 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -498,7 +498,6 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G _MESSAGESRS.Gender = Gender or "female" _MESSAGESRS.MSRS:SetGoogle(PathToCredentials) - _MESSAGESRS.google = PathToCredentials _MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE") _MESSAGESRS.label = Label or "MESSAGE" @@ -512,8 +511,6 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G if Voice then _MESSAGESRS.MSRS:SetVoice(Voice) end _MESSAGESRS.voice = Voice --or MSRS.Voices.Microsoft.Hedda - --if _MESSAGESRS.google and not Voice then _MESSAGESRS.Voice = MSRS.Voices.Google.Standard.en_GB_Standard_A end - --_MESSAGESRS.MSRS:SetVoice(Voice or _MESSAGESRS.voice) _MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE") end diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index 5f158932f..5b9da5494 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -606,8 +606,10 @@ function AICSAR:SetPilotTTSVoice(Voice,Culture,Gender) self.SRSPilot:SetCulture(Culture or "en-US") self.SRSPilot:SetGender(Gender or "male") self.SRSPilot:SetLabel("PILOT") - if self.SRS.google then - self.SRSPilot:SetGoogle(self.SRS.google) + if self.SRSGoogle then + local poptions = self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -- Sound.SRS#MSRS.ProviderOptions + self.SRSOperator:SetGoogle(poptions.credentials) + self.SRSOperator:SetGoogleAPIKey(poptions.key) end return self end @@ -628,8 +630,10 @@ function AICSAR:SetOperatorTTSVoice(Voice,Culture,Gender) self.SRSOperator:SetCulture(Culture or "en-GB") self.SRSOperator:SetGender(Gender or "female") self.SRSPilot:SetLabel("RESCUE") - if self.SRS.google then - self.SRSOperator:SetGoogle(self.SRS.google) + if self.SRSGoogle then + local poptions = self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -- Sound.SRS#MSRS.ProviderOptions + self.SRSOperator:SetGoogle(poptions.credentials) + self.SRSOperator:SetGoogleAPIKey(poptions.key) end return self end diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 0c83d051e..75b621966 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -14957,7 +14957,7 @@ function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture ) self.PilotRadio.gender = Gender or "male" self.PilotRadio.culture = Culture or "en-US" - if (not Voice) and self.SRS and self.SRS.google then + if (not Voice) and self.SRS and self.SRS:GetProvider() == MSRS.Provider.GOOGLE then self.PilotRadio.voice = MSRS.Voices.Google.Standard.en_US_Standard_J end diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index becdf6ec3..096379beb 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -1923,7 +1923,7 @@ function CSAR:_DisplayToAllSAR(_message, _side, _messagetime) local messagetime = _messagetime or self.messageTime if self.msrs then local voice = self.CSARVoice or MSRS.Voices.Google.Standard.en_GB_Standard_F - if self.msrs.google == nil then + if self.msrs:GetProvider() == MSRS.Provider.WINDOWS then voice = self.CSARVoiceMS or MSRS.Voices.Microsoft.Hedda end self:I("Voice = "..voice) From 2be85fb1878d80e12870f80e8561783d8007da6b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 26 Dec 2023 19:19:10 +0100 Subject: [PATCH 456/603] CTLD --- Moose Development/Moose/Ops/CTLD.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 095eb28de..0ae3a2ef8 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1228,7 +1228,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.44" +CTLD.version="1.0.45" --- Instantiate a new CTLD. -- @param #CTLD self @@ -2454,11 +2454,13 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack) realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,nil,subcat) table.insert(droppedcargo,realcargo) else - realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],false,cargotype.PerCrateMass,nil,subcat) - Cargo:RemoveStock() + realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],false,cargotype.PerCrateMass,nil,subcat) end table.insert(self.Spawned_Cargo, realcargo) end + if not (drop or pack) then + Cargo:RemoveStock() + end local text = string.format("Crates for %s have been positioned near you!",cratename) if drop then text = string.format("Crates for %s have been dropped!",cratename) @@ -3824,7 +3826,7 @@ end -- @param #CTLD_CARGO.Enum Type Type of cargo. I.e. VEHICLE or FOB. VEHICLE will move to destination zones when dropped/build, FOB stays put. -- @param #number NoCrates Number of crates needed to build this cargo. -- @param #number PerCrateMass Mass in kg of each crate --- @param #number Stock Number of groups in stock. Nil for unlimited. +-- @param #number Stock Number of buildable groups in stock. Nil for unlimited. -- @param #string SubCategory Name of sub-category (optional). function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory) self:T(self.lid .. " AddCratesCargo") From ec9119d1d059bd6a4a8eb468c6c38d4ac57c3da3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 27 Dec 2023 19:27:40 +0100 Subject: [PATCH 457/603] xxx --- Moose Development/Moose/Core/Set.lua | 141 ++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index c81cec3fd..8877e1462 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -146,7 +146,52 @@ do -- SET_BASE return self end - + + --- [Internal] Add a functional filter + -- @param #SET_BASE self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a CONTROLLABLE object as first argument. + -- @param ... Condition function arguments, if any. + -- @return #boolean If true, at least one condition is true + function SET_BASE:FilterFunction(ConditionFunction, ...) + + local condition={} + condition.func=ConditionFunction + condition.arg={} + + if arg then + condition.arg=arg + end + + if not self.Filter.Functions then self.Filter.Functions = {} end + table.insert(self.Filter.Functions, condition) + + return self + end + + --- [Internal] Check if the condition functions returns true. + -- @param #SET_BASE self + -- @param Wrapper.Controllable#CONTROLLABLE Object The object to filter for + -- @return #boolean If true, at least one condition is true. + function SET_BASE:_EvalFilterFunctions(Object) + + -- Any one condition must be true. + for _,_condition in pairs(self.Filter.Functions or {}) do + local condition=_condition + + -- Call function. + local istrue=condition.func(Object,unpack(condition.arg)) + + -- Any true will return true. + if istrue then + return true + end + + end + + -- No condition was true. + return false + end + --- Clear the Objects in the Set. -- @param #SET_BASE self -- @param #boolean TriggerEvent If `true`, an event remove is triggered for each group that is removed from the set. @@ -1040,6 +1085,7 @@ do Countries = nil, GroupPrefixes = nil, Zones = nil, + Functions = nil, }, FilterMeta = { Coalitions = { @@ -1253,7 +1299,26 @@ do return self end + + --- [User] Add a custom condition function. + -- @function [parent=#SET_GROUP] FilterFunction + -- @param #SET_GROUP self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a GROUP object as first argument. + -- @param ... Condition function arguments if any. + -- @return #SET_GROUP self + -- @usage + -- -- Image you want to exclude a specific GROUP from a SET: + -- local groundset = SET_GROUP:New():FilterCoalitions("blue"):FilterCategoryGround():FilterFunction( + -- -- The function needs to take a GROUP object as first - and in this case, only - argument. + -- function(grp) + -- local isinclude = true + -- if grp:GetName() == "Exclude Me" then isinclude = false end + -- return isinclude + -- end + -- ):FilterOnce() + -- BASE:I(groundset:Flush()) + --- Builds a set of groups of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_GROUP self @@ -1981,6 +2046,11 @@ do end MGroupInclude = MGroupInclude and MGroupZone end + + if self.Filter.Functions then + local MGroupFunc = self:_EvalFilterFunctions(MGroup) + MGroupInclude = MGroupInclude and MGroupFunc + end self:T2( MGroupInclude ) return MGroupInclude @@ -2158,6 +2228,7 @@ do -- SET_UNIT Countries = nil, UnitPrefixes = nil, Zones = nil, + Functions = nil, }, FilterMeta = { Coalitions = { @@ -2529,6 +2600,25 @@ do -- SET_UNIT return self end + --- [User] Add a custom condition function. + -- @function [parent=#SET_UNIT] FilterFunction + -- @param #SET_UNIT self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a UNIT object as first argument. + -- @param ... Condition function arguments if any. + -- @return #SET_UNIT self + -- @usage + -- -- Image you want to exclude a specific UNIT from a SET: + -- local groundset = SET_UNIT:New():FilterCoalitions("blue"):FilterCategories("ground"):FilterFunction( + -- -- The function needs to take a UNIT object as first - and in this case, only - argument. + -- function(unit) + -- local isinclude = true + -- if unit:GetName() == "Exclude Me" then isinclude = false end + -- return isinclude + -- end + -- ):FilterOnce() + -- BASE:I(groundset:Flush()) + + --- Handles the Database to check on an event (birth) that the Object was added in the Database. -- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! -- @param #SET_UNIT self @@ -3196,6 +3286,11 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MGroupZone end + if self.Filter.Functions then + local MUnitFunc = self:_EvalFilterFunctions(MUnit) + MUnitInclude = MUnitInclude and MUnitFunc + end + self:T2( MUnitInclude ) return MUnitInclude end @@ -3479,7 +3574,25 @@ do -- SET_STATIC end return self end - + + --- [User] Add a custom condition function. + -- @function [parent=#SET_STATIC] FilterFunction + -- @param #SET_STATIC self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a STATIC object as first argument. + -- @param ... Condition function arguments if any. + -- @return #SET_STATIC self + -- @usage + -- -- Image you want to exclude a specific CLIENT from a SET: + -- local groundset = SET_STATIC:New():FilterCoalitions("blue"):FilterActive(true):FilterFunction( + -- -- The function needs to take a STATIC object as first - and in this case, only - argument. + -- function(static) + -- local isinclude = true + -- if static:GetName() == "Exclude Me" then isinclude = false end + -- return isinclude + -- end + -- ):FilterOnce() + -- BASE:I(groundset:Flush()) + --- Builds a set of units of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_STATIC self @@ -4538,6 +4651,25 @@ do -- SET_CLIENT return AliveSet.Set or {} end + --- [User] Add a custom condition function. + -- @function [parent=#SET_CLIENT] FilterFunction + -- @param #SET_CLIENT self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a CLIENT object as first argument. + -- @param ... Condition function arguments if any. + -- @return #SET_CLIENT self + -- @usage + -- -- Image you want to exclude a specific CLIENT from a SET: + -- local groundset = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterFunction( + -- -- The function needs to take a UNIT object as first - and in this case, only - argument. + -- function(client) + -- local isinclude = true + -- if client:GetPlayerName() == "Exclude Me" then isinclude = false end + -- return isinclude + -- end + -- ):FilterOnce() + -- BASE:I(groundset:Flush()) + + --- -- @param #SET_CLIENT self -- @param Wrapper.Client#CLIENT MClient @@ -4660,6 +4792,11 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCallsigns end + if self.Filter.Functions then + local MClientFunc = self:_EvalFilterFunctions(MClient) + MClientInclude = MClientInclude and MClientFunc + end + end self:T2( MClientInclude ) return MClientInclude From bfab0fa542527f9066858a4d428280250535294f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 28 Dec 2023 13:33:06 +0100 Subject: [PATCH 458/603] xxx --- Moose Development/Moose/Cargo/Cargo.lua | 52 +++++++++---------- Moose Development/Moose/Cargo/CargoGroup.lua | 50 +++++++++--------- Moose Development/Moose/Functional/Mantis.lua | 4 +- 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 62b1c6ec0..8b7d6040e 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -447,7 +447,7 @@ do -- CARGO function CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) --R2.1 local self = BASE:Inherit( self, FSM:New() ) -- #CARGO - self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) + self:T( { Type, Name, Weight, LoadRadius, NearRadius } ) self:SetStartState( "UnLoaded" ) self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" ) @@ -711,7 +711,7 @@ do -- CARGO -- @param #CARGO self -- @return #CARGO function CARGO:Spawn( PointVec2 ) - self:F() + self:T() end @@ -812,7 +812,7 @@ do -- CARGO -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the CargoGroup is within the loading radius. function CARGO:IsInLoadRadius( Coordinate ) - self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + self:T( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then @@ -832,7 +832,7 @@ do -- CARGO -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo can report itself. function CARGO:IsInReportRadius( Coordinate ) - self:F( { Coordinate } ) + self:T( { Coordinate } ) local Distance = 0 if self:IsUnLoaded() then @@ -853,23 +853,23 @@ do -- CARGO -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). -- @return #boolean function CARGO:IsNear( Coordinate, NearRadius ) - --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) + --self:T( { PointVec2 = PointVec2, NearRadius = NearRadius } ) if self.CargoObject:IsAlive() then --local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) - --self:F( { CargoObjectName = self.CargoObject:GetName() } ) - --self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) - --self:F( { PointVec2 = PointVec2:GetVec2() } ) + --self:T( { CargoObjectName = self.CargoObject:GetName() } ) + --self:T( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) + --self:T( { PointVec2 = PointVec2:GetVec2() } ) local Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) - --self:F( { Distance = Distance, NearRadius = NearRadius or "nil" } ) + --self:T( { Distance = Distance, NearRadius = NearRadius or "nil" } ) if Distance <= NearRadius then - --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } ) + --self:T( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } ) return true end end - --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = false } ) + --self:T( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = false } ) return false end @@ -878,12 +878,12 @@ do -- CARGO -- @param Core.Zone#ZONE_BASE Zone -- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone. function CARGO:IsInZone( Zone ) - --self:F( { Zone } ) + --self:T( { Zone } ) if self:IsLoaded() then return Zone:IsPointVec2InZone( self.CargoCarrier:GetPointVec2() ) else - --self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) + --self:T( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) if self.CargoObject:GetSize() ~= 0 then return Zone:IsPointVec2InZone( self.CargoObject:GetPointVec2() ) else @@ -1056,7 +1056,7 @@ do -- CARGO_REPRESENTABLE -- Inherit CARGO. local self = BASE:Inherit( self, CARGO:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE - self:F( { Type, Name, LoadRadius, NearRadius } ) + self:T( { Type, Name, LoadRadius, NearRadius } ) -- Descriptors. local Desc=CargoObject:GetDesc() @@ -1086,7 +1086,7 @@ do -- CARGO_REPRESENTABLE function CARGO_REPRESENTABLE:Destroy() -- Cargo objects are deleted from the _DATABASE and SET_CARGO objects. - self:F( { CargoName = self:GetName() } ) + self:T( { CargoName = self:GetName() } ) --_EVENTDISPATCHER:CreateEventDeleteCargo( self ) return self @@ -1123,12 +1123,12 @@ do -- CARGO_REPRESENTABLE CoordinateZone:Scan( { Object.Category.UNIT } ) for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do local NearUnit = UNIT:Find( DCSUnit ) - self:F({NearUnit=NearUnit}) + self:T({NearUnit=NearUnit}) local NearUnitCoalition = NearUnit:GetCoalition() local CargoCoalition = self:GetCoalition() if NearUnitCoalition == CargoCoalition then local Attributes = NearUnit:GetDesc() - self:F({Desc=Attributes}) + self:T({Desc=Attributes}) if NearUnit:HasAttribute( "Trucks" ) then MESSAGE:New( Message, 20, NearUnit:GetCallsign() .. " reporting - Cargo " .. self:GetName() ):ToGroup( TaskGroup ) break @@ -1158,7 +1158,7 @@ do -- CARGO_REPORTABLE -- @return #CARGO_REPORTABLE function CARGO_REPORTABLE:New( Type, Name, Weight, LoadRadius, NearRadius ) local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_REPORTABLE - self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) + self:T( { Type, Name, Weight, LoadRadius, NearRadius } ) return self end @@ -1195,7 +1195,7 @@ do -- CARGO_PACKAGE -- @return #CARGO_PACKAGE function CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, LoadRadius, NearRadius ) local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_PACKAGE - self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) + self:T( { Type, Name, Weight, LoadRadius, NearRadius } ) self:T( CargoCarrier ) self.CargoCarrier = CargoCarrier @@ -1213,7 +1213,7 @@ end -- @param #number BoardDistance -- @param #number Angle function CARGO_PACKAGE:onafterOnBoard( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() + self:T() self.CargoInAir = self.CargoCarrier:InAir() @@ -1246,7 +1246,7 @@ end -- @param Wrapper.Unit#UNIT CargoCarrier -- @return #boolean function CARGO_PACKAGE:IsNear( CargoCarrier ) - self:F() + self:T() local CargoCarrierPoint = CargoCarrier:GetCoordinate() @@ -1271,7 +1271,7 @@ end -- @param #number LoadDistance -- @param #number Angle function CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() + self:T() if self:IsNear( CargoCarrier ) then self:__Load( 1, CargoCarrier, Speed, LoadDistance, Angle ) @@ -1292,7 +1292,7 @@ end -- @param #number Radius -- @param #number Angle function CARGO_PACKAGE:onafterUnBoard( From, Event, To, CargoCarrier, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle ) - self:F() + self:T() self.CargoInAir = self.CargoCarrier:InAir() @@ -1331,7 +1331,7 @@ end -- @param Wrapper.Unit#UNIT CargoCarrier -- @param #number Speed function CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed ) - self:F() + self:T() if self:IsNear( CargoCarrier ) then self:__UnLoad( 1, CargoCarrier, Speed ) @@ -1350,7 +1350,7 @@ end -- @param #number LoadDistance -- @param #number Angle function CARGO_PACKAGE:onafterLoad( From, Event, To, CargoCarrier, Speed, LoadDistance, Angle ) - self:F() + self:T() self.CargoCarrier = CargoCarrier @@ -1378,7 +1378,7 @@ end -- @param #number Distance -- @param #number Angle function CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle ) - self:F() + self:T() local StartPointVec2 = self.CargoCarrier:GetPointVec2() local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 5ecfee881..0cb2f1d5b 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -19,7 +19,7 @@ do -- CARGO_GROUP - -- @type CARGO_GROUP + --- @type CARGO_GROUP -- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects. -- @field #string GroupName The name of the CargoGroup. -- @extends Cargo.Cargo#CARGO_REPORTABLE @@ -64,7 +64,7 @@ do -- CARGO_GROUP -- Inherit CAROG_REPORTABLE local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP - self:F( { Type, Name, LoadRadius } ) + self:T( { Type, Name, LoadRadius } ) self.CargoSet = SET_CARGO:New() self.CargoGroup = CargoGroup @@ -146,7 +146,7 @@ do -- CARGO_GROUP -- @param #CARGO_GROUP self function CARGO_GROUP:Respawn() - self:F( { "Respawning" } ) + self:T( { "Respawning" } ) for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do local Cargo = CargoData -- Cargo.Cargo#CARGO @@ -227,7 +227,7 @@ do -- CARGO_GROUP -- @param #CARGO_GROUP self function CARGO_GROUP:Regroup() - self:F("Regroup") + self:T("Regroup") if self.Grouped == false then @@ -241,7 +241,7 @@ do -- CARGO_GROUP for CargoUnitName, CargoUnit in pairs( self.CargoSet:GetSet() ) do local CargoUnit = CargoUnit -- Cargo.CargoUnit#CARGO_UNIT - self:F( { CargoUnit:GetName(), UnLoaded = CargoUnit:IsUnLoaded() } ) + self:T( { CargoUnit:GetName(), UnLoaded = CargoUnit:IsUnLoaded() } ) if CargoUnit:IsUnLoaded() then @@ -258,7 +258,7 @@ do -- CARGO_GROUP -- Then we register the new group in the database self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID ) - self:F( { "Regroup", GroupTemplate } ) + self:T( { "Regroup", GroupTemplate } ) -- Now we spawn the new group based on the template created. self.CargoObject = _DATABASE:Spawn( GroupTemplate ) @@ -267,11 +267,11 @@ do -- CARGO_GROUP end - -- @param #CARGO_GROUP self + --- @param #CARGO_GROUP self -- @param Core.Event#EVENTDATA EventData function CARGO_GROUP:OnEventCargoDead( EventData ) - --self:E(EventData) + self:E(EventData) local Destroyed = false @@ -309,14 +309,14 @@ do -- CARGO_GROUP -- @param Wrapper.Unit#UNIT CargoCarrier -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } ) + self:T( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } ) NearRadius = NearRadius or self.NearRadius -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 self.CargoSet:ForEach( function( Cargo, ... ) - self:F( { "Board Unit", Cargo:GetName( ), Cargo:IsDestroyed(), Cargo.CargoObject:IsAlive() } ) + self:T( { "Board Unit", Cargo:GetName( ), Cargo:IsDestroyed(), Cargo.CargoObject:IsAlive() } ) local CargoGroup = Cargo.CargoObject --Wrapper.Group#GROUP CargoGroup:OptionAlarmStateGreen() Cargo:__Board( 1, CargoCarrier, NearRadius, ... ) @@ -334,7 +334,7 @@ do -- CARGO_GROUP -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_GROUP:onafterLoad( From, Event, To, CargoCarrier, ... ) - --self:F( { From, Event, To, CargoCarrier, ...} ) + --self:T( { From, Event, To, CargoCarrier, ...} ) if From == "UnLoaded" then -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. @@ -359,7 +359,7 @@ do -- CARGO_GROUP -- @param Wrapper.Unit#UNIT CargoCarrier -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - --self:F( { CargoCarrier.UnitName, From, Event, To } ) + --self:T( { CargoCarrier.UnitName, From, Event, To } ) local Boarded = true local Cancelled = false @@ -393,7 +393,7 @@ do -- CARGO_GROUP if not Boarded then self:__Boarding( -5, CargoCarrier, NearRadius, ... ) else - self:F("Group Cargo is loaded") + self:T("Group Cargo is loaded") self:__Load( 1, CargoCarrier, ... ) end else @@ -413,7 +413,7 @@ do -- CARGO_GROUP -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterUnBoard( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( {From, Event, To, ToPointVec2, NearRadius } ) + self:T( {From, Event, To, ToPointVec2, NearRadius } ) NearRadius = NearRadius or 25 @@ -427,7 +427,7 @@ do -- CARGO_GROUP -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 self.CargoSet:ForEach( - -- @param Cargo.Cargo#CARGO Cargo + --- @param Cargo.Cargo#CARGO Cargo function( Cargo, NearRadius ) if not Cargo:IsDestroyed() then local ToVec=nil @@ -456,7 +456,7 @@ do -- CARGO_GROUP -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - --self:F( { From, Event, To, ToPointVec2, NearRadius } ) + --self:T( { From, Event, To, ToPointVec2, NearRadius } ) --local NearRadius = NearRadius or 25 @@ -493,7 +493,7 @@ do -- CARGO_GROUP -- @param #string To -- @param Core.Point#POINT_VEC2 ToPointVec2 function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... ) - --self:F( { From, Event, To, ToPointVec2 } ) + --self:T( { From, Event, To, ToPointVec2 } ) if From == "Loaded" then @@ -611,7 +611,7 @@ do -- CARGO_GROUP -- @param #CARGO_GROUP self -- @param Core.Point#COORDINATE Coordinate function CARGO_GROUP:RouteTo( Coordinate ) - --self:F( {Coordinate = Coordinate } ) + --self:T( {Coordinate = Coordinate } ) -- For each Cargo within the CargoSet, route each object to the Coordinate self.CargoSet:ForEach( @@ -629,13 +629,13 @@ do -- CARGO_GROUP -- @param #number NearRadius -- @return #boolean The Cargo is near to the Carrier or #nil if the Cargo is not near to the Carrier. function CARGO_GROUP:IsNear( CargoCarrier, NearRadius ) - self:F( {NearRadius = NearRadius } ) + self:T( {NearRadius = NearRadius } ) for _, Cargo in pairs( self.CargoSet:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO if Cargo:IsAlive() then if Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) then - self:F( "Near" ) + self:T( "Near" ) return true end end @@ -649,7 +649,7 @@ do -- CARGO_GROUP -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Group is within the load radius. function CARGO_GROUP:IsInLoadRadius( Coordinate ) - --self:F( { Coordinate } ) + --self:T( { Coordinate } ) local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO @@ -669,7 +669,7 @@ do -- CARGO_GROUP return false end - self:F( { Distance = Distance, LoadRadius = self.LoadRadius } ) + self:T( { Distance = Distance, LoadRadius = self.LoadRadius } ) if Distance <= self.LoadRadius then return true else @@ -687,12 +687,12 @@ do -- CARGO_GROUP -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Group is within the report radius. function CARGO_GROUP:IsInReportRadius( Coordinate ) - --self:F( { Coordinate } ) + --self:T( { Coordinate } ) local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO if Cargo then - self:F( { Cargo } ) + self:T( { Cargo } ) local Distance = 0 if Cargo:IsUnLoaded() then Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() ) @@ -738,7 +738,7 @@ do -- CARGO_GROUP -- @return #boolean **true** if the first element of the CargoGroup is in the Zone -- @return #boolean **false** if there is no element of the CargoGroup in the Zone. function CARGO_GROUP:IsInZone( Zone ) - --self:F( { Zone } ) + --self:T( { Zone } ) local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index ce95806b1..1ebe4124e 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -94,7 +94,7 @@ -- Known SAM types at the time of writing are: -- -- * Avenger --- * Chaparrel +-- * Chaparral -- * Hawk -- * Linebacker -- * NASAMS @@ -365,7 +365,7 @@ MANTIS.SamData = { ["SA-15"] = { Range=11, Blindspot=0, Height=6, Type="Short", Radar="Tor 9A331" }, ["SA-13"] = { Range=5, Blindspot=0, Height=3, Type="Short", Radar="Strela" }, ["Avenger"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Avenger" }, - ["Chaparrel"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" }, + ["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" }, ["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Linebacker" }, ["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" }, -- units from HDS Mod, multi launcher options is tricky From c11ca9df4dcd2e3d35e4f5f603f9c86d681dd286 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 29 Dec 2023 14:51:19 +0100 Subject: [PATCH 459/603] xxx --- Moose Development/Moose/Functional/Mantis.lua | 8 ++++---- Moose Development/Moose/Functional/Sead.lua | 8 ++++---- Moose Development/Moose/Wrapper/Controllable.lua | 10 +++++++++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 1ebe4124e..a9c4ddd79 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -22,7 +22,7 @@ -- @module Functional.Mantis -- @image Functional.Mantis.jpg -- --- Last Update: Nov 2023 +-- Last Update: Dec 2023 ------------------------------------------------------------------------- --- **MANTIS** class, extends Core.Base#BASE @@ -631,7 +631,7 @@ do -- TODO Version -- @field #string version - self.version="0.8.15" + self.version="0.8.16" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) --- FSM Functions --- @@ -1149,7 +1149,7 @@ do --self:T(self.lid.." Relocating HQ") local text = self.lid.." Relocating HQ" --local m= MESSAGE:New(text,10,"MANTIS"):ToAll() - _hqgrp:RelocateGroundRandomInRadius(20,500,true,true) + _hqgrp:RelocateGroundRandomInRadius(20,500,true,true,nil,true) end --relocate EWR -- TODO: maybe dependent on AlarmState? Observed: SA11 SR only relocates if no objects in reach @@ -1163,7 +1163,7 @@ do local text = self.lid.." Relocating EWR ".._grp:GetName() local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) if self.verbose then self:I(text) end - _grp:RelocateGroundRandomInRadius(20,500,true,true) + _grp:RelocateGroundRandomInRadius(20,500,true,true,nil,true) end end end diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 07fb312c5..4c02b7b7f 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -17,9 +17,9 @@ -- -- === -- --- ### Authors: **FlightControl**, **applevangelist** +-- ### Authors: **applevangelist**, **FlightControl** -- --- Last Update: Oct 2023 +-- Last Update: Dec 2023 -- -- === -- @@ -144,7 +144,7 @@ function SEAD:New( SEADGroupPrefixes, Padding ) self:AddTransition("*", "ManageEvasion", "*") self:AddTransition("*", "CalculateHitZone", "*") - self:I("*** SEAD - Started Version 0.4.5") + self:I("*** SEAD - Started Version 0.4.6") return self end @@ -401,7 +401,7 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP grp:EnableEmission(false) end grp:OptionAlarmStateGreen() -- needed else we cannot move around - grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond") + grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond",true) if self.UseCallBack then local object = self.CallBack object:SeadSuppressionStart(grp,name,attacker) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index c3d11f34d..67a3cdcfe 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -4025,14 +4025,22 @@ end -- @param #boolean onroad If true, route on road (less problems with AI way finding), default true -- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false -- @param #string formation Formation string as in the mission editor, e.g. "Vee", "Diamond", "Line abreast", etc. Defaults to "Off Road" +-- @param #boolean onland (optional) If true, try up to 50 times to get a coordinate on land.SurfaceType.LAND. Note - this descriptor value is not reliably implemented on all maps. -- @return #CONTROLLABLE self -function CONTROLLABLE:RelocateGroundRandomInRadius( speed, radius, onroad, shortcut, formation ) +function CONTROLLABLE:RelocateGroundRandomInRadius( speed, radius, onroad, shortcut, formation, onland ) self:F2( { self.ControllableName } ) local _coord = self:GetCoordinate() local _radius = radius or 500 local _speed = speed or 20 local _tocoord = _coord:GetRandomCoordinateInRadius( _radius, 100 ) + if onland then + for i=1,50 do + local island = _tocoord:GetSurfaceType() == land.SurfaceType.LAND and true or false + if island then break end + _tocoord = _coord:GetRandomCoordinateInRadius( _radius, 100 ) + end + end local _onroad = onroad or true local _grptsk = {} local _candoroad = false From 3a4c5e307cce14e80d8540330a45f3734a8b1644 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 29 Dec 2023 15:02:20 +0100 Subject: [PATCH 460/603] AIRBOSS - Superfluous error message removal --- Moose Development/Moose/Ops/Airboss.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 75b621966..c3c6c9f2a 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -8212,7 +8212,7 @@ function AIRBOSS:OnEventBirth( EventData ) self:E( EventData ) return end - if EventData.IniUnit == nil then + if EventData.IniUnit == nil and (not EventData.IniObjectCategory == Object.Category.STATIC) then self:E( self.lid .. "ERROR: EventData.IniUnit=nil in event BIRTH!" ) self:E( EventData ) return From 596334dab921c1ce095af89b963de304cb557d72 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 30 Dec 2023 16:48:17 +0100 Subject: [PATCH 461/603] TIRESIAS --- .../Moose/Functional/Tiresias.lua | 388 ++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 Moose Development/Moose/Functional/Tiresias.lua diff --git a/Moose Development/Moose/Functional/Tiresias.lua b/Moose Development/Moose/Functional/Tiresias.lua new file mode 100644 index 000000000..ba7d8ddcb --- /dev/null +++ b/Moose Development/Moose/Functional/Tiresias.lua @@ -0,0 +1,388 @@ +--- **Functional** - TIRESIAS +-- +-- === +-- +-- ## Features: +-- +-- * Tbd +-- +-- === +-- +-- ## Missions: +-- +-- ### [TIRESIAS](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master) +-- +-- === +-- +-- ### Author : **applevangelist ** +-- +-- @module Functional.Tiresias +-- @image Functional.Tiresias.jpg +-- +-- Last Update: Dec 2023 + +------------------------------------------------------------------------- +--- **TIRESIAS** class, extends Core.Base#BASE +-- @type TIRESIAS +-- @field #string ClassName +-- @field #booelan debug +-- @field #string version +-- @field #number Interval +-- @field Core.Set#SET_CLIENT PilotSet +-- @field Core.Set#SET_GROUP GroundSet +-- @field #string CoalitionText +-- @field #number Coalition +-- @field Core.Set#SET_GROUP VehicleSet +-- @field Core.Set#SET_GROUP AAASet +-- @field Core.Set#SET_GROUP SAMSet +-- @field #number AAARange +-- @field #number HeloSwitchRange +-- @field #number PlaneSwitchRange +-- @field Core.Set#SET_GROUP FlightSet +-- @extends Core.Fsm#FSM + +--- +-- @type TIRESIAS.Data +-- @field #string type +-- @field #number range +-- @field #boolean invisible +-- @field #boolean AIOff + + +--- +-- # Documentation +-- +-- @field TIRESIAS +TIRESIAS = { + ClassName = "TIRESIAS", + debug = true, + version = "0.0.1", + Interval = 20, + PilotSet = nil, + GroundSet = nil, + CoalitionText = "blue", + Coalition = coalition.side.BLUE, + VehicleSet = nil, + AAASet = nil, + SAMSet = nil, + AAARange = 60, + HeloSwitchRange = 10, -- NM + PlaneSwitchRange = 25, -- NM +} + +--- +-- @param #TIRESIAS self +-- @return #TIRESIAS self +function TIRESIAS:New() + + -- Inherit everything from FSM class. + local self = BASE:Inherit(self, FSM:New()) -- #TIRESIAS + + self.PilotSet = SET_CLIENT:New():FilterActive(true):FilterCoalitions(self.CoalitionText):FilterStart() + + --- FSM Functions --- + + -- Start State. + self:SetStartState("Stopped") + + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("Stopped", "Start", "Running") -- Start FSM. + self:AddTransition("*", "Status", "*") -- TIRESIAS status update. + self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. + + self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) + + self.lid = string.format("TIRESIAS %s | ",self.version) + + self:I(self.lid.."Managing ground groups!") + + self:__Start(1) + return self +end + +------------------------------------------------------------------------------------------------------------- +-- +-- Helper Functions +-- +------------------------------------------------------------------------------------------------------------- + +--- +-- @param #TIRESIAS self +-- @param Wrapper.Group#GROUP Group +-- @return #boolean isin +function TIRESIAS._FilterAAA(Group) + local grp = Group -- Wrapper.Group#GROUP + local isaaa = grp:IsAAA() + if isaaa == true and grp:IsGround() then + return false -- remove from SET + else + return true -- keep in SET + end +end + +--- +-- @param #TIRESIAS self +-- @param Wrapper.Group#GROUP Group +-- @return #boolean isin +function TIRESIAS._FilterSAM(Group) + local grp = Group -- Wrapper.Group#GROUP + local issam = grp:IsSAM() + if issam == true and grp:IsGround() then + return false -- remove from SET + else + return true -- keep in SET + end +end + +--- +-- @param #TIRESIAS self +-- @return #TIRESIAS self +function TIRESIAS:_InitGroups() + self:I(self.lid.."_InitGroups") + -- Set all groups invisible/motionless + local EngageRange = self.AAARange + self.AAASet:ForEachGroupAlive( + function(grp) + if not grp.Tiresias then + grp:OptionEngageRange(EngageRange) + grp:SetCommandInvisible(true) + grp.Tiresias = { -- #TIRESIAS.Data + type = "AAA", + invisible = true, + range = EngageRange, + } + end + if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then + grp:SetCommandInvisible(true) + end + end + ) + self.VehicleSet:ForEachGroupAlive( + function(grp) + if not grp.Tiresias then + grp:SetAIOff() + grp:SetCommandInvisible(true) + grp.Tiresias = { -- #TIRESIAS.Data + type = "Vehicle", + invisible = true, + AIOff = true, + } + end + if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then + grp:SetCommandInvisible(true) + grp:SetAIOff() + end + end + ) + self.SAMSet:ForEachGroupAlive( + function(grp) + if not grp.Tiresias then + grp:SetCommandInvisible(true) + grp.Tiresias = { -- #TIRESIAS.Data + type = "SAM", + invisible = true, + } + end + if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then + grp:SetCommandInvisible(true) + end + end + ) + return self +end + +--- (Internal) Event handler function +-- @param #TIRESIAS self +-- @param Core.Event#EVENTDATA EventData +-- @return #TIRESIAS self +function TIRESIAS:_EventHandler(EventData) + self:I(string.format("%s Event = %d",self.lid, EventData.id)) + local event = EventData -- Core.Event#EVENTDATA + if event.id == EVENTS.PlayerEnterAircraft or event.id == EVENTS.PlayerEnterUnit then + local _coalition = event.IniCoalition + if _coalition ~= self.Coalition then + return --ignore! + end + local unitname = event.IniUnitName or "none" + local _unit = event.IniUnit + local _group = event.IniGroup + if _group and _group:IsAlive() then + local radius = self.PlaneSwitchRange + if _group:IsHelicopter() then + radius = self.HeloSwitchRange + end + self:_SwitchOnGroups(_group,radius) + end + end + return self +end + +--- +-- @param #TIRESIAS self +-- @param Wrapper.Group#GROUP group +-- @param #number radius Radius in NM +-- @return #TIRESIAS self +function TIRESIAS:_SwitchOnGroups(group,radius) + self:I(self.lid.."_SwitchOnGroups "..group:GetName().." Radius "..radius.." NM") + local zone = ZONE_GROUP:New("Zone-"..group:GetName(),group,UTILS.NMToMeters(radius)) + local ground = SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce() + local count = ground:CountAlive() + if self.debug then + local text = string.format("There are %d groups around this plane or helo!",count) + self:I(text) + end + if ground:CountAlive() > 0 then + ground:ForEachGroupAlive( + function(grp) + if grp.Tiresias and grp.Tiresias.type then + if grp.Tiresias.invisible == true then + grp:SetCommandInvisible(false) + grp.Tiresias.invisible = false + end + if grp.Tiresias.type == "Vehicle" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == false then + grp:SetAIOn() + grp.Tiresias.AIOff = false + end + else + BASE:I("TIRESIAS - This group has not been initialized!") + end + end + ) + end + return self +end + +------------------------------------------------------------------------------------------------------------- +-- +-- FSM Functions +-- +------------------------------------------------------------------------------------------------------------- + +--- +-- @param #TIRESIAS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TIRESIAS self +function TIRESIAS:onafterStart(From, Event, To) + self:I({From, Event, To}) + + local VehicleSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterAAA):FilterFunction(TIRESIAS._FilterSAM):FilterStart() + local AAASet = SET_GROUP:New():FilterCategoryGround():FilterFunction(function(grp) return grp:IsAAA() end):FilterStart() + local SAMSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(function(grp) return grp:IsSAM() end):FilterStart() + self.FlightSet = SET_GROUP:New():FilterCategories({"plane","helicopter"}):FilterStart() + + local EngageRange = self.AAARange + + function VehicleSet:OnAfterAdded(From,Event,To,ObjectName,Object) + BASE:I("TIRESIAS: VEHCILE Object Added: "..Object:GetName()) + if Object and Object:IsAlive() then + Object:SetAIOff() + Object:SetCommandInvisible(true) + Object.Tiresias = { -- #TIRESIAS.Data + type = "Vehicle", + invisible = true, + AIOff = true, + } + end + end + + function AAASet:OnAfterAdded(From,Event,To,ObjectName,Object) + if Object and Object:IsAlive() then + BASE:I("TIRESIAS: AAA Object Added: "..Object:GetName()) + Object:OptionEngageRange(EngageRange) + Object:SetCommandInvisible(true) + Object.Tiresias = { -- #TIRESIAS.Data + type = "AAA", + invisible = true, + range = EngageRange, + } + end + end + + function SAMSet:OnAfterAdded(From,Event,To,ObjectName,Object) + if Object and Object:IsAlive() then + BASE:I("TIRESIAS: SAM Object Added: "..Object:GetName()) + Object:SetCommandInvisible(true) + Object.Tiresias = { -- #TIRESIAS.Data + type = "SAM", + invisible = true, + } + end + end + + self.VehicleSet = VehicleSet + self.AAASet = AAASet + self.SAMSet = SAMSet + + self:_InitGroups() + + self:__Status(1) + return self +end + +--- +-- @param #TIRESIAS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TIRESIAS self +function TIRESIAS:onbeforeStatus(From, Event, To) + self:T({From, Event, To}) + if self:GetState() == "Stopped" then + return false + end + return self +end + +--- +-- @param #TIRESIAS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TIRESIAS self +function TIRESIAS:onafterStatus(From, Event, To) + self:I({From, Event, To}) + if self.debug then + local count = self.VehicleSet:CountAlive() + local AAAcount = self.AAASet:CountAlive() + local SAMcount = self.SAMSet:CountAlive() + local text = string.format("Overall: %d | Vehicles: %d | AAA: %d | SAM: %d",count+AAAcount+SAMcount,count,AAAcount,SAMcount) + self:I(text) + end + self:_InitGroups() + if self.FlightSet:CountAlive() > 0 then + local Set = self.FlightSet:GetAliveSet() + for _,_plane in pairs(Set) do + local plane = _plane -- Wrapper.Group#GROUP + local radius = self.PlaneSwitchRange + if plane:IsHelicopter() then + radius = self.HeloSwitchRange + end + self:_SwitchOnGroups(_plane,radius) + end + end + if self:GetState() ~= "Stopped" then + self:__Status(self.Interval) + end + return self +end + +--- +-- @param #TIRESIAS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TIRESIAS self +function TIRESIAS:onafterStop(From, Event, To) + self:I({From, Event, To}) + self:UnHandleEvent(EVENTS.PlayerEnterAircraft) + return self +end + +------------------------------------------------------------------------------------------------------------- +-- +-- End +-- +------------------------------------------------------------------------------------------------------------- From e3a577ad24f00e2834c73c4b9b737839bb4d0424 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 30 Dec 2023 16:48:48 +0100 Subject: [PATCH 462/603] xxx --- Moose Development/Moose/Cargo/CargoGroup.lua | 2 +- .../Moose/Cargo/CargoSlingload.lua | 16 ++++---- Moose Development/Moose/Cargo/CargoUnit.lua | 24 ++++++------ Moose Development/Moose/Core/Set.lua | 24 +++++------- Moose Development/Moose/Modules_local.lua | 1 + Moose Development/Moose/Wrapper/Group.lua | 38 ++++++++++++++++++- 6 files changed, 67 insertions(+), 38 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 0cb2f1d5b..095ee3aac 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -271,7 +271,7 @@ do -- CARGO_GROUP -- @param Core.Event#EVENTDATA EventData function CARGO_GROUP:OnEventCargoDead( EventData ) - self:E(EventData) + self:T(EventData) local Destroyed = false diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 13d07c2ee..81bc5d95e 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -52,7 +52,7 @@ do -- CARGO_SLINGLOAD -- @return #CARGO_SLINGLOAD function CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_SLINGLOAD - self:F( { Type, Name, NearRadius } ) + self:T( { Type, Name, NearRadius } ) self.CargoObject = CargoStatic @@ -130,7 +130,7 @@ do -- CARGO_SLINGLOAD -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Crate is within the report radius. function CARGO_SLINGLOAD:IsInReportRadius( Coordinate ) - --self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + --self:T( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then @@ -149,7 +149,7 @@ do -- CARGO_SLINGLOAD -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Slingload is within the loading radius. function CARGO_SLINGLOAD:IsInLoadRadius( Coordinate ) - --self:F( { Coordinate } ) + --self:T( { Coordinate } ) local Distance = 0 if self:IsUnLoaded() then @@ -169,7 +169,7 @@ do -- CARGO_SLINGLOAD -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. -- @return #nil There is no valid Cargo in the CargoGroup. function CARGO_SLINGLOAD:GetCoordinate() - --self:F() + --self:T() return self.CargoObject:GetCoordinate() end @@ -199,7 +199,7 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self -- @param Core.Point#COORDINATE Coordinate function CARGO_SLINGLOAD:RouteTo( Coordinate ) - --self:F( {Coordinate = Coordinate } ) + --self:T( {Coordinate = Coordinate } ) end @@ -212,7 +212,7 @@ do -- CARGO_SLINGLOAD -- @return #boolean The Cargo is near to the Carrier. -- @return #nil The Cargo is not near to the Carrier. function CARGO_SLINGLOAD:IsNear( CargoCarrier, NearRadius ) - --self:F( {NearRadius = NearRadius } ) + --self:T( {NearRadius = NearRadius } ) return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) end @@ -222,7 +222,7 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self function CARGO_SLINGLOAD:Respawn() - --self:F( { "Respawning slingload " .. self:GetName() } ) + --self:T( { "Respawning slingload " .. self:GetName() } ) -- Respawn the group... @@ -239,7 +239,7 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self function CARGO_SLINGLOAD:onafterReset() - --self:F( { "Reset slingload " .. self:GetName() } ) + --self:T( { "Reset slingload " .. self:GetName() } ) -- Respawn the group... diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 830f02662..5f92b7a88 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -75,7 +75,7 @@ do -- CARGO_UNIT -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius (optional) Defaut 25 m. function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) + self:T( { From, Event, To, ToPointVec2, NearRadius } ) local Angle = 180 local Speed = 60 @@ -114,7 +114,7 @@ do -- CARGO_UNIT else self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading ) end - self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) + self:T( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) self.CargoCarrier = nil local Points = {} @@ -148,7 +148,7 @@ do -- CARGO_UNIT -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius (optional) Defaut 100 m. function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) + self:T( { From, Event, To, ToPointVec2, NearRadius } ) local Angle = 180 local Speed = 10 @@ -174,7 +174,7 @@ do -- CARGO_UNIT -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius (optional) Defaut 100 m. function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) + self:T( { From, Event, To, ToPointVec2, NearRadius } ) self.CargoInAir = self.CargoObject:InAir() @@ -199,7 +199,7 @@ do -- CARGO_UNIT -- @param #string To -- @param Core.Point#POINT_VEC2 function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) + self:T( { ToPointVec2, From, Event, To } ) local Angle = 180 local Speed = 10 @@ -236,7 +236,7 @@ do -- CARGO_UNIT -- @param Wrapper.Group#GROUP CargoCarrier -- @param #number NearRadius function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier, NearRadius = NearRadius } ) + self:T( { From, Event, To, CargoCarrier, NearRadius = NearRadius } ) self.CargoInAir = self.CargoObject:InAir() @@ -244,7 +244,7 @@ do -- CARGO_UNIT local MaxSpeed = Desc.speedMaxOffRoad local TypeName = Desc.typeName - --self:F({Unit=self.CargoObject:GetName()}) + --self:T({Unit=self.CargoObject:GetName()}) -- A cargo unit can only be boarded if it is not dead @@ -298,9 +298,9 @@ do -- CARGO_UNIT -- @param Wrapper.Client#CLIENT CargoCarrier -- @param #number NearRadius Default 25 m. function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier:GetName(), NearRadius = NearRadius } ) + self:T( { From, Event, To, CargoCarrier:GetName(), NearRadius = NearRadius } ) - self:F( { IsAlive=self.CargoObject:IsAlive() } ) + self:T( { IsAlive=self.CargoObject:IsAlive() } ) if CargoCarrier and CargoCarrier:IsAlive() then -- and self.CargoObject and self.CargoObject:IsAlive() then if (CargoCarrier:IsAir() and not CargoCarrier:InAir()) or true then @@ -321,7 +321,7 @@ do -- CARGO_UNIT local Angle = 180 local Distance = 0 - --self:F({Unit=self.CargoObject:GetName()}) + --self:T({Unit=self.CargoObject:GetName()}) local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. @@ -361,11 +361,11 @@ do -- CARGO_UNIT -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier ) - self:F( { From, Event, To, CargoCarrier } ) + self:T( { From, Event, To, CargoCarrier } ) self.CargoCarrier = CargoCarrier - --self:F({Unit=self.CargoObject:GetName()}) + --self:T({Unit=self.CargoObject:GetName()}) -- Only destroy the CargoObject if there is a CargoObject (packages don't have CargoObjects). if self.CargoObject then diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index d0670abd6..ba08c103b 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -171,25 +171,18 @@ do -- SET_BASE --- [Internal] Check if the condition functions returns true. -- @param #SET_BASE self -- @param Wrapper.Controllable#CONTROLLABLE Object The object to filter for - -- @return #boolean If true, at least one condition is true. - function SET_BASE:_EvalFilterFunctions(Object) - - -- Any one condition must be true. + -- @return #boolean If true, if **all** conditions are true + function SET_BASE:_EvalFilterFunctions(Object) + -- All conditions must be true. for _,_condition in pairs(self.Filter.Functions or {}) do local condition=_condition - -- Call function. - local istrue=condition.func(Object,unpack(condition.arg)) - - -- Any true will return true. - if istrue then - return true + if condition.func(Object,unpack(condition.arg)) == false then + return false end - - end - + end -- No condition was true. - return false + return true end --- Clear the Objects in the Set. @@ -2049,7 +2042,8 @@ do end if self.Filter.Functions then - local MGroupFunc = self:_EvalFilterFunctions(MGroup) + local MGroupFunc = false + MGroupFunc = self:_EvalFilterFunctions(MGroup) MGroupInclude = MGroupInclude and MGroupFunc end diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index e7f671abe..7a9c03cf2 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -79,6 +79,7 @@ __Moose.Include( 'Functional\\Shorad.lua' ) __Moose.Include( 'Functional\\Autolase.lua' ) __Moose.Include( 'Functional\\AICSAR.lua' ) __Moose.Include( 'Functional\\AmmoTruck.lua' ) +__Moose.Include( 'Functional\\Tiresias.lua' ) __Moose.Include( 'Ops\\Airboss.lua' ) __Moose.Include( 'Ops\\RecoveryTanker.lua' ) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 9d52bc287..177ff3cde 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2763,7 +2763,7 @@ end --- Switch on/off invisible flag for the group. -- @param #GROUP self --- @param #boolean switch If true, emission is enabled. If false, emission is disabled. +-- @param #boolean switch If true, Invisible is enabled. If false, Invisible is disabled. -- @return #GROUP self function GROUP:SetCommandInvisible(switch) self:F2( self.GroupName ) @@ -2777,7 +2777,7 @@ end --- Switch on/off immortal flag for the group. -- @param #GROUP self --- @param #boolean switch If true, emission is enabled. If false, emission is disabled. +-- @param #boolean switch If true, Immortal is enabled. If false, Immortal is disabled. -- @return #GROUP self function GROUP:SetCommandImmortal(switch) self:F2( self.GroupName ) @@ -2982,3 +2982,37 @@ function GROUP:GetGroupSTN() local text = report:Text() return tSTN,text end + +--- [GROUND] Determine if a GROUP is a SAM unit, i.e. has radar or optical tracker and is no mobile AAA. +-- @param #GROUP self +-- @return #boolean IsSAM True if SAM, else false +function GROUP:IsSAM() + local issam = false + local units = self:GetUnits() + for _,_unit in pairs(units or {}) do + local unit = _unit -- Wrapper.Unit#UNIT + if unit:HasSEAD() and unit:IsGround() and (not unit:HasAttribute("Mobile AAA")) then + issam = true + break + end + end + return issam +end + +--- [GROUND] Determine if a GROUP is a AAA unit, i.e. has no radar or optical tracker but the AAA = true or the "Mobile AAA" = true attribute. +-- @param #GROUP self +-- @return #boolean IsSAM True if AAA, else false +function GROUP:IsAAA() + local issam = true + local units = self:GetUnits() + for _,_unit in pairs(units or {}) do + local unit = _unit -- Wrapper.Unit#UNIT + if unit:HasSEAD() or (not unit:IsGround()) then + issam = false + if unit:HasAttribute("Mobile AAA") then + issam = true + end + end + end + return issam +end \ No newline at end of file From e1ac5633f901522006748915eec6834d67e335da Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 30 Dec 2023 16:48:59 +0100 Subject: [PATCH 463/603] xxx --- Moose Development/Moose/Cargo/CargoCrate.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 2548c28a6..430032d0b 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -59,7 +59,7 @@ do -- CARGO_CRATE -- @return #CARGO_CRATE function CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_CRATE - self:F( { Type, Name, NearRadius } ) + self:T( { Type, Name, NearRadius } ) self.CargoObject = CargoStatic -- Wrapper.Static#STATIC @@ -116,7 +116,7 @@ do -- CARGO_CRATE -- @param #string To -- @param Core.Point#POINT_VEC2 function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 ) - --self:F( { ToPointVec2, From, Event, To } ) + --self:T( { ToPointVec2, From, Event, To } ) local Angle = 180 local Speed = 10 @@ -153,7 +153,7 @@ do -- CARGO_CRATE -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_CRATE:onenterLoaded( From, Event, To, CargoCarrier ) - --self:F( { From, Event, To, CargoCarrier } ) + --self:T( { From, Event, To, CargoCarrier } ) self.CargoCarrier = CargoCarrier @@ -190,7 +190,7 @@ do -- CARGO_CRATE -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Crate is within the report radius. function CARGO_CRATE:IsInReportRadius( Coordinate ) - --self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + --self:T( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then @@ -210,7 +210,7 @@ do -- CARGO_CRATE -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Crate is within the loading radius. function CARGO_CRATE:IsInLoadRadius( Coordinate ) - --self:F( { Coordinate, LoadRadius = self.NearRadius } ) + --self:T( { Coordinate, LoadRadius = self.NearRadius } ) local Distance = 0 if self:IsUnLoaded() then @@ -231,7 +231,7 @@ do -- CARGO_CRATE -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. -- @return #nil There is no valid Cargo in the CargoGroup. function CARGO_CRATE:GetCoordinate() - --self:F() + --self:T() return self.CargoObject:GetCoordinate() end @@ -261,7 +261,7 @@ do -- CARGO_CRATE -- @param #CARGO_CRATE self -- @param Core.Point#COORDINATE Coordinate function CARGO_CRATE:RouteTo( Coordinate ) - self:F( {Coordinate = Coordinate } ) + self:T( {Coordinate = Coordinate } ) end @@ -274,7 +274,7 @@ do -- CARGO_CRATE -- @return #boolean The Cargo is near to the Carrier. -- @return #nil The Cargo is not near to the Carrier. function CARGO_CRATE:IsNear( CargoCarrier, NearRadius ) - self:F( {NearRadius = NearRadius } ) + self:T( {NearRadius = NearRadius } ) return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) end @@ -283,7 +283,7 @@ do -- CARGO_CRATE -- @param #CARGO_CRATE self function CARGO_CRATE:Respawn() - self:F( { "Respawning crate " .. self:GetName() } ) + self:T( { "Respawning crate " .. self:GetName() } ) -- Respawn the group... @@ -300,7 +300,7 @@ do -- CARGO_CRATE -- @param #CARGO_CRATE self function CARGO_CRATE:onafterReset() - self:F( { "Reset crate " .. self:GetName() } ) + self:T( { "Reset crate " .. self:GetName() } ) -- Respawn the group... From fea05b552c5fe32cea6de26e55e02b1c4143f0ce Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 31 Dec 2023 14:55:57 +0100 Subject: [PATCH 464/603] xxx --- .../Moose/Functional/Tiresias.lua | 146 ++++++++++++++---- Moose Development/Moose/Wrapper/Group.lua | 14 +- 2 files changed, 122 insertions(+), 38 deletions(-) diff --git a/Moose Development/Moose/Functional/Tiresias.lua b/Moose Development/Moose/Functional/Tiresias.lua index ba7d8ddcb..d6df56c76 100644 --- a/Moose Development/Moose/Functional/Tiresias.lua +++ b/Moose Development/Moose/Functional/Tiresias.lua @@ -28,17 +28,17 @@ -- @field #booelan debug -- @field #string version -- @field #number Interval --- @field Core.Set#SET_CLIENT PilotSet -- @field Core.Set#SET_GROUP GroundSet --- @field #string CoalitionText -- @field #number Coalition -- @field Core.Set#SET_GROUP VehicleSet -- @field Core.Set#SET_GROUP AAASet -- @field Core.Set#SET_GROUP SAMSet +-- @field Core.Set#SET_GROUP ExceptionSet -- @field #number AAARange -- @field #number HeloSwitchRange -- @field #number PlaneSwitchRange -- @field Core.Set#SET_GROUP FlightSet +-- @field #boolean SwitchAAA -- @extends Core.Fsm#FSM --- @@ -47,27 +47,28 @@ -- @field #number range -- @field #boolean invisible -- @field #boolean AIOff +-- @field #boolean exception --- -- # Documentation -- --- @field TIRESIAS +-- @field #TIRESIAS TIRESIAS = { ClassName = "TIRESIAS", - debug = true, - version = "0.0.1", + debug = false, + version = "0.0.2", Interval = 20, - PilotSet = nil, GroundSet = nil, - CoalitionText = "blue", Coalition = coalition.side.BLUE, VehicleSet = nil, AAASet = nil, SAMSet = nil, - AAARange = 60, + ExceptionSet = nil, + AAARange = 60, -- 60% HeloSwitchRange = 10, -- NM PlaneSwitchRange = 25, -- NM + SwitchAAA = true, } --- @@ -78,8 +79,6 @@ function TIRESIAS:New() -- Inherit everything from FSM class. local self = BASE:Inherit(self, FSM:New()) -- #TIRESIAS - self.PilotSet = SET_CLIENT:New():FilterActive(true):FilterCoalitions(self.CoalitionText):FilterStart() - --- FSM Functions --- -- Start State. @@ -107,6 +106,29 @@ end -- ------------------------------------------------------------------------------------------------------------- +--- +-- @param #TIRESIAS self +-- @param Core.Set#SET_GROUP Set +-- @return #TIRESIAS self +function TIRESIAS:AddExceptionSet(Set) + self:T(self.lid.."AddExceptionSet") + self.ExceptionSet = Set + + Set:ForEachGroupAlive( + function(grp) + if not grp.Tiresias then + grp.Tiresias = { -- #TIRESIAS.Data + type = "Exception", + exception = true, + } + end + BASE:I("TIRESIAS: Added exception group: "..grp:GetName()) + end + ) + + return self +end + --- -- @param #TIRESIAS self -- @param Wrapper.Group#GROUP Group @@ -139,25 +161,43 @@ end -- @param #TIRESIAS self -- @return #TIRESIAS self function TIRESIAS:_InitGroups() - self:I(self.lid.."_InitGroups") + self:T(self.lid.."_InitGroups") -- Set all groups invisible/motionless local EngageRange = self.AAARange + local SwitchAAA = self.SwitchAAA + --- AAA self.AAASet:ForEachGroupAlive( function(grp) if not grp.Tiresias then grp:OptionEngageRange(EngageRange) grp:SetCommandInvisible(true) + if SwitchAAA then + grp:SetAIOff() + grp:EnableEmission(false) + end grp.Tiresias = { -- #TIRESIAS.Data type = "AAA", invisible = true, range = EngageRange, + exception = false, + AIOff = SwitchAAA, } end - if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then - grp:SetCommandInvisible(true) + if grp.Tiresias and (not grp.Tiresias.exception == true) then + if grp.Tiresias.invisible and grp.Tiresias.invisible == false then + grp:SetCommandInvisible(true) + grp.Tiresias.invisible = true + if SwitchAAA then + grp:SetAIOff() + grp:EnableEmission(false) + grp.Tiresias.AIOff = true + end + end end + --BASE:I(string.format("Init/Switch off AAA %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception))) end ) + --- Vehicles self.VehicleSet:ForEachGroupAlive( function(grp) if not grp.Tiresias then @@ -167,14 +207,20 @@ function TIRESIAS:_InitGroups() type = "Vehicle", invisible = true, AIOff = true, + exception = false, } end - if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then - grp:SetCommandInvisible(true) - grp:SetAIOff() - end + if grp.Tiresias and (not grp.Tiresias.exception == true) then + if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then + grp:SetCommandInvisible(true) + grp:SetAIOff() + grp.Tiresias.invisible = true + end + end + --BASE:I(string.format("Init/Switch off Vehicle %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception))) end ) + --- SAM self.SAMSet:ForEachGroupAlive( function(grp) if not grp.Tiresias then @@ -182,11 +228,16 @@ function TIRESIAS:_InitGroups() grp.Tiresias = { -- #TIRESIAS.Data type = "SAM", invisible = true, + exception = false, } end - if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then - grp:SetCommandInvisible(true) + if grp.Tiresias and (not grp.Tiresias.exception == true) then + if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then + grp:SetCommandInvisible(true) + grp.Tiresias.invisible = true + end end + --BASE:I(string.format("Init/Switch off SAM %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception))) end ) return self @@ -197,7 +248,7 @@ end -- @param Core.Event#EVENTDATA EventData -- @return #TIRESIAS self function TIRESIAS:_EventHandler(EventData) - self:I(string.format("%s Event = %d",self.lid, EventData.id)) + self:T(string.format("%s Event = %d",self.lid, EventData.id)) local event = EventData -- Core.Event#EVENTDATA if event.id == EVENTS.PlayerEnterAircraft or event.id == EVENTS.PlayerEnterUnit then local _coalition = event.IniCoalition @@ -224,7 +275,7 @@ end -- @param #number radius Radius in NM -- @return #TIRESIAS self function TIRESIAS:_SwitchOnGroups(group,radius) - self:I(self.lid.."_SwitchOnGroups "..group:GetName().." Radius "..radius.." NM") + self:T(self.lid.."_SwitchOnGroups "..group:GetName().." Radius "..radius.." NM") local zone = ZONE_GROUP:New("Zone-"..group:GetName(),group,UTILS.NMToMeters(radius)) local ground = SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce() local count = ground:CountAlive() @@ -232,20 +283,27 @@ function TIRESIAS:_SwitchOnGroups(group,radius) local text = string.format("There are %d groups around this plane or helo!",count) self:I(text) end + local SwitchAAA = self.SwitchAAA if ground:CountAlive() > 0 then ground:ForEachGroupAlive( function(grp) - if grp.Tiresias and grp.Tiresias.type then + if grp.Tiresias and grp.Tiresias.type and (not grp.Tiresias.exception == true ) then if grp.Tiresias.invisible == true then grp:SetCommandInvisible(false) grp.Tiresias.invisible = false end - if grp.Tiresias.type == "Vehicle" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == false then + if grp.Tiresias.type == "Vehicle" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then grp:SetAIOn() grp.Tiresias.AIOff = false end + if SwitchAAA and grp.Tiresias.type == "AAA" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then + grp:SetAIOn() + grp:EnableEmission(true) + grp.Tiresias.AIOff = false + end + --BASE:I(string.format("TIRESIAS - Switch on %s %s (Exception %s)",tostring(grp.Tiresias.type),grp:GetName(),tostring(grp.Tiresias.exception))) else - BASE:I("TIRESIAS - This group has not been initialized!") + BASE:E("TIRESIAS - This group has not been initialized or is an exception!") end end ) @@ -266,7 +324,7 @@ end -- @param #string To -- @return #TIRESIAS self function TIRESIAS:onafterStart(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) local VehicleSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterAAA):FilterFunction(TIRESIAS._FilterSAM):FilterStart() local AAASet = SET_GROUP:New():FilterCategoryGround():FilterFunction(function(grp) return grp:IsAAA() end):FilterStart() @@ -275,6 +333,22 @@ function TIRESIAS:onafterStart(From, Event, To) local EngageRange = self.AAARange + if self.ExceptionSet then + local ExceptionSet = self.ExceptionSet + function ExceptionSet:OnAfterAdded(From,Event,To,ObjectName,Object) + BASE:I("TIRESIAS: EXCEPTION Object Added: "..Object:GetName()) + if Object and Object:IsAlive() then + Object.Tiresias = { -- #TIRESIAS.Data + type = "Exception", + exception = true, + } + Object:SetAIOn() + Object:SetCommandInvisible(false) + Object:EnableEmission(true) + end + end + end + function VehicleSet:OnAfterAdded(From,Event,To,ObjectName,Object) BASE:I("TIRESIAS: VEHCILE Object Added: "..Object:GetName()) if Object and Object:IsAlive() then @@ -284,20 +358,29 @@ function TIRESIAS:onafterStart(From, Event, To) type = "Vehicle", invisible = true, AIOff = true, + exception = false, } end end + local SwitchAAA = self.SwitchAAA + function AAASet:OnAfterAdded(From,Event,To,ObjectName,Object) if Object and Object:IsAlive() then BASE:I("TIRESIAS: AAA Object Added: "..Object:GetName()) Object:OptionEngageRange(EngageRange) Object:SetCommandInvisible(true) + if SwitchAAA then + Object:SetAIOff() + Object:EnableEmission(false) + end Object.Tiresias = { -- #TIRESIAS.Data - type = "AAA", - invisible = true, - range = EngageRange, - } + type = "AAA", + invisible = true, + range = EngageRange, + exception = false, + AIOff = SwitchAAA, + } end end @@ -308,6 +391,7 @@ function TIRESIAS:onafterStart(From, Event, To) Object.Tiresias = { -- #TIRESIAS.Data type = "SAM", invisible = true, + exception = false, } end end @@ -343,7 +427,7 @@ end -- @param #string To -- @return #TIRESIAS self function TIRESIAS:onafterStatus(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) if self.debug then local count = self.VehicleSet:CountAlive() local AAAcount = self.AAASet:CountAlive() @@ -376,7 +460,7 @@ end -- @param #string To -- @return #TIRESIAS self function TIRESIAS:onafterStop(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) self:UnHandleEvent(EVENTS.PlayerEnterAircraft) return self end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 177ff3cde..eb87efc58 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2999,19 +2999,19 @@ function GROUP:IsSAM() return issam end ---- [GROUND] Determine if a GROUP is a AAA unit, i.e. has no radar or optical tracker but the AAA = true or the "Mobile AAA" = true attribute. +--- [GROUND] Determine if a GROUP has a AAA unit, i.e. has no radar or optical tracker but the AAA = true or the "Mobile AAA" = true attribute. -- @param #GROUP self -- @return #boolean IsSAM True if AAA, else false function GROUP:IsAAA() - local issam = true + local issam = false local units = self:GetUnits() for _,_unit in pairs(units or {}) do local unit = _unit -- Wrapper.Unit#UNIT - if unit:HasSEAD() or (not unit:IsGround()) then - issam = false - if unit:HasAttribute("Mobile AAA") then - issam = true - end + local desc = unit:GetDesc() or {} + local attr = desc.attributes or {} + if unit:HasSEAD() then return false end + if attr["AAA"] or attr["SAM related"] then + issam = true end end return issam From 04125cef3dae263650cd146de1e283657d15a8eb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 1 Jan 2024 09:26:19 +0100 Subject: [PATCH 465/603] xxx --- .../Moose/Functional/Tiresias.lua | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Functional/Tiresias.lua b/Moose Development/Moose/Functional/Tiresias.lua index d6df56c76..9780d734e 100644 --- a/Moose Development/Moose/Functional/Tiresias.lua +++ b/Moose Development/Moose/Functional/Tiresias.lua @@ -130,13 +130,12 @@ function TIRESIAS:AddExceptionSet(Set) end --- --- @param #TIRESIAS self -- @param Wrapper.Group#GROUP Group -- @return #boolean isin -function TIRESIAS._FilterAAA(Group) +function TIRESIAS._FilterNotAAA(Group) local grp = Group -- Wrapper.Group#GROUP local isaaa = grp:IsAAA() - if isaaa == true and grp:IsGround() then + if isaaa == true and grp:IsGround() and not grp:IsShip() then return false -- remove from SET else return true -- keep in SET @@ -144,16 +143,41 @@ function TIRESIAS._FilterAAA(Group) end --- --- @param #TIRESIAS self +-- @param Wrapper.Group#GROUP Group +-- @return #boolean isin +function TIRESIAS._FilterNotSAM(Group) + local grp = Group -- Wrapper.Group#GROUP + local issam = grp:IsSAM() + if issam == true and grp:IsGround() and not grp:IsShip() then + return false -- remove from SET + else + return true -- keep in SET + end +end + +--- +-- @param Wrapper.Group#GROUP Group +-- @return #boolean isin +function TIRESIAS._FilterAAA(Group) + local grp = Group -- Wrapper.Group#GROUP + local isaaa = grp:IsAAA() + if isaaa == true and grp:IsGround() and not grp:IsShip() then + return true -- remove from SET + else + return false -- keep in SET + end +end + +--- -- @param Wrapper.Group#GROUP Group -- @return #boolean isin function TIRESIAS._FilterSAM(Group) local grp = Group -- Wrapper.Group#GROUP local issam = grp:IsSAM() - if issam == true and grp:IsGround() then - return false -- remove from SET + if issam == true and grp:IsGround() and not grp:IsShip() then + return true -- remove from SET else - return true -- keep in SET + return false -- keep in SET end end @@ -326,9 +350,9 @@ end function TIRESIAS:onafterStart(From, Event, To) self:T({From, Event, To}) - local VehicleSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterAAA):FilterFunction(TIRESIAS._FilterSAM):FilterStart() - local AAASet = SET_GROUP:New():FilterCategoryGround():FilterFunction(function(grp) return grp:IsAAA() end):FilterStart() - local SAMSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(function(grp) return grp:IsSAM() end):FilterStart() + local VehicleSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterNotAAA):FilterFunction(TIRESIAS._FilterNotSAM):FilterStart() + local AAASet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterAAA):FilterStart() + local SAMSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterSAM):FilterStart() self.FlightSet = SET_GROUP:New():FilterCategories({"plane","helicopter"}):FilterStart() local EngageRange = self.AAARange From e36f5b3fbdf5aa601bcdb5b41a2b328d56fb48ca Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 1 Jan 2024 13:06:52 +0100 Subject: [PATCH 466/603] xxx --- .../Moose/Functional/Tiresias.lua | 156 ++++++++++++++---- Moose Development/Moose/Ops/CTLD.lua | 3 +- 2 files changed, 127 insertions(+), 32 deletions(-) diff --git a/Moose Development/Moose/Functional/Tiresias.lua b/Moose Development/Moose/Functional/Tiresias.lua index 9780d734e..078bfda56 100644 --- a/Moose Development/Moose/Functional/Tiresias.lua +++ b/Moose Development/Moose/Functional/Tiresias.lua @@ -1,10 +1,18 @@ ---- **Functional** - TIRESIAS +--- **Functional** - TIRESIAS - manages AI behaviour. -- -- === --- +-- +-- The @{#TIRESIAS} class is working in the back to keep your large-scale ground units in check. +-- -- ## Features: -- --- * Tbd +-- * Designed to keep CPU and Network usage lower on missions with a lot of ground units. +-- * Does not affect ships to keep the Navy guys happy. +-- * Does not affect OpsGroup type groups. +-- * Distinguishes between SAM groups, AAA groups and other ground groups. +-- * Exceptions can be defined to keep certain actions going. +-- * Works coalition-independent in the back +-- * Easy setup. -- -- === -- @@ -34,6 +42,7 @@ -- @field Core.Set#SET_GROUP AAASet -- @field Core.Set#SET_GROUP SAMSet -- @field Core.Set#SET_GROUP ExceptionSet +-- @field Core.Set#SET_OPSGROUP OpsGroupSet -- @field #number AAARange -- @field #number HeloSwitchRange -- @field #number PlaneSwitchRange @@ -50,17 +59,47 @@ -- @field #boolean exception ---- --- # Documentation +--- *Tiresias, Greek demi-god and shapeshifter, blinded by the Gods, works as oracle for you.* (Wiki) +-- +-- === +-- +-- ## TIRESIAS Concept -- +-- * Designed to keep CPU and Network usage lower on missions with a lot of ground units. +-- * Does not affect ships to keep the Navy guys happy. +-- * Does not affect OpsGroup type groups. +-- * Distinguishes between SAM groups, AAA groups and other ground groups. +-- * Exceptions can be defined in SET_GROUP objects to keep certain actions going. +-- * Works coalition-independent in the back +-- * Easy setup. +-- +-- ## Setup +-- +-- Setup is a one-liner: +-- +-- local blinder = TIRESIAS:New() +-- +-- Optionally you can set up exceptions, e.g. for convoys driving around +-- +-- local exceptionset = SET_GROUP:New():FilterCoalitions("red"):FilterPrefixes("Convoy"):FilterStart() +-- local blinder = TIRESIAS:New() +-- blinder:AddExceptionSet(exceptionset) +-- +-- Options +-- +-- -- Setup different radius for activation around helo and airplane groups (applies to AI and humans) +-- blinder:SetActivationRanges(10,25) -- defaults are 10, and 25 +-- +-- -- Setup engagement ranges for AAA (non-advanced SAM units like Flaks etc) and if you want them to be AIOff +-- blinder:SetAAARanges(60,true) -- defaults are 60, and true +-- -- @field #TIRESIAS TIRESIAS = { ClassName = "TIRESIAS", debug = false, - version = "0.0.2", + version = "0.0.4", Interval = 20, GroundSet = nil, - Coalition = coalition.side.BLUE, VehicleSet = nil, AAASet = nil, SAMSet = nil, @@ -71,7 +110,7 @@ TIRESIAS = { SwitchAAA = true, } ---- +--- [USER] Create a new Tiresias object and start it up. -- @param #TIRESIAS self -- @return #TIRESIAS self function TIRESIAS:New() @@ -90,12 +129,32 @@ function TIRESIAS:New() self:AddTransition("*", "Status", "*") -- TIRESIAS status update. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. + self.ExceptionSet = SET_GROUP:New():Clear(false) + self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) self.lid = string.format("TIRESIAS %s | ",self.version) self:I(self.lid.."Managing ground groups!") + --- Triggers the FSM event "Stop". Stops TIRESIAS and all its event handlers. + -- @function [parent=#TIRESIAS] Stop + -- @param #TIRESIAS self + + --- Triggers the FSM event "Stop" after a delay. Stops TIRESIAS and all its event handlers. + -- @function [parent=#TIRESIAS] __Stop + -- @param #TIRESIAS self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Start". Starts TIRESIAS and all its event handlers. Note - `:New()` already starts the instance. + -- @function [parent=#TIRESIAS] Start + -- @param #TIRESIAS self + + --- Triggers the FSM event "Start" after a delay. Starts TIRESIAS and all its event handlers. Note - `:New()` already starts the instance. + -- @function [parent=#TIRESIAS] __Start + -- @param #TIRESIAS self + -- @param #number delay Delay in seconds. + self:__Start(1) return self end @@ -106,14 +165,35 @@ end -- ------------------------------------------------------------------------------------------------------------- ---- +---[USER] Set activation radius for Helos and Planes in Nautical Miles. -- @param #TIRESIAS self --- @param Core.Set#SET_GROUP Set +-- @param #number HeloMiles Radius around a Helicopter in which AI ground units will be activated. Defaults to 10NM. +-- @param #number PlaneMiles Radius around an Airplane in which AI ground units will be activated. Defaults to 25NM. +-- @return #TIRESIAS self +function TIRESIAS:SetActivationRanges(HeloMiles,PlaneMiles) + self.HeloSwitchRange = HeloMiles or 10 + self.PlaneSwitchRange = PlaneMiles or 25 + return self +end + +---[USER] Set AAA Ranges - AAA equals non-SAM systems which qualify as AAA in DCS world. +-- @param #TIRESIAS self +-- @param #number FiringRange The engagement range that AAA units will be set to. Can be 0 to 100 (percent). Defaults to 60. +-- @param #boolean SwitchAAA Decide if these system will have their AI switched off, too. Defaults to true. +-- @return #TIRESIAS self +function TIRESIAS:SetAAARanges(FiringRange,SwitchAAA) + self.AAARange = FiringRange or 60 + self.SwitchAAA = (SwitchAAA == false) and false or true + return self +end + +--- [USER] Add a SET_GROUP of GROUP objects as exceptions. Can be done multiple times. +-- @param #TIRESIAS self +-- @param Core.Set#SET_GROUP Set to add to the exception list. -- @return #TIRESIAS self function TIRESIAS:AddExceptionSet(Set) self:T(self.lid.."AddExceptionSet") - self.ExceptionSet = Set - + local exceptions = self.ExceptionSet Set:ForEachGroupAlive( function(grp) if not grp.Tiresias then @@ -121,15 +201,15 @@ function TIRESIAS:AddExceptionSet(Set) type = "Exception", exception = true, } + exceptions:AddGroup(grp,true) end BASE:I("TIRESIAS: Added exception group: "..grp:GetName()) end - ) - + ) return self end ---- +--- [INTERNAL] Filter Function -- @param Wrapper.Group#GROUP Group -- @return #boolean isin function TIRESIAS._FilterNotAAA(Group) @@ -142,7 +222,7 @@ function TIRESIAS._FilterNotAAA(Group) end end ---- +--- [INTERNAL] Filter Function -- @param Wrapper.Group#GROUP Group -- @return #boolean isin function TIRESIAS._FilterNotSAM(Group) @@ -155,7 +235,7 @@ function TIRESIAS._FilterNotSAM(Group) end end ---- +--- [INTERNAL] Filter Function -- @param Wrapper.Group#GROUP Group -- @return #boolean isin function TIRESIAS._FilterAAA(Group) @@ -168,7 +248,7 @@ function TIRESIAS._FilterAAA(Group) end end ---- +--- [INTERNAL] Filter Function -- @param Wrapper.Group#GROUP Group -- @return #boolean isin function TIRESIAS._FilterSAM(Group) @@ -181,7 +261,7 @@ function TIRESIAS._FilterSAM(Group) end end ---- +--- [INTERNAL] Init Groups -- @param #TIRESIAS self -- @return #TIRESIAS self function TIRESIAS:_InitGroups() @@ -267,7 +347,7 @@ function TIRESIAS:_InitGroups() return self end ---- (Internal) Event handler function +--- [INTERNAL] Event handler function -- @param #TIRESIAS self -- @param Core.Event#EVENTDATA EventData -- @return #TIRESIAS self @@ -275,10 +355,10 @@ function TIRESIAS:_EventHandler(EventData) self:T(string.format("%s Event = %d",self.lid, EventData.id)) local event = EventData -- Core.Event#EVENTDATA if event.id == EVENTS.PlayerEnterAircraft or event.id == EVENTS.PlayerEnterUnit then - local _coalition = event.IniCoalition - if _coalition ~= self.Coalition then - return --ignore! - end + --local _coalition = event.IniCoalition + --if _coalition ~= self.Coalition then + -- return --ignore! + --end local unitname = event.IniUnitName or "none" local _unit = event.IniUnit local _group = event.IniGroup @@ -293,7 +373,7 @@ function TIRESIAS:_EventHandler(EventData) return self end ---- +--- [INTERNAL] Switch Groups Behaviour -- @param #TIRESIAS self -- @param Wrapper.Group#GROUP group -- @param #number radius Radius in NM @@ -341,7 +421,7 @@ end -- ------------------------------------------------------------------------------------------------------------- ---- +--- [INTERNAL] FSM Function -- @param #TIRESIAS self -- @param #string From -- @param #string Event @@ -353,12 +433,13 @@ function TIRESIAS:onafterStart(From, Event, To) local VehicleSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterNotAAA):FilterFunction(TIRESIAS._FilterNotSAM):FilterStart() local AAASet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterAAA):FilterStart() local SAMSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterSAM):FilterStart() + local OpsGroupSet = SET_OPSGROUP:New():FilterActive(true):FilterStart() self.FlightSet = SET_GROUP:New():FilterCategories({"plane","helicopter"}):FilterStart() local EngageRange = self.AAARange - if self.ExceptionSet then - local ExceptionSet = self.ExceptionSet + local ExceptionSet = self.ExceptionSet + if self.ExceptionSet then function ExceptionSet:OnAfterAdded(From,Event,To,ObjectName,Object) BASE:I("TIRESIAS: EXCEPTION Object Added: "..Object:GetName()) if Object and Object:IsAlive() then @@ -371,6 +452,18 @@ function TIRESIAS:onafterStart(From, Event, To) Object:EnableEmission(true) end end + + local OGS = OpsGroupSet:GetAliveSet() + for _,_OG in pairs(OGS or {}) do + local OG = _OG -- Ops.OpsGroup#OPSGROUP + local grp = OG:GetGroup() + ExceptionSet:AddGroup(grp,true) + end + + function OpsGroupSet:OnAfterAdded(From,Event,To,ObjectName,Object) + local grp = Object:GetGroup() + ExceptionSet:AddGroup(grp,true) + end end function VehicleSet:OnAfterAdded(From,Event,To,ObjectName,Object) @@ -423,6 +516,7 @@ function TIRESIAS:onafterStart(From, Event, To) self.VehicleSet = VehicleSet self.AAASet = AAASet self.SAMSet = SAMSet + self.OpsGroupSet = OpsGroupSet self:_InitGroups() @@ -430,7 +524,7 @@ function TIRESIAS:onafterStart(From, Event, To) return self end ---- +--- [INTERNAL] FSM Function -- @param #TIRESIAS self -- @param #string From -- @param #string Event @@ -444,7 +538,7 @@ function TIRESIAS:onbeforeStatus(From, Event, To) return self end ---- +--- [INTERNAL] FSM Function -- @param #TIRESIAS self -- @param #string From -- @param #string Event @@ -477,7 +571,7 @@ function TIRESIAS:onafterStatus(From, Event, To) return self end ---- +--- [INTERNAL] FSM Function -- @param #TIRESIAS self -- @param #string From -- @param #string Event diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 0ae3a2ef8..792b8e2cc 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1433,7 +1433,7 @@ function CTLD:New(Coalition, Prefixes, Alias) --- Pseudo Functions --- ------------------------ - --- Triggers the FSM event "Start". Starts the CTLD. Initializes parameters and starts event handlers. + --- Triggers the FSM event "Start". Starts the CTLD. Initializes parameters and starts event handlers. -- @function [parent=#CTLD] Start -- @param #CTLD self @@ -1443,6 +1443,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param #number delay Delay in seconds. --- Triggers the FSM event "Stop". Stops the CTLD and all its event handlers. + -- @function [parent=#CTLD] Stop -- @param #CTLD self --- Triggers the FSM event "Stop" after a delay. Stops the CTLD and all its event handlers. From 29e255a7bddbe8dd345e326344c6de049f886c5b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 2 Jan 2024 18:12:57 +0100 Subject: [PATCH 467/603] xxx --- Moose Development/Moose/Core/Point.lua | 14 +++++++++++++- Moose Development/Moose/Functional/Tiresias.lua | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 89807b436..b69e7d294 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -181,7 +181,7 @@ do -- COORDINATE -- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance. -- * @{#COORDINATE.ToStringBRA}(): Generates a Bearing, Range & Altitude text. -- * @{#COORDINATE.ToStringBRAANATO}(): Generates a Generates a Bearing, Range, Aspect & Altitude text in NATOPS. - -- * @{#COORDINATE.ToStringLL}(): Generates a Latutide & Longitude text. + -- * @{#COORDINATE.ToStringLL}(): Generates a Latitude & Longitude text. -- * @{#COORDINATE.ToStringLLDMS}(): Generates a Lat, Lon, Degree, Minute, Second text. -- * @{#COORDINATE.ToStringLLDDM}(): Generates a Lat, Lon, Degree, decimal Minute text. -- * @{#COORDINATE.ToStringMGRS}(): Generates a MGRS grid coordinate text. @@ -3071,6 +3071,18 @@ do -- COORDINATE return coord.LOtoLL( self:GetVec3() ) end + --- Get Latitude & Longitude text. + -- @param #COORDINATE self + -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @return #string LLText + function COORDINATE:ToStringLL( Settings ) + + local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy + local lat, lon = coord.LOtoLL( self:GetVec3() ) + return string.format('%f', lat) .. ' ' .. string.format('%f', lon) + end + + --- Provides a Lat Lon string in Degree Minute Second format. -- @param #COORDINATE self -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. diff --git a/Moose Development/Moose/Functional/Tiresias.lua b/Moose Development/Moose/Functional/Tiresias.lua index 078bfda56..9582e581d 100644 --- a/Moose Development/Moose/Functional/Tiresias.lua +++ b/Moose Development/Moose/Functional/Tiresias.lua @@ -33,7 +33,7 @@ --- **TIRESIAS** class, extends Core.Base#BASE -- @type TIRESIAS -- @field #string ClassName --- @field #booelan debug +-- @field #boolean debug -- @field #string version -- @field #number Interval -- @field Core.Set#SET_GROUP GroundSet From 90e7711788dc320d8634e121ab7d0c7c78e01d0a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 3 Jan 2024 18:03:45 +0100 Subject: [PATCH 468/603] STRATEGO --- .../Moose/Functional/Stratego.lua | 1174 +++++++++++++++++ 1 file changed, 1174 insertions(+) create mode 100644 Moose Development/Moose/Functional/Stratego.lua diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua new file mode 100644 index 000000000..a11b18d8e --- /dev/null +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -0,0 +1,1174 @@ +--- **Functional** - Stratego. +-- +-- **Main Features:** +-- +-- * Helper class for mission designers to support classic capture-the-base scenarios. +-- * Creates a network of possible connections between bases (airbases, FARPs, Ships), Ports (defined as zones) and POIs (defined as zones). +-- * Assigns a strategic value to each of the resulting knots. +-- * Can create a list of targets for your next mission move, both strategic and consolidation targets. +-- * Can be used with budgets to limit the target selection. +-- * Highly configureable. +-- +-- === +-- +-- ### Author: **applevangelist** +-- +-- @module Functional.Stratego +-- @image Stratego.png + + +--- +--- **STRATEGO** class, extends Core.Base#BASE +-- @type STRATEGO +-- @field #string ClassName +-- @field #boolean debug +-- @field #string version +-- @field #number portweight +-- @field #number POIweight +-- @field #number maxrunways +-- @field #number coalition +-- @field #table colors +-- @field #table airbasetable +-- @field #table nonconnectedab +-- @field #table easynames +-- @field #number maxdist +-- @field #table disttable +-- @field #table routexists +-- @field #number routefactor +-- @field #table OpsZones +-- @field #number NeutralBenefit +-- @field #number Budget +-- @field #boolean usebudget +-- @field #number CaptureUnits +-- @field #number CaptureThreatlevel +-- @extends Core.Base#BASE + + +--- *If you see what is right and fail to act on it, you lack courage* --- Confucius +-- +-- === +-- +-- # The STRATEGO Concept +-- +-- STRATEGO is a helper class for mission designers. +-- The basic idea is to create a network of knots (bases) on the map, which each have a number of connections +-- to other knots. The base value of each knot is the number of runways of the base (the bigger the more important), or in the case of Ports and POIs, the assigned value points. +-- The strategic value of each base is determined by the number of routes going in and out of the knot, where connections between more strategic knots add a higher value to the +-- strategic value than connections to less valueable knots. +-- +-- ## Setup +-- +-- Setup is map indepent and works automatically. All airbases, FARPS, and ships on the map are considered. **Note:** Later spawned objects are not considered at the moment. +-- +-- -- Setup and start STRATGEO for the blue side, maximal knot distance is 100km +-- local Bluecher = STRATEGO:New("Bluecher",coalition.side.BLUE,100) +-- -- use budgets +-- Bluecher:SetUsingBudget(true,500) +-- -- draw on the map +-- Bluecher:SetDebug(true,true,true) +-- -- Start +-- Bluecher:Start() +-- +-- ### Helper +-- +-- @{#STRATEGO.SetWeights}(): Set weights for knots and routes to determine their importance. +-- +-- ### Hint +-- +-- Each knot is its own @{Ops.OpsZone#OPSZONE} object to manage the coalition alignment of that knot and how it can be conquered. +-- +-- ### Distance +-- +-- The knot distance factor determines how many connections are there on the map. The smaller the lighter is the resulting net. The higher the thicker it gets, with more strategic options. +-- Play around with the distance to get an optimal map for your scenario. +-- +-- One some maps, e.g. Syria, lower distance factors can create "islands" of unconnected network parts on the map. FARPs and POIs can bridge those gaps, or you can add routes manually. +-- +-- @{#STRATEGO.AddRoutesManually}(): Add a route manually. +-- +-- ## Ports and POIs +-- +-- Ports and POIs are @{Core.Zone#ZONE} objects on the map with specfic values. Zones with the keywords "Port" or "POI" in the name are automatically considered at setup time. +-- +-- ## Get next possible targets +-- +-- There are two types of possible target lists, strategic and consolidation. Targets closer to the start knot are chosen as possible targets. +-- +-- +-- * Strategic targets are of higher or equal base weight from a given start point. Can also be obtained for the whole net. +-- * Consoliation targets are of smaller or equal base weight from a given start point. Can also be obtained for the whole net. +-- +-- +-- @{#STRATEGO.UpdateKnotCoalitions}(): Update alls knot's coalition data before takign a decision. +-- @{#STRATEGO.FindStrategicTargets}(): Find a list of possible strategic targets in the network of the enemy or neutral coalition. +-- @{#STRATEGO.FindConsolidationTargets}(): Find a list of possible strategic targets in the network of the enemy or neutral coalition. +-- @{#STRATEGO.FindAffordableStrategicTarget}(): When using budgets, find **one** strategic target you can afford. +-- @{#STRATEGO.FindAffordableConsolidationTarget}(): When using budgets, find **one** consolidation target you can afford. +-- @{#STRATEGO.FindClosestStrategicTarget}(): Find closest strategic target from a given start point. +-- @{#STRATEGO.FindClosestConsolidationTarget}(): Find closest consolidation target from a given start point. +-- @{#STRATEGO.GetHighestWeightKnots}(): Get a list of the knots with the highest weight. Coalition independent. +-- @{#STRATEGO.GetNextHighestWeightKnots}(): Get a list of the knots a weight less than the give parameter. Coalition independent. +-- +-- +-- **How** you act on these suggestions is again totally up to your mission design. +-- +-- ## Using budgets +-- +-- Set up STRATEGO to use budgets to limit the target selection. **How** your side actually earns budgets is up to your mission design. However, when using budgets, a target will only be selected, +-- when you have more budget points available than the value points of the targeted base. +-- +-- -- use budgets +-- Bluecher:SetUsingBudget(true,500) +-- +-- ## Helpers: +-- +-- +-- @{#STRATEGO.GetBudget}(): Get the current budget points. +-- @{#STRATEGO.AddBudget}(): Add a number of budget points. +-- @{#STRATEGO.SubtractBudget}(): Subtract a number of budget points. +-- @{#STRATEGO.SetNeutralBenefit Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +-- +-- +-- ## Functions to query a knot's data +-- +-- +-- @{#STRATEGO.GetKnotBaseWeight}(): Get the base weight of a knot by its name. +-- @{#STRATEGO.GetKnotCoalition}(): Get the COALITION of a knot by its name. +-- @{#STRATEGO.GetKnotType}(): Get the TYPE of a knot by its name. +-- @{#STRATEGO.GetKnotZone}(): Get the ZONE of a knot by its name. +-- @{#STRATEGO.GetKnotOpsZone}(): Get the OPSZONE of a knot by its name. +-- @{#STRATEGO.GetKnotCoordinate}(): Get the COORDINATE of a knot by its name. +-- @{#STRATEGO.IsAirbase}(): Check if the TYPE of a knot is AIRBASE. +-- @{#STRATEGO.IsPort}(): Check if the TYPE of a knot is PORT. +-- @{#STRATEGO.IsPOI}(): Check if the TYPE of a knot is POI. +-- @{#STRATEGO.IsFARP}(): Check if the TYPE of a knot is FARP. +-- @{#STRATEGO.IsShip}(): Check if the TYPE of a knot is SHIP. +-- +-- +-- ## Various +-- +-- +-- @{#STRATEGO.FindNeighborKnots}(): Get neighbor knots of a named knot. +-- @{#STRATEGO.FindRoute}(): Find a route between two knots. +-- @{#STRATEGO.SetCaptureOptions}(): Set how many units of which minimum threat level are needed to capture one knot (i.e. the underlying OpsZone). +-- @{#STRATEGO.SetDebug}(): Set debug and draw options. +-- +-- +-- ## Visualisation example code for the Syria map: +-- +-- local Bluecher = STRATEGO:New("Bluecher",coalition.side.BLUE,100) +-- Bluecher:SetDebug(true,true,true) +-- Bluecher:Start() +-- +-- Bluecher:AddRoutesManually(AIRBASE.Syria.Beirut_Rafic_Hariri,AIRBASE.Syria.Larnaca) +-- Bluecher:AddRoutesManually(AIRBASE.Syria.Incirlik,AIRBASE.Syria.Hatay) +-- Bluecher:AddRoutesManually(AIRBASE.Syria.Incirlik,AIRBASE.Syria.Minakh) +-- Bluecher:AddRoutesManually(AIRBASE.Syria.King_Hussein_Air_College,AIRBASE.Syria.H4) +-- Bluecher:AddRoutesManually(AIRBASE.Syria.Sayqal,AIRBASE.Syria.At_Tanf) +-- +-- local route = Bluecher:FindRoute(AIRBASE.Syria.Rosh_Pina,AIRBASE.Syria.Incirlik,5,true) +-- UTILS.PrintTableToLog(route,1) +-- +-- @field #STRATEGO +STRATEGO = { + ClassName = "STRATEGO", + debug = false, + drawzone = false, + markzone = false, + version = "0.1.0", + portweight = 3, + POIweight = 1, + maxrunways = 3, + coalition = nil, + colors = nil, + airbasetable = {}, + nonconnectedab = {}, + easynames = {}, + maxdist = 150, -- km + disttable = {}, + routexists = {}, + routefactor = 5, + OpsZones = {}, + NeutralBenefit = 100, + Budget = 0, + usebudget = false, + CaptureUnits = 3, + CaptureThreatlevel = 1, +} + +--- +-- @type STRATEGO.Data +-- @field #string name +-- @field #number baseweight +-- @field #number weight +-- @field #number coalition +-- @field #boolean port +-- @field Core.Zone#ZONE_RADIUS zone, +-- @field Core.Point#COORDINATRE coord +-- @field #string type +-- @field Ops.OpsZone#OPSZONE opszone + +--- +-- @type STRATEGO.DistData +-- @field #string start +-- @field #string target +-- @field #number dist + +--- +-- @type STRATEGO.Target +-- @field #string name +-- @field #number dist +-- @field #number points +-- @field #number coalition +-- @field #string coalitionname + +--- +-- @type STRATEGO.Type +-- @field #string AIRBASE +-- @field #string PORT +-- @field #string POI +-- @field #string FARP +-- @field #string SHIP +STRATEGO.Type = { + AIRBASE = "AIRBASE", + PORT = "PORT", + POI = "POI", + FARP = "FARP", + SHIP = "SHIP", +} + +--- [USER] Create a new STRATEGO object and start it up. +-- @param #STRATEGO self +-- @param #string Name Name of the Adviser. +-- @param #number Coalition Coalition, e.g. coalition.side.BLUE. +-- @param #number MaxDist Maximum distance of a single route in kilometers, defaults to 150. +-- @return #STRATEGO self +function STRATEGO:New(Name,Coalition,MaxDist) + -- Inherit everything from BASE class. + local self = BASE:Inherit(self, BASE:New()) -- #STRATEGO + + self.coalition = Coalition + self.coalitiontext = UTILS.GetCoalitionName(Coalition) + self.name = Name or "Hannibal" + + self.maxdist = MaxDist or 150 -- km + self.disttable = {} + self.routexists = {} + + self.lid = string.format("STRATEGO %s %s | ",self.name,self.version) + + self.bases = SET_AIRBASE:New():FilterOnce() + self.ports = SET_ZONE:New():FilterPrefixes("Port"):FilterOnce() + self.POIs = SET_ZONE:New():FilterPrefixes("POI"):FilterOnce() + + self.colors = { + [1] = {0,1,0}, -- green + [2] = {1,0,0}, -- red + [3] = {0,0,1}, -- blue + [4] = {1,0.65,0}, -- orange + } + + return self +end + +--- [USER] Do initial setup and get ready. +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:Start() + self:T(self.lid.."Start") + self:AnalyseBases() + self:AnalysePOIs(self.ports,self.portweight,"PORT") + self:AnalysePOIs(self.POIs,self.POIweight,"POI") + + for i=self.maxrunways,1,-1 do + self:AnalyseRoutes(i,i*self.routefactor,self.colors[(i%3)+1],i) + --self:AnalyseRoutes(2,2*self.routefactor,self.colors[2],2) + --self:AnalyseRoutes(1,1*self.routefactor,self.colors[3],3) + end + self:AnalyseUnconnected(self.colors[4]) + + self:I(self.lid.."Advisory ready.") + return self +end + +--- [USER] Set up usage of budget and set an initial budget in points. +-- @param #STRATEGO self +-- @param #boolean Usebudget If true, use budget for advisory calculations. +-- @param #number StartBudget Initial budget to be used, defaults to 500. +function STRATEGO:SetUsingBudget(Usebudget,StartBudget) + self:T(self.lid.."SetUsingBudget") + self.usebudget = Usebudget + self.Budget = StartBudget + return self +end + +--- [USER] Set debugging. +-- @param #STRATEGO self +-- @param #boolean Debug If true, switch on debugging. +-- @param #boolean DrawZones If true, draw the OpsZones on the F10 map. +-- @param #boolean MarkZones if true, mark the OpsZones on the F10 map (with further information). +function STRATEGO:SetDebug(Debug,DrawZones,MarkZones) + self.debug = Debug + self.drawzone = DrawZones + self.markzone = MarkZones + return self +end + +--- [USER] Set weights for knots and routes to determine their importance. +-- @param #STRATEGO self +-- @param #number MaxRunways Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase knot hence equals the number of runways. +-- @param #number PortWeight Set what weight a port knot has. Defaults to 3. +-- @param #number POIWeight Set what weight a POI knot has. Defaults to 1. +-- @param #number RouteFactor Defines which weight each route between two defined knots gets: Weight * RouteFactor. +-- @return #STRATEGO self +function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor) + self.portweight = PortWeight or 3 + self.POIweight = POIWeight or 1 + self.maxrunways = MaxRunways or 3 + self.routefactor = RouteFactor or 5 + return self +end + +--- [USER] Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +-- @param #STRATEGO self +-- @param #number NeutralBenefit Pointsm defaults to 100. +-- @return #STRATEGO self +function STRATEGO:SetNeutralBenefit(NeutralBenefit) + self.NeutralBenefit = NeutralBenefit or 100 + return self +end + +--- [USER] Set how many units of which minimum threat level are needed to capture one knot (i.e. the underlying OpsZone). +-- @param #STRATEGO self +-- @param #number CaptureUnits Number of units needed, defaults to three. +-- @param #number CaptureThreatlevel Threat level needed, can be 0..10, defaults to one. +-- @return #STRATEGO self +function STRATEGO:SetCaptureOptions(CaptureUnits,CaptureThreatlevel) + self.CaptureUnits = CaptureUnits or 3 + self.CaptureThreatlevel = CaptureThreatlevel or 1 + return self +end + +--- [INTERNAL] Analyse airbase setups +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:AnalyseBases() + self:T(self.lid.."AnalyseBases") + local colors = self.colors + local debug = self.debug + local airbasetable = self.airbasetable + local nonconnectedab = self.nonconnectedab + local easynames = self.easynames + + -- find bases with >= 1 runways + self.bases:ForEach( + function(afb) + local ab = afb -- Wrapper.Airbase#AIRBASE + local abname = ab:GetName() + local runways = ab:GetRunways() + local numrwys = #runways + if numrwys >= 1 then numrwys = numrwys * 0.5 end + local abzone = ab:GetZone() + local coa = ab:GetCoalition() + 1 + local abtype = "AIRBASE" + if ab:IsShip() then + numrwys = 1 + abtype = "SHIP" + end + if ab:IsHelipad() then + numrwys = 1 + abtype = "FARP" + end + local coord = abzone:GetCoordinate() + if debug then + abzone:DrawZone(-1,colors[coa],1,colors[coa],0.3,1) + coord:TextToAll(tostring(numrwys),-1,{0,0,0},1,colors[coa],0.3,20) + end + local opszone = self:GetNewOpsZone(abname,coa-1) + local tbl = { + name = abname, + baseweight = numrwys, + weight = 0, + coalition = coa-1, + port = false, + zone = abzone, + coord = coord, + type = abtype, + } + airbasetable[abname] = tbl + nonconnectedab[abname] = true + local name = string.gsub(abname,"[%p%s]",".") + easynames[name]=abname + end + ) + return self +end + +--- [INTERNAL] Update knot coalitions +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:UpdateKnotCoalitions() + self:T(self.lid.."UpdateKnotCoalitions") + local newtable = {} + for _id,_data in pairs(self.airbasetable) do + local data = _data -- #STRATEGO.Data + if data.type == "AIRBASE" or data.type == "FARP" then + data.coalition = AIRBASE:FindByName(data.name):GetCoalition() + else + data.coalition = data.opszone:GetOwner() + end + newtable[_id] = _data + end + self.airbasetable = nil + self.airbasetable = newtable + return self +end + +--- [INTERNAL] Get an OpsZone from a Zone object. +-- @param #STRATEGO self +-- @param Core.Zone#ZONE Zone +-- @param #number Coalition +-- @return Ops.OpsZone#OPSZONE OpsZone +function STRATEGO:GetNewOpsZone(Zone,Coalition) + self:T(self.lid.."GetNewOpsZone") + local opszone = OPSZONE:New(Zone,Coalition or 0) + opszone:SetCaptureNunits(self.CaptureUnits) + opszone:SetCaptureThreatlevel(self.CaptureThreatlevel) + opszone:SetDrawZone(self.drawzone) + opszone:SetMarkZone(self.markzone) + opszone:Start() + return opszone +end + +--- [INTERNAL] Analyse POI setups +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:AnalysePOIs(Set,Weight,Key) + self:T(self.lid.."AnalysePOIs") + local colors = self.colors + local debug = self.debug + local airbasetable = self.airbasetable + local nonconnectedab = self.nonconnectedab + local easynames = self.easynames + Set:ForEach( + function(port) + local zone = port -- Core.Zone#ZONE_RADIUS + local zname = zone:GetName() + local coord = zone:GetCoordinate() + if debug then + zone:DrawZone(-1,colors[1],1,colors[1],0.3,1) + coord:TextToAll(tostring(Weight),-1,{0,0,0},1,colors[1],0.3,20) + end + local opszone = self:GetNewOpsZone(zone) + local tbl = { -- #STRATEGO.Data + name = zname, + baseweight = Weight, + weight = 0, + coalition = coalition.side.NEUTRAL, + port = true, + zone = zone, + coord = coord, + type = Key, + opszone = opszone, + } + airbasetable[zone:GetName()] = tbl + nonconnectedab[zone:GetName()] = true + local name = string.gsub(zname,"[%p%s]",".") + easynames[name]=zname + end + ) + return self +end + +--- [INTERNAL] Get nice route text +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:GetToFrom(StartPoint,EndPoint) + self:T(self.lid.."GetToFrom") + local pstart = string.gsub(StartPoint,"[%p%s]",".") + local pend = string.gsub(EndPoint,"[%p%s]",".") + local fromto = pstart..";"..pend + local tofrom = pend..";"..pstart + return fromto, tofrom +end + +--- [USER] Manually add a route, for e.g. Island hopping or to connect isolated networks. Use **after** STRATEGO has been started! +-- @param #STRATEGO self +-- @param #string Startpoint +-- @param #string Endpoint +-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to lila. +-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 5. +-- @param #boolean Draw (Optional) If true, draw route on the F10 map. Defaukt false. +-- @return #STRATEGO self +function STRATEGO:AddRoutesManually(Startpoint,Endpoint,Color,Linetype,Draw) + local fromto,tofrom = self:GetToFrom(Startpoint,Endpoint) + local startcoordinate = self.airbasetable[Startpoint].coord + local targetcoordinate = self.airbasetable[Endpoint].coord + local dist = UTILS.Round(targetcoordinate:Get2DDistance(startcoordinate),-2)/1000 + local color = Color or {136/255,0,1} + local linetype = Linetype or 5 + local data = { + start = Startpoint, + target = Endpoint, + dist = dist, + } + --table.insert(disttable,fromto,data) + self.disttable[fromto] = data + self.disttable[tofrom] = data + --table.insert(disttable,tofrom,data) + table.insert(self.routexists,fromto) + table.insert(self.routexists,tofrom) + self.nonconnectedab[Endpoint] = false + self.nonconnectedab[Startpoint] = false + local factor = self.airbasetable[Startpoint].baseweight*self.routefactor + self.airbasetable[Startpoint].weight = self.airbasetable[Startpoint].weight+factor + self.airbasetable[Endpoint].weight = self.airbasetable[Endpoint].weight+factor + if self.debug or Draw then + startcoordinate:LineToAll(targetcoordinate,-1,color,1,linetype,nil,string.format("%dkm",dist)) + end + return self +end + +--- [INTERNAL] Analyse routes +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:AnalyseRoutes(tgtrwys,factor,color,linetype) + self:T(self.lid.."AnalyseRoutes") + for _,_ab in pairs(self.airbasetable) do + if _ab.baseweight >= 1 then + local startpoint = _ab.name + local startcoord = _ab.coord + for _,_data in pairs(self.airbasetable) do + local fromto,tofrom = self:GetToFrom(startpoint,_data.name) + if _data.name == startpoint then + -- sam as we + elseif _data.baseweight == tgtrwys and not (self.routexists[fromto] or self.routexists[tofrom]) then + local tgtc = _data.coord + local dist = UTILS.Round(tgtc:Get2DDistance(startcoord),-2)/1000 + if dist <= self.maxdist then + --local text = string.format("Distance %s to %s is %dkm",startpoint,_data.name,dist) + --MESSAGE:New(text,10):ToLog() + local data = { + start = startpoint, + target = _data.name, + dist = dist, + } + --table.insert(disttable,fromto,data) + self.disttable[fromto] = data + self.disttable[tofrom] = data + --table.insert(disttable,tofrom,data) + table.insert(self.routexists,fromto) + table.insert(self.routexists,tofrom) + self.nonconnectedab[_data.name] = false + self.nonconnectedab[startpoint] = false + self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+factor + self.airbasetable[_data.name].weight = self.airbasetable[_data.name].weight+factor + if self.debug then + startcoord:LineToAll(tgtc,-1,color,1,linetype,nil,string.format("%dkm",dist)) + end + end + end + end + end + end + return self +end + +--- [INTERNAL] Analyse non-connected points. +-- @param #STRATEGO self +-- @param #table Color RGB color to be used. +-- @return #STRATEGO self +function STRATEGO:AnalyseUnconnected(Color) + self:T(self.lid.."AnalyseUnconnected") + -- Non connected ones + for _name,_noconnect in pairs(self.nonconnectedab) do + if _noconnect then + -- Find closest connected airbase + local startpoint = _name + local startcoord = self.airbasetable[_name].coord + local shortest = 1000*1000 + local closest = nil + local closestcoord = nil + for _,_data in pairs(self.airbasetable) do + if _name ~= _data.name then + --local tgt = AIRBASE:FindByName(_data.name) + local tgtc = _data.coord + local dist = UTILS.Round(tgtc:Get2DDistance(startcoord),-2)/1000 + if dist < shortest and self.nonconnectedab[_data.name] == false then + --local text = string.format("Distance %s to %s is %dkm",startpoint,_data.name,dist) + shortest = dist + closest = _data.name + closestcoord = tgtc + --MESSAGE:New(text,10):ToLog():ToAll() + end + end + end + if closest then + if self.debug then + startcoord:LineToAll(closestcoord,-1,Color,1,3,nil,string.format("%dkm",shortest)) + end + self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+1 + self.airbasetable[closest].weight = self.airbasetable[closest].weight+1 + local data = { + start = startpoint, + target = closest, + dist = shortest, + } + local fromto,tofrom = self:GetToFrom(startpoint,closest) + self.disttable[fromto] = data + self.disttable[tofrom] = data + table.insert(self.routexists,fromto) + table.insert(self.routexists,tofrom) + end + end + end + return self +end + +--- [USER] Get a list of the knots with the highest weight. +-- @param #STRATEGO self +-- @return #table Table of knots. +-- @return #number Weight The consolidated weight associated with the knots. +function STRATEGO:GetHighestWeightKnots() + self:T(self.lid.."GetHighestWeightBases") + local weight = 0 + local airbases = {} + for _name,_data in pairs(self.airbasetable) do + if _data.weight >= weight then + weight = _data.weight + if not airbases[weight] then airbases[weight]={} end + table.insert(airbases[weight],_name) + end + end + return airbases[weight],weight +end + +--- [USER] Get a list of the knots a weight less than the give parameter. +-- @param #STRATEGO self +-- @return #table Table of knots. +-- @return #number Weight The consolidated weight associated with the knots. +function STRATEGO:GetNextHighestWeightKnots(Weight) + self:T(self.lid.."GetNextHighestWeightBases") + local weight = 0 + local airbases = {} + for _name,_data in pairs(self.airbasetable) do + if _data.weight >= weight and _data.weight < Weight then + weight = _data.weight + if not airbases[weight] then airbases[weight]={} end + table.insert(airbases[weight],_name) + end + end + return airbases[weight],weight +end + +--- [USER] Get the aggregated weight of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #number Weight The weight or 0 if not found. +function STRATEGO:GetKnotWeight(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].weight or 0 + else + return 0 + end +end + +--- [USER] Get the base weight of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #number Weight The base weight or 0 if not found. +function STRATEGO:GetKnotBaseWeight(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].baseweight or 0 + else + return 0 + end +end + +--- [USER] Get the COALITION of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #number Coalition The coalition. +function STRATEGO:GetKnotCoalition(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].coalition or coalition.side.NEUTRAL + else + return coalition.side.NEUTRAL + end +end + +--- [USER] Get the TYPE of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #string Type Type of the knot, e.g. STRATEGO.Type.AIRBASE or nil if not found. +function STRATEGO:GetKnotType(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type + else + return nil + end +end + +--- [USER] Get the ZONE of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return Core.Zone#ZONE Zone The Zone of the knot or nil if not found. +function STRATEGO:GetKnotZone(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].zone + else + return nil + end +end + +--- [USER] Get the OPSZONE of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return Ops.OpsZone#OPSZONE OpsZone The OpsZone of the knot or nil if not found. +function STRATEGO:GetKnotOpsZone(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].opszone + else + return nil + end +end + +--- [USER] Get the COORDINATE of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return Core.Point#COORDINATE Coordinate The Coordinate of the knot or nil if not found. +function STRATEGO:GetKnotCoordinate(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].coord + else + return nil + end +end + +--- [USER] Check if the TYPE of a knot is AIRBASE. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsAirbase(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.AIRBASE + else + return false + end +end + +--- [USER] Check if the TYPE of a knot is PORT. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsPort(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.PORT + else + return false + end +end + +--- [USER] Check if the TYPE of a knot is POI. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsPOI(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.POI + else + return false + end +end + +--- [USER] Check if the TYPE of a knot is FARP. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsFARP(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.FARP + else + return false + end +end + +--- [USER] Check if the TYPE of a knot is SHIP. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsShip(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.SHIP + else + return false + end +end + +--- [USER] Get the next best consolidation target knot with a lower BaseWeight. +-- @param #STRATEGO self +-- @param #string Startpoint Name of start point. +-- @param #number BaseWeight Base weight of the knot, i.e. the number of runways of an airbase or the weight of ports or POIs. +-- @return #number ShortestDist Shortest distance found. +-- @return #string Name Name of the target knot. +-- @return #number Weight Consolidated weight of the target knot, zero if none found. +-- @return #number Coalition Coaltion of the target. +function STRATEGO:FindClosestConsolidationTarget(Startpoint,BaseWeight) + self:T(self.lid.."FindClosestConsolidationTarget for "..Startpoint.." Weight "..BaseWeight or 0) + -- find existing routes + local shortest = 1000*1000 + local target = nil + local weight = 0 + local coa = nil + if not BaseWeight then BaseWeight = self.maxrunways-1 end + local startpoint = string.gsub(Startpoint,"[%p%s]",".") + for _,_route in pairs(self.routexists) do + if string.find(_route,startpoint,1,true) then + --BASE:I({_route,startpoint}) + local dist = self.disttable[_route].dist + local tname = string.gsub(_route,startpoint,"") + local tname = string.gsub(tname,";","") + local cname = self.easynames[tname] + local targetweight = self.airbasetable[cname].baseweight + coa = self.airbasetable[cname].coalition + if (dist < shortest) and (coa ~= self.coalition) and (BaseWeight >= targetweight) then + shortest = dist + target = cname + weight = self.airbasetable[cname].weight + coa = self.airbasetable[cname].coalition + end + end + end + return shortest,target, weight, coa +end + +--- [USER] Get the next best strategic target knot with same or higher BaseWeight. +-- @param #STRATEGO self +-- @param #string Startpoint Name of start point. +-- @param #number BaseWeight Base weight of the knot, e.g. the number of runways of an airbase or the weight of ports or POIs. +-- @return #number ShortestDist Shortest distance found. +-- @return #string Name Name of the target knot. +-- @return #number Weight Consolidated weight of the target knot, zero if none found. +-- @return #number Coalition Coaltion of the target. +function STRATEGO:FindClosestStrategicTarget(Startpoint,BaseWeight) + self:T(self.lid.."FindClosestStrategicTarget") + -- find existing routes + local shortest = 1000*1000 + local target = nil + local weight = 0 + local coa = nil + if not BaseWeight then BaseWeight = self.maxrunways end + local startpoint = string.gsub(Startpoint,"[%p%s]",".") + for _,_route in pairs(self.routexists) do + if string.find(_route,startpoint,1,true) then + local dist = self.disttable[_route].dist + local tname = string.gsub(_route,startpoint,"") + local tname = string.gsub(tname,";","") + local cname = self.easynames[tname] + if dist < shortest and self.airbasetable[cname].coalition ~= self.coalition and self.airbasetable[cname].baseweight >= BaseWeight then + shortest = dist + target = cname + weight = self.airbasetable[cname].weight + coa = self.airbasetable[cname].coalition + end + end + end + return shortest,target,weight, coa +end + +--- [USER] Get the next best strategic target knots in the network. +-- @param #STRATEGO self +-- @return #table of #STRATEGO.Target data points +function STRATEGO:FindStrategicTargets() + local targets = {} + for _,_data in pairs(self.airbasetable) do + local data = _data -- #STRATEGO.Data + if data.coalition == self.coalition then + local dist, name, points, coa = self:FindClosestStrategicTarget(data.name,self.maxrunways) + if coa == coalition.side.NEUTRAL and points ~= 0 then + local fpoints = points + self.NeutralBenefit + local tries = 1 + while targets[fpoints] or tries < 100 do + fpoints = points + (self.NeutralBenefit+math.random(1,100)) + tries = tries + 1 + end + targets[fpoints] = { + name = name, + dist = dist, + points = fpoints, + coalition = coa, + } + end + local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE + if coa == enemycoa and points ~= 0 then + local fpoints = points + local tries = 1 + while targets[fpoints] or tries < 100 do + fpoints = points + (math.random(1,100)) + tries = tries + 1 + end + targets[fpoints] = { + name = name, + dist = dist, + points = fpoints, + coalition = coa, + } + end + end + end + return targets +end + +--- [USER] Get the next best consolidation target knots in the network. +-- @param #STRATEGO self +-- @return #table of #STRATEGO.Target data points +function STRATEGO:FindConsolidationTargets() + local targets = {} + for _,_data in pairs(self.airbasetable) do + local data = _data -- #STRATEGO.Data + if data.coalition == self.coalition then + local dist, name, points, coa = self:FindClosestConsolidationTarget(data.name,self.maxrunways-1) + if coa == coalition.side.NEUTRAL and points ~= 0 then + local fpoints = points + self.NeutralBenefit + local tries = 1 + while targets[fpoints] or tries < 100 do + fpoints = points - (self.NeutralBenefit+math.random(1,100)) + tries = tries + 1 + end + targets[fpoints] = { + name = name, + dist = dist, + points = fpoints, + coalition = coa, + coalitionname = UTILS.GetCoalitionName(coa), + } + end + local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE + if coa == enemycoa and points ~= 0 then + local fpoints = points + local tries = 1 + while targets[fpoints] or tries < 100 do + fpoints = points - (math.random(1,100)) + tries = tries + 1 + end + targets[fpoints] = { + name = name, + dist = dist, + points = fpoints, + coalition = coa, + coalitionname = UTILS.GetCoalitionName(coa), + } + end + end + end + return targets +end + +--- [USER] Get neighbor knots of a named knot. +-- @param #STRATEGO self +-- @param #string Name The name to search the neighbors for. +-- @param #boolean Enemies (optional) If true, find only enemy neighbors. +-- @param #boolean Friends (optional) If true, find only friendly or neutral neighbors. +-- @return #table Neighbors Table of #STRATEGO.DistData entries indexed by neighbor knot names. +function STRATEGO:FindNeighborKnots(Name,Enemies,Friends) + local neighbors = {} + local name = string.gsub(Name,"[%p%s]",".") + for _route,_data in pairs(self.disttable) do + if string.find(_route,name,1,true) then + local dist = self.disttable[_route] -- #STRATEGO.DistData + local tname = string.gsub(_route,name,"") + local tname = string.gsub(tname,";","") + local cname = self.easynames[tname] -- name of target + local encoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE + if Enemies == true then + if self.airbasetable[cname].coalition == encoa then + neighbors[cname] = dist + end + elseif Friends == true then + if self.airbasetable[cname].coalition ~= encoa then + neighbors[cname] = dist + end + else + neighbors[cname] = dist + end + end + end + return neighbors +end + +--- [USER] Find a route between two knots. +-- @param #STRATEGO self +-- @param #string Start The name of the start knot. +-- @param #string End The name of the end knot. +-- @param #number Hops Max iterations to find a route. +-- @param #boolean Draw If true, draw the route on the map. +-- @return #table Route Table of #string name entries of the route +-- @return #boolean Complete If true, the route was found end-to-end. +function STRATEGO:FindRoute(Start,End,Hops,Draw) + self:I({Start,End,Hops}) + --local bases = UTILS.DeepCopy(self.airbasetable) + local Route = {} + local hops = Hops or 4 + local routecomplete = false + + local function Checker(neighbors) + for _name,_data in pairs(neighbors) do + if _name == End then + -- found it + return End + end + end + return nil + end + + local function NextClosest(Start,End) + local ecoord = self.airbasetable[End].coord + local knots = self:FindNeighborKnots(Start) + local closest = nil + local closedist = 1000*1000 + for _name,_dist in pairs(knots) do + local kcoord = self.airbasetable[_name].coord + local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5) + if dist < closedist then + closedist = dist + closest = _name + end + end + if closest then + --MESSAGE:New(string.format("Start %s | End %s | Nextclosest %s",Start,End,closest),10,"STRATEGO"):ToLog():ToAll() + return closest + end + end + + local function DrawRoute(Route) + for i=1,#Route-1 do + local p1=Route[i] + local p2=Route[i+1] + local c1 = self.airbasetable[p1].coord -- Core.Point#COORDINATE + local c2 = self.airbasetable[p2].coord -- Core.Point#COORDINATE + c1:LineToAll(c2,-1,{0,0,0},1,6) + end + end + + -- One hop + Route[#Route+1] = Start + local knots = self:FindNeighborKnots(Start) + local endpoint = Checker(knots) + + if endpoint then + Route[#Route+1] = endpoint + routecomplete = true + else + local spoint = Start + for i=1,hops do + local Next = NextClosest(spoint,End) + if Next then + Route[#Route+1] = Next + local knots = self:FindNeighborKnots(Next) + local endpoint = Checker(knots) + if endpoint then + Route[#Route+1] = endpoint + routecomplete = true + break + else + spoint = Next + end + end + end + end + if self.debug or Draw then DrawRoute(Route) end + return Route, routecomplete +end + +--- [USER] Add budget points. +-- @param #STRATEGO self +-- @param #number Number of points to add. +-- @return #STRATEGO self +function STRATEGO:AddBudget(Number) + self.Budget = self.Budget + Number + return self +end + +--- [USER] Subtract budget points. +-- @param #STRATEGO self +-- @param #number Number of points to subtract. +-- @return #STRATEGO self +function STRATEGO:SubtractBudget(Number) + self.Budget = self.Budget - Number + return self +end + +--- [USER] Get budget points. +-- @param #STRATEGO self +-- @return #number budget +function STRATEGO:GetBudget() + return self.Budget +end + +--- [USER] Find **one** affordable strategic target. +-- @param #STRATEGO self +-- @return #table Target Table with #STRATEGO.Target data or nil if none found. +function STRATEGO:FindAffordableStrategicTarget() + local targets = self:FindStrategicTargets() -- #table of #STRATEGO.Target + local budget = self.Budget + --local leftover = self.Budget + local target = nil -- #STRATEGO.Target + local Targets = {} + for _,_data in pairs(targets) do + local data = _data -- #STRATEGO.Target + --if data.points <= budget and budget-data.points < leftover then + if data.points <= budget then + --leftover = budget-data.points + table.insert(Targets,data) + self:T(self.lid.."Affordable strategic target: "..data.name) + end + end + if not targets then + self:T(self.lid.."No suitable target found!") + return nil + end + target = Targets[math.random(1,#Targets)] + if target then + self:T(self.lid.."Final affordable strategic target: "..target.name) + return target + else + return nil + end +end + +--- [USER] Find **one** affordable consolidation target. +-- @param #STRATEGO self +-- @return #table Target Table with #STRATEGO.Target data or nil if none found. +function STRATEGO:FindAffordableConsolidationTarget() + local targets = self:FindConsolidationTargets() -- #table of #STRATEGO.Target + local budget = self.Budget + --local leftover = self.Budget + local target = nil -- #STRATEGO.Target + local Targets = {} + for _,_data in pairs(targets) do + local data = _data -- #STRATEGO.Target + --if data.points <= budget and budget-data.points < leftover then + if data.points <= budget then + --leftover = budget-data.points + table.insert(Targets,data) + self:T(self.lid.."Affordable consolidation target: "..data.name) + end + end + if not targets then + self:T(self.lid.."No suitable target found!") + return nil + end + target = Targets[math.random(1,#Targets)] + if target then + self:T(self.lid.."Final affordable consolidation target: "..target.name) + return target + else + return nil + end +end + +--------------------------------------------------------------------------------------------------------------- +-- +-- End +-- +--------------------------------------------------------------------------------------------------------------- \ No newline at end of file From e5fbaeafcdaadb81765200687b82a03a05b36507 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 3 Jan 2024 18:03:53 +0100 Subject: [PATCH 469/603] xxx --- Moose Development/Moose/Core/Set.lua | 31 ++++++++++++++++++++--- Moose Development/Moose/Modules_local.lua | 1 + Moose Setup/Moose.files | 2 ++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index f4e5a0ed7..1ad9bdea6 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -455,7 +455,7 @@ do -- SET_BASE --- Gets a random object from the @{Core.Set#SET_BASE} and derived classes. -- @param #SET_BASE self - -- @return Core.Base#BASE + -- @return Core.Base#BASE or nil if none found or the SET is empty! function SET_BASE:GetRandom() local tablemax = 0 for _,_ind in pairs(self.Index) do @@ -466,6 +466,23 @@ do -- SET_BASE self:T3( { RandomItem } ) return RandomItem end + + --- Gets a random object from the @{Core.Set#SET_BASE} and derived classes. A bit slower than @{#SET_BASE.GetRandom}() but tries to ensure you get an object back if the SET is not empty. + -- @param #SET_BASE self + -- @return Core.Base#BASE or nil if the SET is empty! + function SET_BASE:GetRandomSurely() + local tablemax = 0 + local sorted = {} + for _,_obj in pairs(self.Set) do + tablemax = tablemax + 1 + sorted[tablemax] = _obj + end + --local tablemax = table.maxn(self.Index) + --local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] + local RandomItem = sorted[math.random(1,tablemax)] + self:T3( { RandomItem } ) + return RandomItem + end --- Retrieves the amount of objects in the @{Core.Set#SET_BASE} and derived classes. -- @param #SET_BASE self @@ -8215,9 +8232,15 @@ do -- SET_SCENERY -- @param #SET_SCENERY self -- @return Core.Point#COORDINATE The center coordinate of all the objects in the set. function SET_SCENERY:GetCoordinate() - - local Coordinate = self:GetRandom():GetCoordinate() - + + local Coordinate = COORDINATE:New({0,0,0}) + + local Item = self:GetRandomSurely() + + if Item then + Coordinate:GetCoordinate() + end + local x1 = Coordinate.x local x2 = Coordinate.x local y1 = Coordinate.y diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 7a9c03cf2..b9aa9b0ab 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -80,6 +80,7 @@ __Moose.Include( 'Functional\\Autolase.lua' ) __Moose.Include( 'Functional\\AICSAR.lua' ) __Moose.Include( 'Functional\\AmmoTruck.lua' ) __Moose.Include( 'Functional\\Tiresias.lua' ) +__Moose.Include( 'Functional\\Stratego.lua' ) __Moose.Include( 'Ops\\Airboss.lua' ) __Moose.Include( 'Ops\\RecoveryTanker.lua' ) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 6aae29e92..9410d551e 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -71,6 +71,8 @@ Functional/Autolase.lua Functional/AICSAR.lua Functional/AmmoTruck.lua Functional/ZoneGoalCargo.lua +Functional/Stratego.lua +Functional/Tiresias.lua Ops/Airboss.lua Ops/RecoveryTanker.lua From 57ff653dc396c6de23850ae9e0fee99f8384158d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 3 Jan 2024 18:56:28 +0100 Subject: [PATCH 470/603] xxx --- Moose Development/Moose/Functional/Stratego.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 4f1cd8320..6e285a5cd 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -120,13 +120,13 @@ -- -- use budgets -- Bluecher:SetUsingBudget(true,500) -- --- ## Helpers: +-- ### Helpers: -- -- -- @{#STRATEGO.GetBudget}(): Get the current budget points. -- @{#STRATEGO.AddBudget}(): Add a number of budget points. -- @{#STRATEGO.SubtractBudget}(): Subtract a number of budget points. --- @{#STRATEGO.SetNeutralBenefit Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +-- @{#STRATEGO.SetNeutralBenefit}(): Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. -- -- -- ## Functions to query a knot's data From 98d131881f50bc3ba4d118e18551e6ddf1f3b9dd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 4 Jan 2024 11:24:34 +0100 Subject: [PATCH 471/603] xxx --- .../Moose/Functional/Stratego.lua | 186 +++++++++--------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 6e285a5cd..2f3f272a0 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -4,7 +4,7 @@ -- -- * Helper class for mission designers to support classic capture-the-base scenarios. -- * Creates a network of possible connections between bases (airbases, FARPs, Ships), Ports (defined as zones) and POIs (defined as zones). --- * Assigns a strategic value to each of the resulting knots. +-- * Assigns a strategic value to each of the resulting nodes. -- * Can create a list of targets for your next mission move, both strategic and consolidation targets. -- * Can be used with budgets to limit the target selection. -- * Highly configureable. @@ -51,16 +51,16 @@ -- # The STRATEGO Concept -- -- STRATEGO is a helper class for mission designers. --- The basic idea is to create a network of knots (bases) on the map, which each have a number of connections --- to other knots. The base value of each knot is the number of runways of the base (the bigger the more important), or in the case of Ports and POIs, the assigned value points. --- The strategic value of each base is determined by the number of routes going in and out of the knot, where connections between more strategic knots add a higher value to the --- strategic value than connections to less valueable knots. +-- The basic idea is to create a network of nodes (bases) on the map, which each have a number of connections +-- to other nodes. The base value of each node is the number of runways of the base (the bigger the more important), or in the case of Ports and POIs, the assigned value points. +-- The strategic value of each base is determined by the number of routes going in and out of the node, where connections between more strategic nodes add a higher value to the +-- strategic value than connections to less valueable nodes. -- -- ## Setup -- -- Setup is map indepent and works automatically. All airbases, FARPS, and ships on the map are considered. **Note:** Later spawned objects are not considered at the moment. -- --- -- Setup and start STRATGEO for the blue side, maximal knot distance is 100km +-- -- Setup and start STRATGEO for the blue side, maximal node distance is 100km -- local Bluecher = STRATEGO:New("Bluecher",coalition.side.BLUE,100) -- -- use budgets -- Bluecher:SetUsingBudget(true,500) @@ -71,15 +71,15 @@ -- -- ### Helper -- --- @{#STRATEGO.SetWeights}(): Set weights for knots and routes to determine their importance. +-- @{#STRATEGO.SetWeights}(): Set weights for nodes and routes to determine their importance. -- -- ### Hint -- --- Each knot is its own @{Ops.OpsZone#OPSZONE} object to manage the coalition alignment of that knot and how it can be conquered. +-- Each node is its own @{Ops.OpsZone#OPSZONE} object to manage the coalition alignment of that node and how it can be conquered. -- -- ### Distance -- --- The knot distance factor determines how many connections are there on the map. The smaller the lighter is the resulting net. The higher the thicker it gets, with more strategic options. +-- The node distance factor determines how many connections are there on the map. The smaller the lighter is the resulting net. The higher the thicker it gets, with more strategic options. -- Play around with the distance to get an optimal map for your scenario. -- -- One some maps, e.g. Syria, lower distance factors can create "islands" of unconnected network parts on the map. FARPs and POIs can bridge those gaps, or you can add routes manually. @@ -92,22 +92,22 @@ -- -- ## Get next possible targets -- --- There are two types of possible target lists, strategic and consolidation. Targets closer to the start knot are chosen as possible targets. +-- There are two types of possible target lists, strategic and consolidation. Targets closer to the start node are chosen as possible targets. -- -- -- * Strategic targets are of higher or equal base weight from a given start point. Can also be obtained for the whole net. -- * Consoliation targets are of smaller or equal base weight from a given start point. Can also be obtained for the whole net. -- -- --- @{#STRATEGO.UpdateKnotCoalitions}(): Update alls knot's coalition data before takign a decision. +-- @{#STRATEGO.UpdateNodeCoalitions}(): Update alls node's coalition data before takign a decision. -- @{#STRATEGO.FindStrategicTargets}(): Find a list of possible strategic targets in the network of the enemy or neutral coalition. -- @{#STRATEGO.FindConsolidationTargets}(): Find a list of possible strategic targets in the network of the enemy or neutral coalition. -- @{#STRATEGO.FindAffordableStrategicTarget}(): When using budgets, find **one** strategic target you can afford. -- @{#STRATEGO.FindAffordableConsolidationTarget}(): When using budgets, find **one** consolidation target you can afford. -- @{#STRATEGO.FindClosestStrategicTarget}(): Find closest strategic target from a given start point. -- @{#STRATEGO.FindClosestConsolidationTarget}(): Find closest consolidation target from a given start point. --- @{#STRATEGO.GetHighestWeightKnots}(): Get a list of the knots with the highest weight. Coalition independent. --- @{#STRATEGO.GetNextHighestWeightKnots}(): Get a list of the knots a weight less than the give parameter. Coalition independent. +-- @{#STRATEGO.GetHighestWeightNodes}(): Get a list of the nodes with the highest weight. Coalition independent. +-- @{#STRATEGO.GetNextHighestWeightNodes}(): Get a list of the nodes a weight less than the give parameter. Coalition independent. -- -- -- **How** you act on these suggestions is again totally up to your mission design. @@ -126,31 +126,31 @@ -- @{#STRATEGO.GetBudget}(): Get the current budget points. -- @{#STRATEGO.AddBudget}(): Add a number of budget points. -- @{#STRATEGO.SubtractBudget}(): Subtract a number of budget points. --- @{#STRATEGO.SetNeutralBenefit}(): Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +-- @{#STRATEGO.SetNeutralBenefit}(): Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy node when taking decisions. -- -- --- ## Functions to query a knot's data +-- ## Functions to query a node's data -- -- --- @{#STRATEGO.GetKnotBaseWeight}(): Get the base weight of a knot by its name. --- @{#STRATEGO.GetKnotCoalition}(): Get the COALITION of a knot by its name. --- @{#STRATEGO.GetKnotType}(): Get the TYPE of a knot by its name. --- @{#STRATEGO.GetKnotZone}(): Get the ZONE of a knot by its name. --- @{#STRATEGO.GetKnotOpsZone}(): Get the OPSZONE of a knot by its name. --- @{#STRATEGO.GetKnotCoordinate}(): Get the COORDINATE of a knot by its name. --- @{#STRATEGO.IsAirbase}(): Check if the TYPE of a knot is AIRBASE. --- @{#STRATEGO.IsPort}(): Check if the TYPE of a knot is PORT. --- @{#STRATEGO.IsPOI}(): Check if the TYPE of a knot is POI. --- @{#STRATEGO.IsFARP}(): Check if the TYPE of a knot is FARP. --- @{#STRATEGO.IsShip}(): Check if the TYPE of a knot is SHIP. +-- @{#STRATEGO.GetNodeBaseWeight}(): Get the base weight of a node by its name. +-- @{#STRATEGO.GetNodeCoalition}(): Get the COALITION of a node by its name. +-- @{#STRATEGO.GetNodeType}(): Get the TYPE of a node by its name. +-- @{#STRATEGO.GetNodeZone}(): Get the ZONE of a node by its name. +-- @{#STRATEGO.GetNodeOpsZone}(): Get the OPSZONE of a node by its name. +-- @{#STRATEGO.GetNodeCoordinate}(): Get the COORDINATE of a node by its name. +-- @{#STRATEGO.IsAirbase}(): Check if the TYPE of a node is AIRBASE. +-- @{#STRATEGO.IsPort}(): Check if the TYPE of a node is PORT. +-- @{#STRATEGO.IsPOI}(): Check if the TYPE of a node is POI. +-- @{#STRATEGO.IsFARP}(): Check if the TYPE of a node is FARP. +-- @{#STRATEGO.IsShip}(): Check if the TYPE of a node is SHIP. -- -- -- ## Various -- -- --- @{#STRATEGO.FindNeighborKnots}(): Get neighbor knots of a named knot. --- @{#STRATEGO.FindRoute}(): Find a route between two knots. --- @{#STRATEGO.SetCaptureOptions}(): Set how many units of which minimum threat level are needed to capture one knot (i.e. the underlying OpsZone). +-- @{#STRATEGO.FindNeighborNodes}(): Get neighbor nodes of a named node. +-- @{#STRATEGO.FindRoute}(): Find a route between two nodes. +-- @{#STRATEGO.SetCaptureOptions}(): Set how many units of which minimum threat level are needed to capture one node (i.e. the underlying OpsZone). -- @{#STRATEGO.SetDebug}(): Set debug and draw options. -- -- @@ -175,7 +175,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.1.0", + version = "0.2.1", portweight = 3, POIweight = 1, maxrunways = 3, @@ -314,12 +314,12 @@ function STRATEGO:SetDebug(Debug,DrawZones,MarkZones) return self end ---- [USER] Set weights for knots and routes to determine their importance. +--- [USER] Set weights for nodes and routes to determine their importance. -- @param #STRATEGO self --- @param #number MaxRunways Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase knot hence equals the number of runways. --- @param #number PortWeight Set what weight a port knot has. Defaults to 3. --- @param #number POIWeight Set what weight a POI knot has. Defaults to 1. --- @param #number RouteFactor Defines which weight each route between two defined knots gets: Weight * RouteFactor. +-- @param #number MaxRunways Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase node hence equals the number of runways. +-- @param #number PortWeight Set what weight a port node has. Defaults to 3. +-- @param #number POIWeight Set what weight a POI node has. Defaults to 1. +-- @param #number RouteFactor Defines which weight each route between two defined nodes gets: Weight * RouteFactor. -- @return #STRATEGO self function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor) self.portweight = PortWeight or 3 @@ -329,7 +329,7 @@ function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor) return self end ---- [USER] Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +--- [USER] Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy node when taking decisions. -- @param #STRATEGO self -- @param #number NeutralBenefit Pointsm defaults to 100. -- @return #STRATEGO self @@ -338,7 +338,7 @@ function STRATEGO:SetNeutralBenefit(NeutralBenefit) return self end ---- [USER] Set how many units of which minimum threat level are needed to capture one knot (i.e. the underlying OpsZone). +--- [USER] Set how many units of which minimum threat level are needed to capture one node (i.e. the underlying OpsZone). -- @param #STRATEGO self -- @param #number CaptureUnits Number of units needed, defaults to three. -- @param #number CaptureThreatlevel Threat level needed, can be 0..10, defaults to one. @@ -404,11 +404,11 @@ function STRATEGO:AnalyseBases() return self end ---- [INTERNAL] Update knot coalitions +--- [INTERNAL] Update node coalitions -- @param #STRATEGO self -- @return #STRATEGO self -function STRATEGO:UpdateKnotCoalitions() - self:T(self.lid.."UpdateKnotCoalitions") +function STRATEGO:UpdateNodeCoalitions() + self:T(self.lid.."UpdateNodeCoalitions") local newtable = {} for _id,_data in pairs(self.airbasetable) do local data = _data -- #STRATEGO.Data @@ -625,11 +625,11 @@ function STRATEGO:AnalyseUnconnected(Color) return self end ---- [USER] Get a list of the knots with the highest weight. +--- [USER] Get a list of the nodes with the highest weight. -- @param #STRATEGO self --- @return #table Table of knots. --- @return #number Weight The consolidated weight associated with the knots. -function STRATEGO:GetHighestWeightKnots() +-- @return #table Table of nodes. +-- @return #number Weight The consolidated weight associated with the nodes. +function STRATEGO:GetHighestWeightNodes() self:T(self.lid.."GetHighestWeightBases") local weight = 0 local airbases = {} @@ -643,11 +643,11 @@ function STRATEGO:GetHighestWeightKnots() return airbases[weight],weight end ---- [USER] Get a list of the knots a weight less than the give parameter. +--- [USER] Get a list of the nodes a weight less than the give parameter. -- @param #STRATEGO self --- @return #table Table of knots. --- @return #number Weight The consolidated weight associated with the knots. -function STRATEGO:GetNextHighestWeightKnots(Weight) +-- @return #table Table of nodes. +-- @return #number Weight The consolidated weight associated with the nodes. +function STRATEGO:GetNextHighestWeightNodes(Weight) self:T(self.lid.."GetNextHighestWeightBases") local weight = 0 local airbases = {} @@ -661,11 +661,11 @@ function STRATEGO:GetNextHighestWeightKnots(Weight) return airbases[weight],weight end ---- [USER] Get the aggregated weight of a knot by its name. +--- [USER] Get the aggregated weight of a node by its name. -- @param #STRATEGO self -- @param #string Name. -- @return #number Weight The weight or 0 if not found. -function STRATEGO:GetKnotWeight(Name) +function STRATEGO:GetNodeWeight(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].weight or 0 else @@ -673,11 +673,11 @@ function STRATEGO:GetKnotWeight(Name) end end ---- [USER] Get the base weight of a knot by its name. +--- [USER] Get the base weight of a node by its name. -- @param #STRATEGO self -- @param #string Name. -- @return #number Weight The base weight or 0 if not found. -function STRATEGO:GetKnotBaseWeight(Name) +function STRATEGO:GetNodeBaseWeight(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].baseweight or 0 else @@ -685,11 +685,11 @@ function STRATEGO:GetKnotBaseWeight(Name) end end ---- [USER] Get the COALITION of a knot by its name. +--- [USER] Get the COALITION of a node by its name. -- @param #STRATEGO self -- @param #string Name. -- @return #number Coalition The coalition. -function STRATEGO:GetKnotCoalition(Name) +function STRATEGO:GetNodeCoalition(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].coalition or coalition.side.NEUTRAL else @@ -697,11 +697,11 @@ function STRATEGO:GetKnotCoalition(Name) end end ---- [USER] Get the TYPE of a knot by its name. +--- [USER] Get the TYPE of a node by its name. -- @param #STRATEGO self -- @param #string Name. --- @return #string Type Type of the knot, e.g. STRATEGO.Type.AIRBASE or nil if not found. -function STRATEGO:GetKnotType(Name) +-- @return #string Type Type of the node, e.g. STRATEGO.Type.AIRBASE or nil if not found. +function STRATEGO:GetNodeType(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].type else @@ -709,11 +709,11 @@ function STRATEGO:GetKnotType(Name) end end ---- [USER] Get the ZONE of a knot by its name. +--- [USER] Get the ZONE of a node by its name. -- @param #STRATEGO self -- @param #string Name. --- @return Core.Zone#ZONE Zone The Zone of the knot or nil if not found. -function STRATEGO:GetKnotZone(Name) +-- @return Core.Zone#ZONE Zone The Zone of the node or nil if not found. +function STRATEGO:GetNodeZone(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].zone else @@ -721,11 +721,11 @@ function STRATEGO:GetKnotZone(Name) end end ---- [USER] Get the OPSZONE of a knot by its name. +--- [USER] Get the OPSZONE of a node by its name. -- @param #STRATEGO self -- @param #string Name. --- @return Ops.OpsZone#OPSZONE OpsZone The OpsZone of the knot or nil if not found. -function STRATEGO:GetKnotOpsZone(Name) +-- @return Ops.OpsZone#OPSZONE OpsZone The OpsZone of the node or nil if not found. +function STRATEGO:GetNodeOpsZone(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].opszone else @@ -733,11 +733,11 @@ function STRATEGO:GetKnotOpsZone(Name) end end ---- [USER] Get the COORDINATE of a knot by its name. +--- [USER] Get the COORDINATE of a node by its name. -- @param #STRATEGO self -- @param #string Name. --- @return Core.Point#COORDINATE Coordinate The Coordinate of the knot or nil if not found. -function STRATEGO:GetKnotCoordinate(Name) +-- @return Core.Point#COORDINATE Coordinate The Coordinate of the node or nil if not found. +function STRATEGO:GetNodeCoordinate(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].coord else @@ -745,7 +745,7 @@ function STRATEGO:GetKnotCoordinate(Name) end end ---- [USER] Check if the TYPE of a knot is AIRBASE. +--- [USER] Check if the TYPE of a node is AIRBASE. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -757,7 +757,7 @@ function STRATEGO:IsAirbase(Name) end end ---- [USER] Check if the TYPE of a knot is PORT. +--- [USER] Check if the TYPE of a node is PORT. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -769,7 +769,7 @@ function STRATEGO:IsPort(Name) end end ---- [USER] Check if the TYPE of a knot is POI. +--- [USER] Check if the TYPE of a node is POI. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -781,7 +781,7 @@ function STRATEGO:IsPOI(Name) end end ---- [USER] Check if the TYPE of a knot is FARP. +--- [USER] Check if the TYPE of a node is FARP. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -793,7 +793,7 @@ function STRATEGO:IsFARP(Name) end end ---- [USER] Check if the TYPE of a knot is SHIP. +--- [USER] Check if the TYPE of a node is SHIP. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -805,13 +805,13 @@ function STRATEGO:IsShip(Name) end end ---- [USER] Get the next best consolidation target knot with a lower BaseWeight. +--- [USER] Get the next best consolidation target node with a lower BaseWeight. -- @param #STRATEGO self -- @param #string Startpoint Name of start point. --- @param #number BaseWeight Base weight of the knot, i.e. the number of runways of an airbase or the weight of ports or POIs. +-- @param #number BaseWeight Base weight of the node, i.e. the number of runways of an airbase or the weight of ports or POIs. -- @return #number ShortestDist Shortest distance found. --- @return #string Name Name of the target knot. --- @return #number Weight Consolidated weight of the target knot, zero if none found. +-- @return #string Name Name of the target node. +-- @return #number Weight Consolidated weight of the target node, zero if none found. -- @return #number Coalition Coaltion of the target. function STRATEGO:FindClosestConsolidationTarget(Startpoint,BaseWeight) self:T(self.lid.."FindClosestConsolidationTarget for "..Startpoint.." Weight "..BaseWeight or 0) @@ -842,13 +842,13 @@ function STRATEGO:FindClosestConsolidationTarget(Startpoint,BaseWeight) return shortest,target, weight, coa end ---- [USER] Get the next best strategic target knot with same or higher BaseWeight. +--- [USER] Get the next best strategic target node with same or higher BaseWeight. -- @param #STRATEGO self -- @param #string Startpoint Name of start point. --- @param #number BaseWeight Base weight of the knot, e.g. the number of runways of an airbase or the weight of ports or POIs. +-- @param #number BaseWeight Base weight of the node, e.g. the number of runways of an airbase or the weight of ports or POIs. -- @return #number ShortestDist Shortest distance found. --- @return #string Name Name of the target knot. --- @return #number Weight Consolidated weight of the target knot, zero if none found. +-- @return #string Name Name of the target node. +-- @return #number Weight Consolidated weight of the target node, zero if none found. -- @return #number Coalition Coaltion of the target. function STRATEGO:FindClosestStrategicTarget(Startpoint,BaseWeight) self:T(self.lid.."FindClosestStrategicTarget") @@ -876,7 +876,7 @@ function STRATEGO:FindClosestStrategicTarget(Startpoint,BaseWeight) return shortest,target,weight, coa end ---- [USER] Get the next best strategic target knots in the network. +--- [USER] Get the next best strategic target nodes in the network. -- @param #STRATEGO self -- @return #table of #STRATEGO.Target data points function STRATEGO:FindStrategicTargets() @@ -919,7 +919,7 @@ function STRATEGO:FindStrategicTargets() return targets end ---- [USER] Get the next best consolidation target knots in the network. +--- [USER] Get the next best consolidation target nodes in the network. -- @param #STRATEGO self -- @return #table of #STRATEGO.Target data points function STRATEGO:FindConsolidationTargets() @@ -964,13 +964,13 @@ function STRATEGO:FindConsolidationTargets() return targets end ---- [USER] Get neighbor knots of a named knot. +--- [USER] Get neighbor nodes of a named node. -- @param #STRATEGO self -- @param #string Name The name to search the neighbors for. -- @param #boolean Enemies (optional) If true, find only enemy neighbors. -- @param #boolean Friends (optional) If true, find only friendly or neutral neighbors. --- @return #table Neighbors Table of #STRATEGO.DistData entries indexed by neighbor knot names. -function STRATEGO:FindNeighborKnots(Name,Enemies,Friends) +-- @return #table Neighbors Table of #STRATEGO.DistData entries indexed by neighbor node names. +function STRATEGO:FindNeighborNodes(Name,Enemies,Friends) local neighbors = {} local name = string.gsub(Name,"[%p%s]",".") for _route,_data in pairs(self.disttable) do @@ -996,10 +996,10 @@ function STRATEGO:FindNeighborKnots(Name,Enemies,Friends) return neighbors end ---- [USER] Find a route between two knots. +--- [USER] Find a route between two nodes. -- @param #STRATEGO self --- @param #string Start The name of the start knot. --- @param #string End The name of the end knot. +-- @param #string Start The name of the start node. +-- @param #string End The name of the end node. -- @param #number Hops Max iterations to find a route. -- @param #boolean Draw If true, draw the route on the map. -- @return #table Route Table of #string name entries of the route @@ -1023,10 +1023,10 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw) local function NextClosest(Start,End) local ecoord = self.airbasetable[End].coord - local knots = self:FindNeighborKnots(Start) + local nodes = self:FindNeighborNodes(Start) local closest = nil local closedist = 1000*1000 - for _name,_dist in pairs(knots) do + for _name,_dist in pairs(nodes) do local kcoord = self.airbasetable[_name].coord local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5) if dist < closedist then @@ -1052,8 +1052,8 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw) -- One hop Route[#Route+1] = Start - local knots = self:FindNeighborKnots(Start) - local endpoint = Checker(knots) + local nodes = self:FindNeighborNodes(Start) + local endpoint = Checker(nodes) if endpoint then Route[#Route+1] = endpoint @@ -1064,8 +1064,8 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw) local Next = NextClosest(spoint,End) if Next then Route[#Route+1] = Next - local knots = self:FindNeighborKnots(Next) - local endpoint = Checker(knots) + local nodes = self:FindNeighborNodes(Next) + local endpoint = Checker(nodes) if endpoint then Route[#Route+1] = endpoint routecomplete = true From 170f97d013325ac39b1efe17bfece036377811a6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 4 Jan 2024 14:01:02 +0100 Subject: [PATCH 472/603] xxx --- .../Moose/Functional/Stratego.lua | 2 +- Moose Development/Moose/Sound/SRS.lua | 119 +++++++++--------- 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 2f3f272a0..af7eb3d6f 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -14,7 +14,7 @@ -- ### Author: **applevangelist** -- -- @module Functional.Stratego --- @image Stratego.png +-- @image Functional.Stratego.png --- diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index c50e7ee76..559d23c56 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -109,10 +109,10 @@ -- -- Use a specific voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. --- +-- -- Note that you can set voices for each provider via the @{#MSRS.SetVoiceProvider} function. Also shortcuts are available, *i.e.* -- @{#MSRS.SetVoiceWindows}, @{#MSRS.SetVoiceGoogle}, @{#MSRS.SetVoiceAzure} and @{#MSRS.SetVoiceAmazon}. --- +-- -- For voices there are enumerators in this class to help you out on voice names: -- -- MSRS.Voices.Microsoft -- e.g. MSRS.Voices.Microsoft.Hedda - the Microsoft enumerator contains all voices known to work with SRS @@ -134,32 +134,32 @@ -- ## Config file for many variables, auto-loaded by Moose -- -- See @{#MSRS.LoadConfigFile} for details on how to set this up. --- +-- -- ## TTS Providers --- +-- -- The default provider for generating speech from text is the native Windows TTS service. Note that you need to install the voices you want to use. -- -- **Pro-Tip** - use the command line with power shell to call `DCS-SR-ExternalAudio.exe` - it will tell you what is missing -- and also the Google Console error, in case you have missed a step in setting up your Google TTS. -- For example, `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` -- plays a message on 255 MHz AM for the blue coalition in-game. --- +-- -- ### Google -- -- In order to use Google Cloud for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsGoogle} functions: -- -- msrs:SetProvider(MSRS.Provider.GOOGLE) -- msrs:SetProviderOptionsGoogle(CredentialsFile, AccessKey) --- +-- -- The parameter `CredentialsFile` is used with the default 'DCS-SR-ExternalAudio.exe' backend and must be the full path to the credentials JSON file. -- The `AccessKey` parameter is used with the DCS-gRPC backend (see below). --- +-- -- You can set the voice to use with Google via @{#MSRS.SetVoiceGoogle}. --- +-- -- When using Google it also allows you to utilize SSML in your text for more flexibility. -- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech -- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml --- +-- -- ### Amazon Web Service [Only DCS-gRPC backend] -- -- In order to use Amazon Web Service (AWS) for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsAmazon} functions: @@ -177,16 +177,16 @@ -- -- msrs:SetProvider(MSRS.Provider.AZURE) -- msrs:SetProviderOptionsAmazon(AccessKey, Region) --- +-- -- The parameter `AccessKey` is your Azure access key. The parameter `Region` is your [Azure region](https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). --- +-- -- You can set the voice to use with Azure via @{#MSRS.SetVoiceAzure}. --- +-- -- ## Backend --- +-- -- The default interface to SRS is via calling the 'DCS-SR-ExternalAudio.exe'. As noted above, this has the unavoidable drawback that a pop-up briefly appears -- and DCS might be put out of focus. --- +-- -- ## DCS-gRPC as an alternative to 'DCS-SR-ExternalAudio.exe' for TTS -- -- Another interface to SRS is [DCS-gRPC](https://github.com/DCS-gRPC/rust-server). This does not call an exe file and therefore avoids the annoying pop-up window. @@ -776,7 +776,7 @@ end function MSRS:SetVoiceProvider(Voice, Provider) self:F( {Voice=Voice, Provider=Provider} ) self.poptions=self.poptions or {} - + self.poptions[Provider or self:GetProvider()]=Voice return self @@ -834,7 +834,7 @@ end function MSRS:GetVoice(Provider) Provider=Provider or self.provider - + if Provider and self.poptions[Provider] and self.poptions[Provider].voice then return self.poptions[Provider].voice else @@ -863,7 +863,7 @@ function MSRS:SetGoogle(PathToCredentials) if PathToCredentials then self.provider = MSRS.Provider.GOOGLE - + self:SetProviderOptionsGoogle(PathToCredentials, PathToCredentials) end @@ -878,15 +878,15 @@ end function MSRS:SetGoogleAPIKey(APIKey) self:F( {APIKey=APIKey} ) if APIKey then - + self.provider = MSRS.Provider.GOOGLE - + if self.poptions[MSRS.Provider.GOOGLE] then self.poptions[MSRS.Provider.GOOGLE].key=APIKey else self:SetProviderOptionsGoogle(nil ,APIKey) end - + end return self end @@ -894,14 +894,14 @@ end --- Set provider used to generate text-to-speech. -- These options are available: --- +-- -- - `MSRS.Provider.WINDOWS`: Microsoft Windows (default) -- - `MSRS.Provider.GOOGLE`: Google Cloud -- - `MSRS.Provider.AZURE`: Microsoft Azure (only with DCS-gRPC backend) -- - `MSRS.Provier.AMAZON`: Amazone Web Service (only with DCS-gRPC backend) --- +-- -- Note that all providers except Microsoft Windows need as additonal information the credentials of your account. --- +-- -- @param #MSRS self -- @param #string Provider -- @return #MSRS self @@ -950,7 +950,7 @@ end -- @param #string Region Region to use. -- @return #MSRS.ProviderOptions Provider optionas table. function MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) - self:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) + BASE:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) local option={} --#MSRS.ProviderOptions option.provider=Provider @@ -1124,20 +1124,20 @@ function MSRS:PlaySoundText(SoundText, Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlaySoundText, self, SoundText, 0) else - + if self.backend==MSRS.Backend.GRPC then self:_DCSgRPCtts(SoundText.text, nil, SoundText.gender, SoundText.culture, SoundText.voice, SoundText.volume, SoundText.label, SoundText.coordinate) else -- Get command. local command=self:_GetCommand(nil, nil, nil, SoundText.gender, SoundText.voice, SoundText.culture, SoundText.volume, SoundText.speed) - + -- Append text. command=command..string.format(" --text=\"%s\"", tostring(SoundText.text)) - + -- Execute command. self:_ExecCommand(command) - + end end @@ -1189,25 +1189,25 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) else - + Frequencies = Frequencies or self:GetFrequencies() Modulations = Modulations or self:GetModulations() - + if self.backend==MSRS.Backend.SRSEXE then -- Get command line. local command=self:_GetCommand(UTILS.EnsureTable(Frequencies, false), UTILS.EnsureTable(Modulations, false), nil, Gender, Voice, Culture, Volume, nil, nil, Label, Coordinate) - + -- Append text. command=command..string.format(" --text=\"%s\"", tostring(Text)) - + -- Execute command. self:_ExecCommand(command) - + elseif self.backend==MSRS.Backend.GRPC then - + self:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) - + end end @@ -1264,8 +1264,8 @@ end --- Get lat, long and alt from coordinate. -- @param #MSRS self -- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. --- @return #number Latitude (or 0 if no input coordinate was given). --- @return #number Longitude (or 0 if no input coordinate was given). +-- @return #number Latitude (or 0 if no input coordinate was given). +-- @return #number Longitude (or 0 if no input coordinate was given). -- @return #number Altitude (or 0 if no input coordinate was given). function MSRS:_GetLatLongAlt(Coordinate) self:F( {Coordinate=Coordinate} ) @@ -1349,7 +1349,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp -- Set provider options if self.provider==MSRS.Provider.GOOGLE then - local pops=self:GetProviderOptions() + local pops=self:GetProviderOptions() command=command..string.format(' --ssml -G "%s"', pops.credentials) elseif self.provider==MSRS.Provider.WINDOWS then -- Nothing to do. @@ -1512,15 +1512,15 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab self:F({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) local options = {} -- #MSRS.GRPCOptions - + local ssml = Text or '' - + -- Get frequenceies. Frequencies = UTILS.EnsureTable(Frequencies, true) or self:GetFrequencies() - + -- Plain text (not really used. options.plaintext=Text - + -- Name shows as sender. options.srsClientName = Label or self.Label @@ -1528,8 +1528,8 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab if self.coordinate then options.position = {} options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) - end - + end + -- Coalition (gRPC expects lower case) options.coalition = UTILS.GetCoalitionName(self.coalition):lower() @@ -1540,7 +1540,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab -- Provider options: voice, credentials options.provider = {} options.provider[provider] = self:GetProviderOptions(provider) - + -- Voice Voice=Voice or self:GetVoice(self.provider) or self.voice @@ -1548,10 +1548,11 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab -- We use a specific voice options.provider[provider].voice = Voice else - -- DCS-gRPC doesn't directly support language/gender, but can use SSML - - local preTag, genderProp, langProp, postTag = '', '', '', '' + -- DCS-gRPC doesn't directly support language/gender, but can use SSML + + local preTag, genderProp, langProp, postTag = '', '', '', '' + local gender="" if self.gender then gender=string.format(' gender=\"%s\"', self.gender) @@ -1565,7 +1566,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab ssml=string.format("%s", gender, language, Text) end end - + for _,freq in pairs(Frequencies) do self:F("Calling GRPC.tts with the following parameter:") self:F({ssml=ssml, freq=freq, options=options}) @@ -1609,7 +1610,7 @@ end -- -- Windows -- win = { -- voice = "Microsoft Hazel Desktop", --- }, +-- }, -- -- Google Cloud -- gcloud = { -- voice = "en-GB-Standard-A", -- The Google Cloud voice to use (see https://cloud.google.com/text-to-speech/docs/voices). @@ -1625,7 +1626,7 @@ end -- }, -- -- Microsoft Azure -- azure = { --- voice="en-US-AriaNeural", --The default Azure voice to use (see https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). +-- voice="en-US-AriaNeural", --The default Azure voice to use (see https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). -- key="Your access key", -- Your Azure access key. -- region="westeurope", -- The Azure region to use (see https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). -- }, @@ -1672,16 +1673,16 @@ function MSRS:LoadConfigFile(Path,Filename) local filexsists = UTILS.FileExists(pathandfile) if filexsists and not MSRS.ConfigLoaded then - + env.info("FF reading config file") - + -- Load global MSRS_Config assert(loadfile(path..file))() - + if MSRS_Config then - + local Self = self or MSRS --#MSRS - + Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" Self.port = MSRS_Config.Port or 5002 Self.backend = MSRS_Config.Backend or MSRS.Backend.SRSEXE @@ -1702,13 +1703,13 @@ function MSRS:LoadConfigFile(Path,Filename) Self.poptions[provider]=MSRS_Config[provider] end end - + Self.ConfigLoaded = true - + end env.info("MSRS - Successfully loaded default configuration from disk!",false) end - + if not filexsists then env.info("MSRS - Cannot find default configuration file!",false) return false From 8171e3aad2c3aff037be7299c15bf18471d359f5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 4 Jan 2024 18:48:26 +0100 Subject: [PATCH 473/603] xxx --- Moose Development/Moose/Functional/Stratego.lua | 17 +++++++++++------ Moose Development/Moose/Ops/Legion.lua | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index af7eb3d6f..e236544b2 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -394,6 +394,7 @@ function STRATEGO:AnalyseBases() zone = abzone, coord = coord, type = abtype, + opszone = opszone, } airbasetable[abname] = tbl nonconnectedab[abname] = true @@ -494,10 +495,10 @@ end --- [USER] Manually add a route, for e.g. Island hopping or to connect isolated networks. Use **after** STRATEGO has been started! -- @param #STRATEGO self --- @param #string Startpoint --- @param #string Endpoint +-- @param #string Startpoint Starting Point, e.g. AIRBASE.Syria.Hatay +-- @param #string Endpoint End Point, e.g. AIRBASE.Syria.H4 -- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to lila. --- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 5. +-- @param #number Linetype (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 5. -- @param #boolean Draw (Optional) If true, draw route on the F10 map. Defaukt false. -- @return #STRATEGO self function STRATEGO:AddRoutesManually(Startpoint,Endpoint,Color,Linetype,Draw) @@ -1002,9 +1003,11 @@ end -- @param #string End The name of the end node. -- @param #number Hops Max iterations to find a route. -- @param #boolean Draw If true, draw the route on the map. +-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to black. +-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 6. -- @return #table Route Table of #string name entries of the route -- @return #boolean Complete If true, the route was found end-to-end. -function STRATEGO:FindRoute(Start,End,Hops,Draw) +function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) self:I({Start,End,Hops}) --local bases = UTILS.DeepCopy(self.airbasetable) local Route = {} @@ -1046,7 +1049,9 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw) local p2=Route[i+1] local c1 = self.airbasetable[p1].coord -- Core.Point#COORDINATE local c2 = self.airbasetable[p2].coord -- Core.Point#COORDINATE - c1:LineToAll(c2,-1,{0,0,0},1,6) + local line = LineType or 6 + local color = Color or {0,0,0} + c1:LineToAll(c2,-1,color,1,line) end end @@ -1076,7 +1081,7 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw) end end end - if self.debug or Draw then DrawRoute(Route) end + if (self.debug or Draw) then DrawRoute(Route) end return Route, routecomplete end diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 0f6535e99..610636593 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -2265,7 +2265,7 @@ function LEGION:RecruitAssetsForMission(Mission) end end - self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight)) + self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight or 0)) end From b329bf80887b26aa9719ae08407503ef00d59f1f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 5 Jan 2024 12:13:18 +0100 Subject: [PATCH 474/603] xxx --- Moose Development/Moose/Ops/Awacs.lua | 22 ++++++++++++++-------- Moose Development/Moose/Ops/PlayerTask.lua | 14 +++++++++----- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index e908f6465..7c9c0a67b 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -17,7 +17,7 @@ -- === -- -- ### Author: **applevangelist** --- @date Last Update Nov 2023 +-- @date Last Update Jan 2024 -- @module Ops.AWACS -- @image OPS_AWACS.jpg @@ -507,7 +507,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.59", -- #string + version = "0.2.60", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -1405,15 +1405,17 @@ function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number) self.TacticalFrequencies[freq] = freq end if self.AwacsSRS then - self.TacticalSRS = MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation,self.Volume) + self.TacticalSRS = MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation) self.TacticalSRS:SetCoalition(self.coalition) self.TacticalSRS:SetGender(self.Gender) self.TacticalSRS:SetCulture(self.Culture) self.TacticalSRS:SetVoice(self.Voice) self.TacticalSRS:SetPort(self.Port) self.TacticalSRS:SetLabel("AWACS") + self.TacticalSRS:SetVolume(self.Volume) if self.PathToGoogleKey then - self.TacticalSRS:SetGoogle(self.PathToGoogleKey) + --self.TacticalSRS:SetGoogle(self.PathToGoogleKey) + self.TacticalSRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) end self.TacticalSRSQ = MSRSQUEUE:New("Tactical AWACS") end @@ -2079,8 +2081,9 @@ end -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey Path to your google key if you want to use google TTS +-- @param #string AccessKey Your Google API access key. This is necessary if DCS-gRPC is used as backend. -- @return #AWACS self -function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey) +function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey) self:T(self.lid.."SetSRS") self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" self.Gender = Gender or "male" @@ -2088,17 +2091,20 @@ function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey self.Port = Port or 5002 self.Voice = Voice self.PathToGoogleKey = PathToGoogleKey + self.AccessKey = AccessKey self.Volume = Volume or 1.0 - self.AwacsSRS = MSRS:New(self.PathToSRS,self.MultiFrequency,self.MultiModulation,self.Volume) + self.AwacsSRS = MSRS:New(self.PathToSRS,self.MultiFrequency,self.MultiModulation) self.AwacsSRS:SetCoalition(self.coalition) self.AwacsSRS:SetGender(self.Gender) self.AwacsSRS:SetCulture(self.Culture) self.AwacsSRS:SetVoice(self.Voice) self.AwacsSRS:SetPort(self.Port) self.AwacsSRS:SetLabel("AWACS") + self.AwacsSRS:SetVolume(Volume) if self.PathToGoogleKey then - self.AwacsSRS:SetGoogle(self.PathToGoogleKey) + --self.AwacsSRS:SetGoogle(self.PathToGoogleKey) + self.AwacsSRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) end return self @@ -6595,7 +6601,7 @@ function AWACS:onafterCheckTacticalQueue(From,Event,To) if self.PathToGoogleKey then gtext = string.format("%s",gtext) end - self.TacticalSRSQ:NewTransmission(gtext,nil,self.TacticalSRS,nil,0.5,nil,nil,nil,frequency,self.TacticalModulation,nil,nil,nil,nil,nil) + self.TacticalSRSQ:NewTransmission(gtext,nil,self.TacticalSRS,nil,0.5,nil,nil,nil,frequency,self.TacticalModulation) self:T(RadioEntry.TextTTS) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 87b9921cb..79ffe5803 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update Oct 2023 +-- @date Last Update Jan 2024 do @@ -1552,7 +1552,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.63" +PLAYERTASKCONTROLLER.version="0.1.64" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -4005,9 +4005,10 @@ end -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS +-- @param #string AccessKey Your Google API access key. This is necessary if DCS-gRPC is used as backend. -- @param Core.Point#COORDINATE Coordinate Coordinate from which the controller radio is sending -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Coordinate) +function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate) self:T(self.lid.."SetSRS") self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" -- self.Gender = Gender or "male" -- @@ -4015,6 +4016,7 @@ function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Cultu self.Port = Port or 5002 -- self.Voice = Voice -- self.PathToGoogleKey = PathToGoogleKey -- + self.AccessKey = AccessKey self.Volume = Volume or 1.0 -- self.UseSRS = true self.Frequency = Frequency or {127,251} -- @@ -4022,15 +4024,17 @@ function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Cultu self.Modulation = Modulation or {radio.modulation.FM,radio.modulation.AM} -- self.BCModulation = self.Modulation -- set up SRS - self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation,self.Volume) + self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation) self.SRS:SetCoalition(self.Coalition) self.SRS:SetLabel(self.MenuName or self.Name) self.SRS:SetGender(self.Gender) self.SRS:SetCulture(self.Culture) self.SRS:SetPort(self.Port) self.SRS:SetVoice(self.Voice) + self.SRS:SetVolume(self.Volume) if self.PathToGoogleKey then - self.SRS:SetGoogle(self.PathToGoogleKey) + --self.SRS:SetGoogle(self.PathToGoogleKey) + self.SRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) end if Coordinate then self.SRS:SetCoordinate(Coordinate) From 4ad51b8866be93bd5490dd535dd90930f6394c5e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 5 Jan 2024 15:35:54 +0100 Subject: [PATCH 475/603] xxx --- .../Moose/Functional/Stratego.lua | 115 ++++++++++++++++-- 1 file changed, 104 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index e236544b2..f8c9e8c07 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -42,6 +42,7 @@ -- @field #number CaptureUnits -- @field #number CaptureThreatlevel -- @extends Core.Base#BASE +-- @extends Core.Fsm#FSM --- *If you see what is right and fail to act on it, you lack courage* --- Confucius @@ -175,7 +176,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.1", + version = "0.2.2", portweight = 3, POIweight = 1, maxrunways = 3, @@ -244,8 +245,8 @@ STRATEGO.Type = { -- @param #number MaxDist Maximum distance of a single route in kilometers, defaults to 150. -- @return #STRATEGO self function STRATEGO:New(Name,Coalition,MaxDist) - -- Inherit everything from BASE class. - local self = BASE:Inherit(self, BASE:New()) -- #STRATEGO + -- Inherit everything from FSM class. + local self = BASE:Inherit(self, FSM:New()) -- #STRATEGO self.coalition = Coalition self.coalitiontext = UTILS.GetCoalitionName(Coalition) @@ -267,14 +268,56 @@ function STRATEGO:New(Name,Coalition,MaxDist) [3] = {0,0,1}, -- blue [4] = {1,0.65,0}, -- orange } - + + -- Start State. + self:SetStartState("Stopped") + + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("Stopped", "Start", "Running") -- Start FSM. + self:AddTransition("*", "Update", "*") -- Start FSM. + self:AddTransition("*", "NodeEvent", "*") -- Start FSM. + self:AddTransition("Running", "Stop", "Stopped") -- Start FSM. + + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "Start". Starts the STRATEGO. Initializes parameters and starts event handlers. + -- @function [parent=#STRATEGO] Start + -- @param #STRATEGO self + + --- Triggers the FSM event "Start" after a delay. Starts the STRATEGO. Initializes parameters and starts event handlers. + -- @function [parent=#STRATEGO] __Start + -- @param #STRATEGO self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Stop". Stops the STRATEGO and all its event handlers. + -- @function [parent=#STRATEGO] Stop + -- @param #STRATEGO self + + --- Triggers the FSM event "Stop" after a delay. Stops the STRATEGO and all its event handlers. + -- @function [parent=#STRATEGO] __Stop + -- @param #STRATEGO self + -- @param #number delay Delay in seconds. + + --- FSM Function OnAfterNodeEvent. A node changed coalition. + -- @function [parent=#STRATEGO] OnAfterNodeEvent + -- @param #STRATEGO self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Ops.OpsZone#OPSZONE OpsZone The OpsZone triggering the event. + -- @param #number Coalition The coalition of the new owner. + -- @return #STRATEGO self + return self end ---- [USER] Do initial setup and get ready. +--- [INTERNAL] FSM function for initial setup and getting ready. -- @param #STRATEGO self -- @return #STRATEGO self -function STRATEGO:Start() +function STRATEGO:onafterStart(From,Event,To) self:T(self.lid.."Start") self:AnalyseBases() self:AnalysePOIs(self.ports,self.portweight,"PORT") @@ -282,12 +325,27 @@ function STRATEGO:Start() for i=self.maxrunways,1,-1 do self:AnalyseRoutes(i,i*self.routefactor,self.colors[(i%3)+1],i) - --self:AnalyseRoutes(2,2*self.routefactor,self.colors[2],2) - --self:AnalyseRoutes(1,1*self.routefactor,self.colors[3],3) end self:AnalyseUnconnected(self.colors[4]) self:I(self.lid.."Advisory ready.") + + self:__Update(180) + return self +end + +--- [INTERNAL] Update knot association +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:onafterUpdate(From,Event,To) + self:T(self.lid.."Update") + + self:UpdateNodeCoalitions() + + if self:GetState() == "Running" then + self:__Update(180) + end + return self end @@ -308,6 +366,7 @@ end -- @param #boolean DrawZones If true, draw the OpsZones on the F10 map. -- @param #boolean MarkZones if true, mark the OpsZones on the F10 map (with further information). function STRATEGO:SetDebug(Debug,DrawZones,MarkZones) + self:T(self.lid.."SetDebug") self.debug = Debug self.drawzone = DrawZones self.markzone = MarkZones @@ -322,6 +381,7 @@ end -- @param #number RouteFactor Defines which weight each route between two defined nodes gets: Weight * RouteFactor. -- @return #STRATEGO self function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor) + self:T(self.lid.."SetWeights") self.portweight = PortWeight or 3 self.POIweight = POIWeight or 1 self.maxrunways = MaxRunways or 3 @@ -334,6 +394,7 @@ end -- @param #number NeutralBenefit Pointsm defaults to 100. -- @return #STRATEGO self function STRATEGO:SetNeutralBenefit(NeutralBenefit) + self:T(self.lid.."SetNeutralBenefit") self.NeutralBenefit = NeutralBenefit or 100 return self end @@ -344,6 +405,7 @@ end -- @param #number CaptureThreatlevel Threat level needed, can be 0..10, defaults to one. -- @return #STRATEGO self function STRATEGO:SetCaptureOptions(CaptureUnits,CaptureThreatlevel) + self:T(self.lid.."SetCaptureOptions") self.CaptureUnits = CaptureUnits or 3 self.CaptureThreatlevel = CaptureThreatlevel or 1 return self @@ -438,6 +500,15 @@ function STRATEGO:GetNewOpsZone(Zone,Coalition) opszone:SetDrawZone(self.drawzone) opszone:SetMarkZone(self.markzone) opszone:Start() + + local function Captured(opszone,coalition) + self:__NodeEvent(1,opszone,coalition) + end + + function opszone:OnBeforeCaptured(From,Event,To,Coalition) + Captured(opszone,Coalition) + end + return opszone end @@ -502,6 +573,7 @@ end -- @param #boolean Draw (Optional) If true, draw route on the F10 map. Defaukt false. -- @return #STRATEGO self function STRATEGO:AddRoutesManually(Startpoint,Endpoint,Color,Linetype,Draw) + self:T(self.lid.."AddRoutesManually") local fromto,tofrom = self:GetToFrom(Startpoint,Endpoint) local startcoordinate = self.airbasetable[Startpoint].coord local targetcoordinate = self.airbasetable[Endpoint].coord @@ -631,7 +703,7 @@ end -- @return #table Table of nodes. -- @return #number Weight The consolidated weight associated with the nodes. function STRATEGO:GetHighestWeightNodes() - self:T(self.lid.."GetHighestWeightBases") + self:T(self.lid.."GetHighestWeightNodes") local weight = 0 local airbases = {} for _name,_data in pairs(self.airbasetable) do @@ -649,7 +721,7 @@ end -- @return #table Table of nodes. -- @return #number Weight The consolidated weight associated with the nodes. function STRATEGO:GetNextHighestWeightNodes(Weight) - self:T(self.lid.."GetNextHighestWeightBases") + self:T(self.lid.."GetNextHighestWeightNodes") local weight = 0 local airbases = {} for _name,_data in pairs(self.airbasetable) do @@ -667,6 +739,7 @@ end -- @param #string Name. -- @return #number Weight The weight or 0 if not found. function STRATEGO:GetNodeWeight(Name) + self:T(self.lid.."GetNodeWeight") if Name and self.airbasetable[Name] then return self.airbasetable[Name].weight or 0 else @@ -679,6 +752,7 @@ end -- @param #string Name. -- @return #number Weight The base weight or 0 if not found. function STRATEGO:GetNodeBaseWeight(Name) + self:T(self.lid.."GetNodeBaseWeight") if Name and self.airbasetable[Name] then return self.airbasetable[Name].baseweight or 0 else @@ -691,6 +765,7 @@ end -- @param #string Name. -- @return #number Coalition The coalition. function STRATEGO:GetNodeCoalition(Name) + self:T(self.lid.."GetNodeCoalition") if Name and self.airbasetable[Name] then return self.airbasetable[Name].coalition or coalition.side.NEUTRAL else @@ -703,6 +778,7 @@ end -- @param #string Name. -- @return #string Type Type of the node, e.g. STRATEGO.Type.AIRBASE or nil if not found. function STRATEGO:GetNodeType(Name) + self:T(self.lid.."GetNodeType") if Name and self.airbasetable[Name] then return self.airbasetable[Name].type else @@ -715,6 +791,7 @@ end -- @param #string Name. -- @return Core.Zone#ZONE Zone The Zone of the node or nil if not found. function STRATEGO:GetNodeZone(Name) + self:T(self.lid.."GetNodeZone") if Name and self.airbasetable[Name] then return self.airbasetable[Name].zone else @@ -727,6 +804,7 @@ end -- @param #string Name. -- @return Ops.OpsZone#OPSZONE OpsZone The OpsZone of the node or nil if not found. function STRATEGO:GetNodeOpsZone(Name) + self:T(self.lid.."GetNodeOpsZone") if Name and self.airbasetable[Name] then return self.airbasetable[Name].opszone else @@ -739,6 +817,7 @@ end -- @param #string Name. -- @return Core.Point#COORDINATE Coordinate The Coordinate of the node or nil if not found. function STRATEGO:GetNodeCoordinate(Name) + self:T(self.lid.."GetNodeCoordinate") if Name and self.airbasetable[Name] then return self.airbasetable[Name].coord else @@ -751,6 +830,7 @@ end -- @param #string Name. -- @return #boolean Outcome function STRATEGO:IsAirbase(Name) + self:T(self.lid.."IsAirbase") if Name and self.airbasetable[Name] then return self.airbasetable[Name].type == STRATEGO.Type.AIRBASE else @@ -763,6 +843,7 @@ end -- @param #string Name. -- @return #boolean Outcome function STRATEGO:IsPort(Name) + self:T(self.lid.."IsPort") if Name and self.airbasetable[Name] then return self.airbasetable[Name].type == STRATEGO.Type.PORT else @@ -775,6 +856,7 @@ end -- @param #string Name. -- @return #boolean Outcome function STRATEGO:IsPOI(Name) + self:T(self.lid.."IsPOI") if Name and self.airbasetable[Name] then return self.airbasetable[Name].type == STRATEGO.Type.POI else @@ -787,6 +869,7 @@ end -- @param #string Name. -- @return #boolean Outcome function STRATEGO:IsFARP(Name) + self:T(self.lid.."IsFARP") if Name and self.airbasetable[Name] then return self.airbasetable[Name].type == STRATEGO.Type.FARP else @@ -799,6 +882,7 @@ end -- @param #string Name. -- @return #boolean Outcome function STRATEGO:IsShip(Name) + self:T(self.lid.."IsShip") if Name and self.airbasetable[Name] then return self.airbasetable[Name].type == STRATEGO.Type.SHIP else @@ -881,6 +965,7 @@ end -- @param #STRATEGO self -- @return #table of #STRATEGO.Target data points function STRATEGO:FindStrategicTargets() + self:T(self.lid.."FindStrategicTargets") local targets = {} for _,_data in pairs(self.airbasetable) do local data = _data -- #STRATEGO.Data @@ -924,6 +1009,7 @@ end -- @param #STRATEGO self -- @return #table of #STRATEGO.Target data points function STRATEGO:FindConsolidationTargets() + self:T(self.lid.."FindConsolidationTargets") local targets = {} for _,_data in pairs(self.airbasetable) do local data = _data -- #STRATEGO.Data @@ -972,6 +1058,7 @@ end -- @param #boolean Friends (optional) If true, find only friendly or neutral neighbors. -- @return #table Neighbors Table of #STRATEGO.DistData entries indexed by neighbor node names. function STRATEGO:FindNeighborNodes(Name,Enemies,Friends) + self:T(self.lid.."FindNeighborNodes") local neighbors = {} local name = string.gsub(Name,"[%p%s]",".") for _route,_data in pairs(self.disttable) do @@ -1008,7 +1095,8 @@ end -- @return #table Route Table of #string name entries of the route -- @return #boolean Complete If true, the route was found end-to-end. function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) - self:I({Start,End,Hops}) + self:T(self.lid.."FindRoute") + --self:I({Start,End,Hops}) --local bases = UTILS.DeepCopy(self.airbasetable) local Route = {} local hops = Hops or 4 @@ -1090,6 +1178,7 @@ end -- @param #number Number of points to add. -- @return #STRATEGO self function STRATEGO:AddBudget(Number) + self:T(self.lid.."AddBudget") self.Budget = self.Budget + Number return self end @@ -1099,6 +1188,7 @@ end -- @param #number Number of points to subtract. -- @return #STRATEGO self function STRATEGO:SubtractBudget(Number) + self:T(self.lid.."SubtractBudget") self.Budget = self.Budget - Number return self end @@ -1107,6 +1197,7 @@ end -- @param #STRATEGO self -- @return #number budget function STRATEGO:GetBudget() + self:T(self.lid.."GetBudget") return self.Budget end @@ -1114,6 +1205,7 @@ end -- @param #STRATEGO self -- @return #table Target Table with #STRATEGO.Target data or nil if none found. function STRATEGO:FindAffordableStrategicTarget() + self:T(self.lid.."FindAffordableStrategicTarget") local targets = self:FindStrategicTargets() -- #table of #STRATEGO.Target local budget = self.Budget --local leftover = self.Budget @@ -1145,6 +1237,7 @@ end -- @param #STRATEGO self -- @return #table Target Table with #STRATEGO.Target data or nil if none found. function STRATEGO:FindAffordableConsolidationTarget() + self:T(self.lid.."FindAffordableConsolidationTarget") local targets = self:FindConsolidationTargets() -- #table of #STRATEGO.Target local budget = self.Budget --local leftover = self.Budget From b90853f43196e4e0b690baab68f13a9f38567faf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 5 Jan 2024 15:36:03 +0100 Subject: [PATCH 476/603] xxx --- Moose Development/Moose/Core/Message.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 46c896f0d..ce95f03c8 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -480,7 +480,7 @@ _MESSAGESRS = {} -- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS() -- function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate) - _MESSAGESRS.MSRS = MSRS:New(PathToSRS,Frequency or 243,Modulation or radio.modulation.AM,Volume) + _MESSAGESRS.MSRS = MSRS:New(PathToSRS,Frequency or 243,Modulation or radio.modulation.AM) _MESSAGESRS.frequency = Frequency _MESSAGESRS.modulation = Modulation or radio.modulation.AM @@ -497,7 +497,7 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G _MESSAGESRS.MSRS:SetGender(Gender) _MESSAGESRS.Gender = Gender or "female" - _MESSAGESRS.MSRS:SetGoogle(PathToCredentials) + _MESSAGESRS.MSRS:SetProviderOptionsGoogle(PathToCredentials) _MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE") _MESSAGESRS.label = Label or "MESSAGE" @@ -543,7 +543,7 @@ function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volum _MESSAGESRS.MSRS:SetCoordinate(coordinate) end local category = string.gsub(self.MessageCategory,":","") - _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency or _MESSAGESRS.frequency,modulation or _MESSAGESRS.modulation, gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,nil,volume or _MESSAGESRS.volume,category,coordinate or _MESSAGESRS.coordinate) + _MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,0.5,1,nil,nil,nil,frequency or _MESSAGESRS.frequency,modulation or _MESSAGESRS.modulation, gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,nil,volume or _MESSAGESRS.volume,category,coordinate or _MESSAGESRS.coordinate) end return self end From 1a1c3a6c9f26e9d703a610bad0611476239c2b75 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 5 Jan 2024 15:41:01 +0100 Subject: [PATCH 477/603] xx --- Moose Development/Moose/Sound/SRS.lua | 45 +++++++++++++++++---------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 559d23c56..134c2780d 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -516,8 +516,20 @@ end -- @return #MSRS self function MSRS:SetBackend(Backend) self:F( {Backend=Backend} ) - self.backend=Backend or MSRS.Backend.SRSEXE - + Backend = Backend or MSRS.Backend.SRSEXE -- avoid nil + local function Checker(back) + local ok = false + for _,_backend in pairs(MSRS.Backend) do + if tostring(back) == _backend then ok = true end + end + return ok + end + + if Checker(Backend) then + self.backend=Backend + else + MESSAGE:New("ERROR: Backend "..tostring(Backend).." is not supported!",30,"MSRS",true):ToLog():ToAll() + end return self end @@ -1184,8 +1196,8 @@ end -- @param Core.Point#COORDINATE Coordinate Coordinate. -- @return #MSRS self function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) - self:F( {Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate} ) - + self:T(( {Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate} ) + self:T((self.lid.."Backend "..self.backend) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) else @@ -1205,6 +1217,7 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture self:_ExecCommand(command) elseif self.backend==MSRS.Backend.GRPC then + BASE:I("MSRS.Backend.GRPC") self:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) @@ -1831,7 +1844,7 @@ end -- @param #MSRSQUEUE self -- @return #MSRSQUEUE self The MSRSQUEUE object. function MSRSQUEUE:Clear() - self:I(self.lid.."Clearing MSRSQUEUE") + self:T((self.lid.."Clearing MSRSQUEUE") self.queue={} return self end @@ -1842,7 +1855,6 @@ end -- @param #MSRSQUEUE.Transmission transmission The transmission data table. -- @return #MSRSQUEUE self function MSRSQUEUE:AddTransmission(transmission) - -- Init. transmission.isplaying=false transmission.Tstarted=nil @@ -1921,20 +1933,20 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr transmission.Tplay=tstart or timer.getAbsTime() transmission.subtitle=subtitle transmission.interval=interval or 0 - transmission.frequency=frequency - transmission.modulation=modulation + transmission.frequency=frequency or msrs.frequencies + transmission.modulation=modulation or msrs.modulations transmission.subgroups=subgroups if transmission.subtitle then transmission.subduration=subduration or transmission.duration else transmission.subduration=0 --nil end - transmission.gender = gender - transmission.culture = culture - transmission.voice = voice - transmission.volume = volume - transmission.label = label - transmission.coordinate = coordinate + transmission.gender = gender or msrs.gender + transmission.culture = culture or msrs.culture + transmission.voice = voice or msrs.voice + transmission.volume = volume or msrs.volume + transmission.label = label or msrs.Label + transmission.coordinate = coordinate or msrs.coordinate -- Add transmission to queue. self:AddTransmission(transmission) @@ -1946,7 +1958,8 @@ end -- @param #MSRSQUEUE self -- @param #MSRSQUEUE.Transmission transmission The transmission. function MSRSQUEUE:Broadcast(transmission) - + self:T((self.lid.."Broadcast") + if transmission.frequency then transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label, transmission.coordinate) else @@ -2115,7 +2128,7 @@ function MSRSQUEUE:_CheckRadioQueue(delay) -- Found a new transmission. if next~=nil and not playing then -- Debug info. - self:T(self.lid..string.format("Broadcasting text=\"%s\" at T=%.3f", next.text, time)) + self:T((self.lid..string.format("Broadcasting text=\"%s\" at T=%.3f", next.text, time)) -- Call SRS. self:Broadcast(next) From 0db96082a101ea9e61ca46c7c6b83091710b79d3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 6 Jan 2024 18:17:56 +0100 Subject: [PATCH 478/603] xxx --- Moose Development/Moose/Core/Database.lua | 12 ++++- Moose Development/Moose/Core/Message.lua | 58 +++++++++++++---------- Moose Development/Moose/Ops/ATIS.lua | 35 +++++++++----- 3 files changed, 67 insertions(+), 38 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 009418497..14b99fc76 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1343,9 +1343,17 @@ function DATABASE:_RegisterAirbase(airbase) -- Unique ID. local airbaseUID=airbase:GetID(true) - + + local typename = airbase:GetTypeName() + + local category = airbase.category + + if category == Airbase.Category.SHIP and typename == "FARP_SINGLE_01" then + category = Airbase.Category.HELIPAD + end + -- Debug output. - local text=string.format("Register %s: %s (UID=%d), Runways=%d, Parking=%d [", AIRBASE.CategoryName[airbase.category], tostring(DCSAirbaseName), airbaseUID, #airbase.runways, airbase.NparkingTotal) + local text=string.format("Register %s: %s (UID=%d), Runways=%d, Parking=%d [", AIRBASE.CategoryName[category], tostring(DCSAirbaseName), airbaseUID, #airbase.runways, airbase.NparkingTotal) for _,terminalType in pairs(AIRBASE.TerminalType) do if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType] then text=text..string.format("%d=%d ", terminalType, airbase.NparkingTerminal[terminalType]) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index ce95f03c8..31669d458 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -459,14 +459,14 @@ end _MESSAGESRS = {} ---- Set up MESSAGE generally to allow Text-To-Speech via SRS and TTS functions. --- @param #string PathToSRS Path to SRS Folder, defaults to "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone". --- @param #number Port Port number of SRS, defaults to 5002. --- @param #string PathToCredentials (optional) Path to credentials file for e.g. Google. +--- Set up MESSAGE generally to allow Text-To-Speech via SRS and TTS functions. `SetMSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible. +-- @param #string PathToSRS (optional) Path to SRS Folder, defaults to "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone" or your configuration file setting. +-- @param #number Port Port (optional) number of SRS, defaults to 5002 or your configuration file setting. +-- @param #string PathToCredentials (optional) Path to credentials file for Google. -- @param #number Frequency Frequency in MHz. Can also be given as a #table of frequencies. -- @param #number Modulation Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. --- @param #string Gender (optional) Gender, i.e. "male" or "female", defaults to "female". --- @param #string Culture (optional) Culture, e.g. "en-US", defaults to "en-GB" +-- @param #string Gender (optional) Gender, i.e. "male" or "female", defaults to "female" or your configuration file setting. +-- @param #string Culture (optional) Culture, e.g. "en-US", defaults to "en-GB" or your configuration file setting. -- @param #string Voice (optional) Voice. Will override gender and culture settings, e.g. MSRS.Voices.Microsoft.Hazel or MSRS.Voices.Google.Standard.de_DE_Standard_D. Hint on Microsoft voices - working voices are limited to Hedda, Hazel, David, Zira and Hortense. **Must** be installed on your Desktop or Server! -- @param #number Coalition (optional) Coalition, can be coalition.side.RED, coalition.side.BLUE or coalition.side.NEUTRAL. Defaults to coalition.side.NEUTRAL. -- @param #number Volume (optional) Volume, can be between 0.0 and 1.0 (loudest). @@ -480,42 +480,50 @@ _MESSAGESRS = {} -- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS() -- function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate) - _MESSAGESRS.MSRS = MSRS:New(PathToSRS,Frequency or 243,Modulation or radio.modulation.AM) - _MESSAGESRS.frequency = Frequency - _MESSAGESRS.modulation = Modulation or radio.modulation.AM + _MESSAGESRS.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - _MESSAGESRS.MSRS:SetCoalition(Coalition or coalition.side.NEUTRAL) - _MESSAGESRS.coalition = Coalition or coalition.side.NEUTRAL + _MESSAGESRS.frequency = Frequency or MSRS.frequencies or 243 + _MESSAGESRS.modulation = Modulation or MSRS.modulations or radio.modulation.AM + _MESSAGESRS.MSRS = MSRS:New(_MESSAGESRS.PathToSRS,_MESSAGESRS.frequency, _MESSAGESRS.modulation) + + _MESSAGESRS.coalition = Coalition or MSRS.coalition or coalition.side.NEUTRAL + _MESSAGESRS.MSRS:SetCoalition(_MESSAGESRS.coalition) + _MESSAGESRS.coordinate = Coordinate - _MESSAGESRS.MSRS:SetCoordinate(Coordinate) + if Coordinate then + _MESSAGESRS.MSRS:SetCoordinate(Coordinate) + end + + _MESSAGESRS.Culture = Culture or MSRS.culture or "en-GB" _MESSAGESRS.MSRS:SetCulture(Culture) - _MESSAGESRS.Culture = Culture or "en-GB" + _MESSAGESRS.Gender = Gender or MSRS.gender or "female" _MESSAGESRS.MSRS:SetGender(Gender) - _MESSAGESRS.Gender = Gender or "female" - _MESSAGESRS.MSRS:SetProviderOptionsGoogle(PathToCredentials) - - _MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE") - _MESSAGESRS.label = Label or "MESSAGE" - - _MESSAGESRS.MSRS:SetPort(Port or 5002) - _MESSAGESRS.port = Port or 5002 + if PathToCredentials then + _MESSAGESRS.MSRS:SetProviderOptionsGoogle(PathToCredentials) + end - _MESSAGESRS.volume = Volume or 1 + _MESSAGESRS.label = Label or MSRS.Label or "MESSAGE" + _MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE") + + _MESSAGESRS.port = Port or MSRS.port or 5002 + _MESSAGESRS.MSRS:SetPort(Port or 5002) + + _MESSAGESRS.volume = Volume or MSRS.volume or 1 _MESSAGESRS.MSRS:SetVolume(_MESSAGESRS.volume) if Voice then _MESSAGESRS.MSRS:SetVoice(Voice) end - _MESSAGESRS.voice = Voice --or MSRS.Voices.Microsoft.Hedda + _MESSAGESRS.voice = Voice or MSRS.voice --or MSRS.Voices.Microsoft.Hedda - _MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE") + _MESSAGESRS.SRSQ = MSRSQUEUE:New(_MESSAGESRS.label) end ---- Sends a message via SRS. +--- Sends a message via SRS. `ToSRS()` will try to use as many attributes configured with @{Core.Message#MESSAGE.SetMSRS}() and @{Sound.SRS#MSRS.LoadConfigFile}() as possible. -- @param #MESSAGE self -- @param #number frequency (optional) Frequency in MHz. Can also be given as a #table of frequencies. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. -- @param #number modulation (optional) Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting. diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 064ab1880..0fcf939b5 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1526,7 +1526,7 @@ function ATIS:MarkRunways( markall ) end end ---- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary. +--- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary.`SetSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible. -- @param #ATIS self -- @param #string PathToSRS Path to SRS directory (only necessary if SRS exe backend is used). -- @param #string Gender Gender: "male" or "female" (default). @@ -1536,25 +1536,38 @@ end -- @param #string GoogleKey Path to Google JSON-Key (SRS exe backend) or Google API key (DCS-gRPC backend). -- @return #ATIS self function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) - if PathToSRS or MSRS.path then + --if PathToSRS or MSRS.path then self.useSRS=true - self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation) - self.msrs:SetGender(Gender) - self.msrs:SetCulture(Culture) - self.msrs:SetVoice(Voice) - self.msrs:SetPort(Port) + + local path = PathToSRS or MSRS.path + local gender = Gender or MSRS.gender + local culture = Culture or MSRS.culture + local voice = Voice or MSRS.voice + local port = Port or MSRS.port or 5002 + + self.msrs=MSRS:New(path, self.frequency, self.modulation) + self.msrs:SetGender(gender) + self.msrs:SetCulture(culture) + self.msrs:SetPort(port) self.msrs:SetCoalition(self:GetCoalition()) self.msrs:SetLabel("ATIS") - self.msrs:SetGoogle(GoogleKey) + if GoogleKey then + self.msrs:SetProviderOptionsGoogle(GoogleKey,GoogleKey) + end + -- Pre-configured Google? + if self.msrs:GetProvider() == MSRS.Provider.GOOGLE then + voice = Voice or MSRS.poptions.gcloud.voice + end + self.msrs:SetVoice(voice) self.msrs:SetCoordinate(self.airbase:GetCoordinate()) self.msrsQ = MSRSQUEUE:New("ATIS") self.msrsQ:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers) if self.dTQueueCheck<=10 then self:SetQueueUpdateTime(90) end - else - self:E(self.lid..string.format("ERROR: No SRS path specified!")) - end + --else + --self:E(self.lid..string.format("ERROR: No SRS path specified!")) + --end return self end From cfe99341d71510c4977710915eb9245fb296c058 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 6 Jan 2024 18:20:55 +0100 Subject: [PATCH 479/603] STRATEGO * Fix if an airbase has no zone predefined --- Moose Development/Moose/Functional/Stratego.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index f8c9e8c07..43752ea7e 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -176,7 +176,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.2", + version = "0.2.3", portweight = 3, POIweight = 1, maxrunways = 3, @@ -431,6 +431,9 @@ function STRATEGO:AnalyseBases() local numrwys = #runways if numrwys >= 1 then numrwys = numrwys * 0.5 end local abzone = ab:GetZone() + if not abzone then + abzone = ZONE_RADIUS:New(abname,ab:GetVec2(),500) + end local coa = ab:GetCoalition() + 1 local abtype = "AIRBASE" if ab:IsShip() then @@ -441,7 +444,7 @@ function STRATEGO:AnalyseBases() numrwys = 1 abtype = "FARP" end - local coord = abzone:GetCoordinate() + local coord = ab:GetCoordinate() if debug then abzone:DrawZone(-1,colors[coa],1,colors[coa],0.3,1) coord:TextToAll(tostring(numrwys),-1,{0,0,0},1,colors[coa],0.3,20) From 45fb191483af55eadb0a346e0a29d17ba4ffd4b1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 6 Jan 2024 18:21:10 +0100 Subject: [PATCH 480/603] Fixes for MSRS changes --- Moose Development/Moose/Ops/Awacs.lua | 27 +++++++++++++--------- Moose Development/Moose/Ops/PlayerTask.lua | 24 +++++++++++-------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 7c9c0a67b..f0d815ccd 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -2071,7 +2071,7 @@ function AWACS:AddGroupToDetection(Group) return self end ---- [User] Set AWACS SRS TTS details - see @{Sound.SRS} for details +--- [User] Set AWACS SRS TTS details - see @{Sound.SRS} for details. `SetSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible. -- @param #AWACS self -- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone" -- @param #string Gender Defaults to "male" @@ -2080,16 +2080,16 @@ end -- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume Volume - between 0.0 (silent) and 1.0 (loudest) --- @param #string PathToGoogleKey Path to your google key if you want to use google TTS --- @param #string AccessKey Your Google API access key. This is necessary if DCS-gRPC is used as backend. +-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS; if you use a config file for MSRS, hand in nil here. +-- @param #string AccessKey (Optional) Your Google API access key. This is necessary if DCS-gRPC is used as backend; if you use a config file for MSRS, hand in nil here. -- @return #AWACS self function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey) self:T(self.lid.."SetSRS") - self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - self.Gender = Gender or "male" - self.Culture = Culture or "en-US" - self.Port = Port or 5002 - self.Voice = Voice + self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + self.Gender = Gender or MSRS.gender or "male" + self.Culture = Culture or MSRS.culture or "en-US" + self.Port = Port or MSRS.port or 5002 + self.Voice = Voice or MSRS.voice self.PathToGoogleKey = PathToGoogleKey self.AccessKey = AccessKey self.Volume = Volume or 1.0 @@ -2098,7 +2098,6 @@ function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey self.AwacsSRS:SetCoalition(self.coalition) self.AwacsSRS:SetGender(self.Gender) self.AwacsSRS:SetCulture(self.Culture) - self.AwacsSRS:SetVoice(self.Voice) self.AwacsSRS:SetPort(self.Port) self.AwacsSRS:SetLabel("AWACS") self.AwacsSRS:SetVolume(Volume) @@ -2106,7 +2105,13 @@ function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey --self.AwacsSRS:SetGoogle(self.PathToGoogleKey) self.AwacsSRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) end - + -- Pre-configured Google? + if self.AwacsSRS:GetProvider() == MSRS.Provider.GOOGLE then + self.PathToGoogleKey = MSRS.poptions.gcloud.credentials + self.Voice = Voice or MSRS.poptions.gcloud.voice + self.AccessKey = AccessKey or MSRS.poptions.gcloud.key + end + self.AwacsSRS:SetVoice(self.Voice) return self end @@ -3669,7 +3674,7 @@ function AWACS:_CheckInAI(FlightGroup,Group,AuftragsNr) CAPVoice = self.CapVoices[math.floor(math.random(1,10))] end - FlightGroup:SetSRS(self.PathToSRS,self.CAPGender,self.CAPCulture,CAPVoice,self.Port,self.PathToGoogleKey,"FLIGHT") + FlightGroup:SetSRS(self.PathToSRS,self.CAPGender,self.CAPCulture,CAPVoice,self.Port,self.PathToGoogleKey,"FLIGHT",1) local checkai = self.gettext:GetEntry("CHECKINAI",self.locale) text = string.format(checkai,self.callsigntxt, managedgroup.CallSign, self.CAPTimeOnStation, self.AOName) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 79ffe5803..855b954ca 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -3993,7 +3993,7 @@ function PLAYERTASKCONTROLLER:SetupIntel(RecceName) return self end ---- [User] Set SRS TTS details - see @{Sound.SRS} for details +--- [User] Set SRS TTS details - see @{Sound.SRS} for details.`SetSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible. -- @param #PLAYERTASKCONTROLLER self -- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations! -- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies! @@ -4004,17 +4004,17 @@ end -- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) --- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS --- @param #string AccessKey Your Google API access key. This is necessary if DCS-gRPC is used as backend. +-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS; if you use a config file for MSRS, hand in nil here. +-- @param #string AccessKey (Optional) Your Google API access key. This is necessary if DCS-gRPC is used as backend; if you use a config file for MSRS, hand in nil here. -- @param Core.Point#COORDINATE Coordinate Coordinate from which the controller radio is sending -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate) self:T(self.lid.."SetSRS") - self.PathToSRS = PathToSRS or "C:\\Program Files\\DCS-SimpleRadio-Standalone" -- - self.Gender = Gender or "male" -- - self.Culture = Culture or "en-US" -- - self.Port = Port or 5002 -- - self.Voice = Voice -- + self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" -- + self.Gender = Gender or MSRS.gender or "male" -- + self.Culture = Culture or MSRS.culture or "en-US" -- + self.Port = Port or MSRS.port or 5002 -- + self.Voice = Voice or MSRS.voice self.PathToGoogleKey = PathToGoogleKey -- self.AccessKey = AccessKey self.Volume = Volume or 1.0 -- @@ -4030,15 +4030,21 @@ function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Cultu self.SRS:SetGender(self.Gender) self.SRS:SetCulture(self.Culture) self.SRS:SetPort(self.Port) - self.SRS:SetVoice(self.Voice) self.SRS:SetVolume(self.Volume) if self.PathToGoogleKey then --self.SRS:SetGoogle(self.PathToGoogleKey) self.SRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) + end + -- Pre-configured Google? + if self.SRS:GetProvider() == MSRS.Provider.GOOGLE then + self.PathToGoogleKey = MSRS.poptions.gcloud.credentials + self.Voice = Voice or MSRS.poptions.gcloud.voice + self.AccessKey = AccessKey or MSRS.poptions.gcloud.key end if Coordinate then self.SRS:SetCoordinate(Coordinate) end + self.SRS:SetVoice(self.Voice) self.SRSQueue = MSRSQUEUE:New(self.MenuName or self.Name) self.SRSQueue:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers) return self From 7e842fbd46a90f1c2bb288d78c08baa33b95e00c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 7 Jan 2024 13:16:52 +0100 Subject: [PATCH 481/603] xxx --- Moose Development/Moose/Core/Message.lua | 1 + Moose Development/Moose/Sound/RadioQueue.lua | 6 ++++-- Moose Development/Moose/Sound/SRS.lua | 14 +++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 31669d458..1ffcb1516 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -505,6 +505,7 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G if PathToCredentials then _MESSAGESRS.MSRS:SetProviderOptionsGoogle(PathToCredentials) + _MESSAGESRS.MSRS:SetProvider(MSRS.Provider.GOOGLE) end _MESSAGESRS.label = Label or MSRS.Label or "MESSAGE" diff --git a/Moose Development/Moose/Sound/RadioQueue.lua b/Moose Development/Moose/Sound/RadioQueue.lua index 80ac49752..7f320cedc 100644 --- a/Moose Development/Moose/Sound/RadioQueue.lua +++ b/Moose Development/Moose/Sound/RadioQueue.lua @@ -183,8 +183,10 @@ end -- @param #number Port SRS port. Default 5002. -- @return #RADIOQUEUE self The RADIOQUEUE object. function RADIOQUEUE:SetSRS(PathToSRS, Port) - self.msrs=MSRS:New(PathToSRS, self.frequency/1000000, self.modulation) - self.msrs:SetPort(Port) + local path = PathToSRS or MSRS.path + local port = Port or MSRS.port + self.msrs=MSRS:New(path, self.frequency/1000000, self.modulation) + self.msrs:SetPort(port) return self end diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 0d8a8f05b..b0ac95894 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -918,12 +918,16 @@ end -- @param #string Provider -- @return #MSRS self function MSRS:SetProvider(Provider) - self:F( {Provider=Provider} ) - self.provider = Provider or MSRS.Provider.WINDOWS - return self + BASE:F( {Provider=Provider} ) + if self then + self.provider = Provider or MSRS.Provider.WINDOWS + return self + else + MSRS.provider = Provider or MSRS.Provider.WINDOWS + end + return end - --- Get provider. -- @param #MSRS self -- @return #MSRS self @@ -940,7 +944,7 @@ end -- @param #string Region Region to use. -- @return #MSRS.ProviderOptions Provider optionas table. function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) - self:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) + BASE:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) local option=MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) if self then From cf4d35efa28aa69e44f422706cdc28c17ad4ead3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 7 Jan 2024 13:25:14 +0100 Subject: [PATCH 482/603] xxx --- Moose Development/Moose/Ops/ATIS.lua | 17 ++++++++- Moose Development/Moose/Ops/Awacs.lua | 4 +- Moose Development/Moose/Ops/FlightControl.lua | 37 +++++++++++-------- Moose Development/Moose/Ops/OpsGroup.lua | 9 +++-- Moose Development/Moose/Ops/PlayerRecce.lua | 8 +++- Moose Development/Moose/Ops/PlayerTask.lua | 3 +- 6 files changed, 56 insertions(+), 22 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 0fcf939b5..d07c751f1 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1553,9 +1553,10 @@ function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) self.msrs:SetLabel("ATIS") if GoogleKey then self.msrs:SetProviderOptionsGoogle(GoogleKey,GoogleKey) + self.msrs:SetProvider(MSRS.Provider.GOOGLE) end -- Pre-configured Google? - if self.msrs:GetProvider() == MSRS.Provider.GOOGLE then + if (not GoogleKey) and self.msrs:GetProvider() == MSRS.Provider.GOOGLE then voice = Voice or MSRS.poptions.gcloud.voice end self.msrs:SetVoice(voice) @@ -1571,6 +1572,20 @@ function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) return self end +--- Set an alternative provider to the one set in your MSRS configuration file. +-- @param #ATIS self +-- @param #string Provider The provider to use. Known providers are: `MSRS.Provider.WINDOWS` and `MSRS.Provider.GOOGLE` +-- @return #ATIS self +function ATIS:SetSRSProvider(Provider) + self:T(self.lid.."SetSRSProvider") + if self.msrs then + self.msrs:SetProvider(Provider) + else + MESSAGE:New(self.lid.."Set up SRS first before trying to change the provider!",30,"ATIS"):ToAll():ToLog() + end + return self +end + --- Set the time interval between radio queue updates. -- @param #ATIS self -- @param #number TimeInterval Interval in seconds. Default 5 sec. diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index f0d815ccd..704f52f6f 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1416,6 +1416,7 @@ function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number) if self.PathToGoogleKey then --self.TacticalSRS:SetGoogle(self.PathToGoogleKey) self.TacticalSRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) + self.TacticalSRS:SetProvider(MSRS.Provider.GOOGLE) end self.TacticalSRSQ = MSRSQUEUE:New("Tactical AWACS") end @@ -2104,9 +2105,10 @@ function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey if self.PathToGoogleKey then --self.AwacsSRS:SetGoogle(self.PathToGoogleKey) self.AwacsSRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) + self.AwacsSRS:SetProvider(MSRS.Provider.GOOGLE) end -- Pre-configured Google? - if self.AwacsSRS:GetProvider() == MSRS.Provider.GOOGLE then + if (not PathToGoogleKey) and self.AwacsSRS:GetProvider() == MSRS.Provider.GOOGLE then self.PathToGoogleKey = MSRS.poptions.gcloud.credentials self.Voice = Voice or MSRS.poptions.gcloud.voice self.AccessKey = AccessKey or MSRS.poptions.gcloud.key diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 651c6dc27..d918956e1 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -411,26 +411,36 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port, self:SetRunwayRepairtime() self.nosubs = false - -- Set SRS Port - self:SetSRSPort(Port or 5002) - -- Set Callsign Options self:SetCallSignOptions(true,true) -- Init msrs queue. self.msrsqueue=MSRSQUEUE:New(self.alias) + -- Init msrs bases + local path = PathToSRS or MSRS.path + local port = Port or MSRS.port or 5002 + + -- Set SRS Port + self:SetSRSPort(port) + -- SRS for Tower. - self.msrsTower=MSRS:New(PathToSRS, Frequency, Modulation) - self.msrsTower:SetPort(self.Port) - self.msrsTower:SetGoogle(GoogleKey) + self.msrsTower=MSRS:New(path, Frequency, Modulation) + self.msrsTower:SetPort(port) + if GoogleKey then + self.msrsTower:SetProviderOptionsGoogle(GoogleKey,GoogleKey) + self.msrsTower:SetProvider(MSRS.Provider.GOOGLE) + end self.msrsTower:SetCoordinate(self:GetCoordinate()) self:SetSRSTower() -- SRS for Pilot. self.msrsPilot=MSRS:New(PathToSRS, Frequency, Modulation) self.msrsPilot:SetPort(self.Port) - self.msrsPilot:SetGoogle(GoogleKey) + if GoogleKey then + self.msrsPilot:SetProviderOptionsGoogle(GoogleKey,GoogleKey) + self.msrsPilot:SetProvider(MSRS.Provider.GOOGLE) + end self.msrsTower:SetCoordinate(self:GetCoordinate()) self:SetSRSPilot() @@ -633,7 +643,6 @@ function FLIGHTCONTROL:_SetSRSOptions(msrs, Gender, Culture, Voice, Volume, Labe msrs:SetVoice(Voice) msrs:SetVolume(Volume) msrs:SetLabel(Label) - msrs:SetGoogle(PathToGoogleCredentials) msrs:SetCoalition(self:GetCoalition()) msrs:SetPort(Port or self.Port or 5002) end @@ -648,12 +657,11 @@ end -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. See [Google Voices](https://cloud.google.com/text-to-speech/docs/voices). -- @param #number Volume Volume. Default 1.0. -- @param #string Label Name under which SRS transmits. Default `self.alias`. --- @param #string PathToGoogleCredentials Path to google credentials json file. -- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:SetSRSTower(Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials) +function FLIGHTCONTROL:SetSRSTower(Gender, Culture, Voice, Volume, Label) if self.msrsTower then - self:_SetSRSOptions(self.msrsTower, Gender or "female", Culture or "en-GB", Voice, Volume, Label or self.alias, PathToGoogleCredentials) + self:_SetSRSOptions(self.msrsTower, Gender or "female", Culture or "en-GB", Voice, Volume, Label or self.alias) end return self @@ -666,12 +674,11 @@ end -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. -- @param #number Volume Volume. Default 1.0. -- @param #string Label Name under which SRS transmits. Default "Pilot". --- @param #string PathToGoogleCredentials Path to google credentials json file. -- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:SetSRSPilot(Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials) +function FLIGHTCONTROL:SetSRSPilot(Gender, Culture, Voice, Volume, Label) if self.msrsPilot then - self:_SetSRSOptions(self.msrsPilot, Gender or "male", Culture or "en-US", Voice, Volume, Label or "Pilot", PathToGoogleCredentials) + self:_SetSRSOptions(self.msrsPilot, Gender or "male", Culture or "en-US", Voice, Volume, Label or "Pilot") end return self @@ -876,7 +883,7 @@ end --- Set ATIS. -- @param #FLIGHTCONTROL self --- @param Ops.ATIS#ATIS ATIS ATIS. +-- @param Ops.ATIS#ATIS Atis ATIS. -- @return #FLIGHTCONTROL self function FLIGHTCONTROL:SetATIS(Atis) self.atis=Atis diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 1566e6634..fb9162521 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -2314,14 +2314,17 @@ end -- @return #OPSGROUP self function OPSGROUP:SetSRS(PathToSRS, Gender, Culture, Voice, Port, PathToGoogleKey, Label, Volume) self.useSRS=true - self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation) + local path = PathToSRS or MSRS.path + local port = Port or MSRS.port + self.msrs=MSRS:New(path, self.frequency, self.modulation) self.msrs:SetGender(Gender) self.msrs:SetCulture(Culture) self.msrs:SetVoice(Voice) - self.msrs:SetPort(Port) + self.msrs:SetPort(port) self.msrs:SetLabel(Label) if PathToGoogleKey then - self.msrs:SetGoogle(PathToGoogleKey) + self.msrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey) + self.msrs:SetProvider(MSRS.Provider.GOOGLE) end self.msrs:SetCoalition(self:GetCoalition()) self.msrs:SetVolume(Volume) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 5a87c3be1..7cac793b1 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1517,7 +1517,13 @@ function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,V self.SRS:SetVoice(self.Voice) self.SRS:SetVolume(self.Volume) if self.PathToGoogleKey then - self.SRS:SetGoogle(self.PathToGoogleKey) + self.SRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.PathToGoogleKey) + self.SRS:SetProvider(MSRS.Provider.GOOGLE) + end + -- Pre-configured Google? + if (not PathToGoogleKey) and self.AwacsSRS:GetProvider() == MSRS.Provider.GOOGLE then + self.PathToGoogleKey = MSRS.poptions.gcloud.credentials + self.Voice = Voice or MSRS.poptions.gcloud.voice end self.SRSQueue = MSRSQUEUE:New(self.MenuName or self.Name) self.SRSQueue:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 855b954ca..0b95512e4 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -4034,9 +4034,10 @@ function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Cultu if self.PathToGoogleKey then --self.SRS:SetGoogle(self.PathToGoogleKey) self.SRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) + self.SRS:SetProvider(MSRS.Provider.GOOGLE) end -- Pre-configured Google? - if self.SRS:GetProvider() == MSRS.Provider.GOOGLE then + if (not PathToGoogleKey) and self.SRS:GetProvider() == MSRS.Provider.GOOGLE then self.PathToGoogleKey = MSRS.poptions.gcloud.credentials self.Voice = Voice or MSRS.poptions.gcloud.voice self.AccessKey = AccessKey or MSRS.poptions.gcloud.key From e5658b32986fd1dc669587d6bb9478f15418f448 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 7 Jan 2024 14:45:10 +0100 Subject: [PATCH 483/603] xxx --- Moose Development/Moose/Sound/SRS.lua | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index b0ac95894..ec38d5862 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -272,6 +272,13 @@ MSRS.Voices = { ["Zira"] = "Microsoft Zira Desktop", -- en-US ["Hortense"] = "Microsoft Hortense Desktop", --fr-FR }, + MicrosoftGRPC = { + ["Hedda"] = "Hedda", -- de-DE + ["Hazel"] = "Hazel", -- en-GB + ["David"] = "David", -- en-US + ["Zira"] = "Zira", -- en-US + ["Hortense"] = "Hortense", --fr-FR + }, Google = { Standard = { ["en_AU_Standard_A"] = 'en-AU-Standard-A', -- [1] FEMALE @@ -1610,7 +1617,7 @@ end -- -- -- Moose MSRS default Config -- MSRS_Config = { --- Path = C:\\Program Files\\DCS-SimpleRadio-Standalone, -- Path to SRS install directory. +-- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- Path to SRS install directory. -- Port = 5002, -- Port of SRS server. Default 5002. -- Backend = "srsexe", -- Interface to SRS: "srsexe" or "grpc". -- Frequency = {127, 243}, -- Default frequences. Must be a table 1..n entries! @@ -1622,8 +1629,7 @@ end -- Gender = "male", -- Voice = "Microsoft Hazel Desktop", -- Voice that is used if no explicit provider voice is specified. -- Label = "MSRS", --- Provider = "win", --Provider for generating TTS (win, gcloud, azure, aws). --- +-- Provider = "win", --Provider for generating TTS (win, gcloud, azure, aws). -- -- Windows -- win = { -- voice = "Microsoft Hazel Desktop", @@ -1649,7 +1655,7 @@ end -- }, -- } -- --- 3) The config file is automatically loaded when Moose starts. YOu can also load the config into the MSRS raw class manually before you do anything else: +-- 3) The config file is automatically loaded when Moose starts. You can also load the config into the MSRS raw class manually before you do anything else: -- -- MSRS.LoadConfigFile() -- Note the "." here -- @@ -1665,8 +1671,7 @@ end -- 4) Use the config in your code like so, variable names are basically the same as in the config file, but all lower case, examples: -- -- -- Needed once only --- MESSAGE.SetMSRS(MSRS.path,nil,MSRS.google,243,radio.modulation.AM,nil,nil, --- MSRS.Voices.Google.Standard.de_DE_Standard_B,coalition.side.BLUE) +-- MESSAGE.SetMSRS(MSRS.path,MSRS.port,nil,127,rado.modulation.FM,nil,nil,nil,nil,nil,"TALK") -- -- -- later on in your code -- From 20f881f0a9c37d575532e16b25260abfad0ed450 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 7 Jan 2024 15:40:08 +0100 Subject: [PATCH 484/603] #AWACS * Picture clean, correct order of callsigns --- Moose Development/Moose/Ops/Awacs.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 704f52f6f..234d727de 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -507,7 +507,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.60", -- #string + version = "0.2.61", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -2948,7 +2948,7 @@ function AWACS:_Picture(Group,IsGeneral) if not self.intel then -- no intel yet! local picclean = self.gettext:GetEntry("PICCLEAN",self.locale) - text = string.format(picclean,self.callsigntxt, gcallsign) + text = string.format(picclean,gcallsign,self.callsigntxt) textScreen = text self:_NewRadioEntry(text,text,GID,false,true,true,false) From 9211770056a14ce48313dd76493cd4d810f7701f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 9 Jan 2024 08:58:22 +0100 Subject: [PATCH 485/603] xx --- Moose Development/Moose/Sound/SRS.lua | 35 ++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index ec38d5862..bbb9800d7 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -271,13 +271,42 @@ MSRS.Voices = { ["David"] = "Microsoft David Desktop", -- en-US ["Zira"] = "Microsoft Zira Desktop", -- en-US ["Hortense"] = "Microsoft Hortense Desktop", --fr-FR + ["de-DE-Hedda"] = "Microsoft Hedda Desktop", -- de-DE + ["en-GB-Hazel"] = "Microsoft Hazel Desktop", -- en-GB + ["en-US-David"] = "Microsoft David Desktop", -- en-US + ["en-US-Zira"] = "Microsoft Zira Desktop", -- en-US + ["fr-FR-Hortense"] = "Microsoft Hortense Desktop", --fr-FR }, - MicrosoftGRPC = { - ["Hedda"] = "Hedda", -- de-DE + MicrosoftGRPC = { -- en-US/GB voices only as of Jan 2024 + --["Hedda"] = "Hedda", -- de-DE ["Hazel"] = "Hazel", -- en-GB + ["George"] = "George", -- en-GB + ["Susan"] = "Susan", -- en-GB ["David"] = "David", -- en-US ["Zira"] = "Zira", -- en-US - ["Hortense"] = "Hortense", --fr-FR + ["Mark"] = "Mark", -- en-US + --[[ + ["James"] = "James", --en-AU + ["Catherine"] = "Catherine", --en-AU + ["Richard"] = "Richard", --en-CA + ["Linda"] = "Linda", --en-CA + ["Ravi"] = "Ravi", --en-IN + ["Heera"] = "Heera", --en-IN + ["Sean"] = "Sean", --en-IR + ["en_GB_Hazel"] = "Hazel", -- en-GB + ["en_GB_George"] = "George", -- en-GB + ["en_GB_Susan"] = "Susan", -- en-GB + ["en_US_David"] = "David", -- en-US + ["en_US_Zira"] = "Zira", -- en-US + ["en_US_Mark"] = "Mark", -- en-US + ["en_AU_James"] = "James", --en-AU + ["en_AU_Catherine"] = "Catherine", --en-AU + ["en_CA_Richard"] = "Richard", --en-CA + ["en_CA_Linda"] = "Linda", --en-CA + ["en_IN_Ravi"] = "Ravi", --en-IN + ["en_IN_Heera"] = "Heera", --en-IN + ["en_IR_Sean"] = "Sean", --en-IR + --]] }, Google = { Standard = { From 7995940579bf1d930841e7da44288b0cda82ee37 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 9 Jan 2024 17:27:26 +0100 Subject: [PATCH 486/603] SRS changes --- Moose Development/Moose/Ops/Airboss.lua | 4 ++-- Moose Development/Moose/Sound/SRS.lua | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 28ccad0c9..1b9355b11 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -3062,7 +3062,7 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum -- SRS local Frequency = self.AirbossRadio.frequency local Modulation = self.AirbossRadio.modulation - self.SRS = MSRS:New(PathToSRS,Frequency,Modulation,Volume,AltBackend) + self.SRS = MSRS:New(PathToSRS,Frequency,Modulation,AltBackend) self.SRS:SetCoalition(self:GetCoalition()) self.SRS:SetCoordinate(self:GetCoordinate()) self.SRS:SetCulture(Culture or "en-US") @@ -3072,7 +3072,7 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum self.SRS:SetPort(Port or 5002) self.SRS:SetLabel(self.AirbossRadio.alias or "AIRBOSS") self.SRS:SetCoordinate(self.carrier:GetCoordinate()) - self.SRS:SetVolume(Volume) + self.SRS:SetVolume(Volume or 1) --self.SRS:SetModulations(Modulations) if GoogleCreds then self.SRS:SetGoogle(GoogleCreds) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index bbb9800d7..9d075dfd0 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -265,7 +265,7 @@ MSRS.version="0.3.0" --- Voices -- @type MSRS.Voices MSRS.Voices = { - Microsoft = { + Microsoft = { -- working ones if not using gRPC and MS ["Hedda"] = "Microsoft Hedda Desktop", -- de-DE ["Hazel"] = "Microsoft Hazel Desktop", -- en-GB ["David"] = "Microsoft David Desktop", -- en-US @@ -277,7 +277,7 @@ MSRS.Voices = { ["en-US-Zira"] = "Microsoft Zira Desktop", -- en-US ["fr-FR-Hortense"] = "Microsoft Hortense Desktop", --fr-FR }, - MicrosoftGRPC = { -- en-US/GB voices only as of Jan 2024 + MicrosoftGRPC = { -- en-US/GB voices only as of Jan 2024, working ones if using gRPC and MS, if voice packs are installed --["Hedda"] = "Hedda", -- de-DE ["Hazel"] = "Hazel", -- en-GB ["George"] = "George", -- en-GB @@ -285,7 +285,6 @@ MSRS.Voices = { ["David"] = "David", -- en-US ["Zira"] = "Zira", -- en-US ["Mark"] = "Mark", -- en-US - --[[ ["James"] = "James", --en-AU ["Catherine"] = "Catherine", --en-AU ["Richard"] = "Richard", --en-CA From 27158ee7e8d6c516ee3c92426183e90a35a457f4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 10 Jan 2024 13:38:59 +0100 Subject: [PATCH 487/603] xxx --- Moose Development/Moose/Ops/EasyGCICAP.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 534534fab..401d5e056 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -258,10 +258,10 @@ EASYGCICAP.version="0.0.9" --- Create a new GCICAP Manager -- @param #EASYGCICAP self --- @param #string Alias --- @param #string AirbaseName --- @param #string Coalition --- @param #string EWRName +-- @param #string Alias A Name for this GCICAP +-- @param #string AirbaseName Name of the Home Airbase +-- @param #string Coalition Coalition, e.g. "blue" or "red" +-- @param #string EWRName (Partial) group name of the EWR system of the coalition, e.g. "Red EWR" -- @return #EASYGCICAP self function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName) -- Inherit everything from FSM class. @@ -491,7 +491,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) -- Create Airwing local CAP_Wing = AIRWING:New(Airbasename,Alias) - CAP_Wing:SetVerbosityLevel(3) + CAP_Wing:SetVerbosityLevel(0) CAP_Wing:SetReportOff() CAP_Wing:SetMarker(false) CAP_Wing:SetAirbase(AIRBASE:FindByName(Airbasename)) From b0e0926b70122fdb4bad81edd68dad356b0dab5a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 10 Jan 2024 15:17:24 +0100 Subject: [PATCH 488/603] Added player name to `...PilotDown()` event --- Moose Development/Moose/Ops/CSAR.lua | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 370587751..5ecc2c4c8 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -293,7 +293,7 @@ CSAR.AircraftType["Bronco-OV-10A"] = 2 --- CSAR class version. -- @field #string version -CSAR.version="1.0.18" +CSAR.version="1.0.19" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -536,6 +536,7 @@ function CSAR:New(Coalition, Template, Alias) -- @param #number Frequency Beacon frequency in kHz. -- @param #string Leadername Name of the #UNIT of the downed pilot. -- @param #string CoordinatesText String of the position of the pilot. Format determined by self.coordtype. + -- @param #string Playername Player name if any given. Might be nil! --- On After "Aproach" event. Heli close to downed Pilot. -- @function [parent=#CSAR] OnAfterApproach @@ -842,7 +843,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla 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. + self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage, _playerName) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc. return self end @@ -1224,7 +1225,8 @@ end -- @param #string _GroupName Name of the Group -- @param #number _freq Beacon frequency. -- @param #boolean _nomessage Send message true or false. -function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage) +-- @param #string _playername Name of the downed pilot if any +function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage, _playername) self:T(self.lid .. " _InitSARForPilot") local _leader = _downedGroup:GetUnit(1) local _groupName = _GroupName @@ -1247,7 +1249,7 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage) end -- trigger FSM event - self:__PilotDown(2,_downedGroup, _freqk, _groupName, _coordinatesText) + self:__PilotDown(2,_downedGroup, _freqk, _groupName, _coordinatesText, _playername) return self end @@ -2543,8 +2545,9 @@ end -- @param #number Frequency Beacon frequency in kHz. -- @param #string Leadername Name of the #UNIT of the downed pilot. -- @param #string CoordinatesText String of the position of the pilot. Format determined by self.coordtype. -function CSAR:onbeforePilotDown(From, Event, To, Group, Frequency, Leadername, CoordinatesText) - self:T({From, Event, To, Group, Frequency, Leadername, CoordinatesText}) +-- @param #string Playername Player name if any given. Might be nil! +function CSAR:onbeforePilotDown(From, Event, To, Group, Frequency, Leadername, CoordinatesText, Playername) + self:T({From, Event, To, Group, Frequency, Leadername, CoordinatesText, tostring(Playername)}) return self end From 5680344dfd9d57f89ae765aea8baf56bfbc850c9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 11 Jan 2024 12:51:36 +0100 Subject: [PATCH 489/603] EASYGCICAP Additions --- Moose Development/Moose/Ops/EasyGCICAP.lua | 3 +- .../Moose/Wrapper/Controllable.lua | 56 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 401d5e056..115eb1c63 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -528,6 +528,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) flightgroup:SetDespawnAfterHolding() flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename)) flightgroup:GetGroup():CommandEPLRS(true,5) + flightgroup:GetGroup():SetOptionRadarUsingForContinousSearch() if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS and Mission.type ~= AUFTRAG.Type.RECON then flightgroup:SetDetection(true) flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet) @@ -548,7 +549,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) flightgroup:SetFuelLowRTB(true) Intel:AddAgent(flightgroup) function flightgroup:OnAfterHolding(From,Event,To) - self:ClearToLand(5) + self:Despawn(1,true) end end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 8deaf6cf9..384281c32 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -4018,6 +4018,62 @@ function CONTROLLABLE:OptionEngageRange( EngageRange ) return nil end +--- [AIR] Set how the AI uses the onboard radar. +-- @param #CONTROLLABLE self +-- @param #number Option Options are: `NEVER = 0, FOR_ATTACK_ONLY = 1,FOR_SEARCH_IF_REQUIRED = 2, FOR_CONTINUOUS_SEARCH = 3` +-- @return #CONTROLLABLE self +function CONTROLLABLE:SetOptionRadarUsing(Option) + self:F2( { self.ControllableName } ) + if self:IsAir() then + self:SetOption(AI.Option.Air.id.RADAR_USING,Option) + end + return self +end + +--- [AIR] Set how the AI uses the onboard radar. Here: never. +-- @param #CONTROLLABLE self +-- @return #CONTROLLABLE self +function CONTROLLABLE:SetOptionRadarUsingNever() + self:F2( { self.ControllableName } ) + if self:IsAir() then + self:SetOption(AI.Option.Air.id.RADAR_USING,0) + end + return self +end + +--- [AIR] Set how the AI uses the onboard radar, here: for attack only. +-- @param #CONTROLLABLE self +-- @return #CONTROLLABLE self +function CONTROLLABLE:SetOptionRadarUsingForAttackOnly() + self:F2( { self.ControllableName } ) + if self:IsAir() then + self:SetOption(AI.Option.Air.id.RADAR_USING,1) + end + return self +end + +--- [AIR] Set how the AI uses the onboard radar, here: when required for searching. +-- @param #CONTROLLABLE self +-- @return #CONTROLLABLE self +function CONTROLLABLE:SetOptionRadarUsingForSearchIfRequired() + self:F2( { self.ControllableName } ) + if self:IsAir() then + self:SetOption(AI.Option.Air.id.RADAR_USING,2) + end + return self +end + +--- [AIR] Set how the AI uses the onboard radar, here: always on. +-- @param #CONTROLLABLE self +-- @return #CONTROLLABLE self +function CONTROLLABLE:SetOptionRadarUsingForContinousSearch() + self:F2( { self.ControllableName } ) + if self:IsAir() then + self:SetOption(AI.Option.Air.id.RADAR_USING,3) + end + return self +end + --- (GROUND) Relocate controllable to a random point within a given radius; use e.g.for evasive actions; Note that not all ground controllables can actually drive, also the alarm state of the controllable might stop it from moving. -- @param #CONTROLLABLE self -- @param #number speed Speed of the controllable, default 20 From aedd2a6391ee5d90e1097238d3640a295e046d10 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 11 Jan 2024 16:10:40 +0100 Subject: [PATCH 490/603] #EASYGCICAP * Added assignment of in-flight groups to intercepts --- Moose Development/Moose/Ops/EasyGCICAP.lua | 322 +++++++++++++-------- 1 file changed, 209 insertions(+), 113 deletions(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 115eb1c63..79d699a49 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -47,7 +47,6 @@ -- @field #number capalt -- @field #number capdir -- @field #number capleg --- @field #number capgrouping -- @field #number maxinterceptsize -- @field #number missionrange -- @field #number noaltert5 @@ -65,6 +64,7 @@ -- @field #boolean Monitor -- @field #boolean TankerInvisible -- @field #number CapFormation +-- @field #table ReadyFlightGroups -- @extends Core.Fsm#FSM --- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown. @@ -190,7 +190,6 @@ EASYGCICAP = { capalt = 25000, capdir = 45, capleg = 15, - capgrouping = 2, maxinterceptsize = 2, missionrange = 100, noaltert5 = 4, @@ -209,6 +208,7 @@ EASYGCICAP = { Monitor = false, TankerInvisible = true, CapFormation = nil, + ReadyFlightGroups = {}, } --- Internal Squadron data type @@ -244,7 +244,7 @@ EASYGCICAP = { --- EASYGCICAP class version. -- @field #string version -EASYGCICAP.version="0.0.9" +EASYGCICAP.version="0.1.10" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -321,6 +321,7 @@ end -- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group -- @return #EASYGCICAP self function EASYGCICAP:SetCAPFormation(Formation) + self:T(self.lid.."SetCAPFormation") self.CapFormation = Formation return self end @@ -428,7 +429,7 @@ end --- Set default number of airframes standing by for intercept tasks (visible on the airfield) -- @param #EASYGCICAP self -- @param #number Airframes defaults to 2 --- @return #EASYGCICAP selfAirframes +-- @return #EASYGCICAP self function EASYGCICAP:SetDefaultNumberAlter5Standby(Airframes) self:T(self.lid.."SetDefaultNumberAlter5Standby") self.noaltert5 = math.abs(Airframes) or 2 @@ -438,13 +439,25 @@ end --- Set default engage range for intruders detected by CAP flights in NM. -- @param #EASYGCICAP self -- @param #number Range defaults to 50NM --- @return #EASYGCICAP selfAirframes +-- @return #EASYGCICAP self function EASYGCICAP:SetDefaultEngageRange(Range) self:T(self.lid.."SetDefaultNumberAlter5Standby") self.engagerange = Range or 50 return self end +--- Set default overhead for intercept calculations +-- @param #EASYGCICAP self +-- @param #number Overhead The overhead to use. +-- @return #EASYGCICAP self +-- @usage Either a CAP Plane or a newly spawned GCI plane will take care of intruders. Standard overhead is 0.75, i.e. a group of 3 intrudes will +-- be managed by 2 planes from the assigned AirWing. There is an maximum missions limitation per AirWing, so we do not spam the skies. +function EASYGCICAP:SetDefaultOverhead(Overhead) + self:T(self.lid.."SetDefaultOverhead") + self.overhead = Overhead or 0.75 + return self +end + --- Add an AirWing to the manager -- @param #EASYGCICAP self -- @param #string Airbasename @@ -1046,6 +1059,172 @@ function EASYGCICAP:AddRejectZone(Zone) return self end +--- (Internal) Try to assign the intercept to a FlightGroup already in air and ready. +-- @param #EASYGCICAP self +-- @param #table ReadyFlightGroups ReadyFlightGroups +-- @param Ops.Auftrag#AUFTRAG InterceptAuftrag The Auftrag +-- @param Wrapper.Group#GROUP Group The Target +-- @param #number WingSize Calculated number of Flights +-- @return #boolean assigned +-- @return #number leftover +function EASYGCICAP:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,Group,WingSize) + self:I("_TryAssignIntercept for size "..WingSize or 1) + local assigned = false + local wingsize = WingSize or 1 + local mindist = 0 + local disttable = {} + if Group and Group:IsAlive() then + local gcoord = Group:GetCoordinate() or COORDINATE:New(0,0,0) + self:I(self.lid..string.format("Assignment for %s",Group:GetName())) + for _name,_FG in pairs(ReadyFlightGroups or {}) do + local FG = _FG -- Ops.FlightGroup#FLIGHTGROUP + local fcoord = FG:GetCoordinate() + local dist = math.floor(UTILS.Round(fcoord:Get2DDistance(gcoord)/1000,1)) + self:I(self.lid..string.format("FG %s Distance %dkm",_name,dist)) + disttable[#disttable+1] = { FG=FG, dist=dist} + if dist>mindist then mindist=dist end + end + + local function sortDistance(a, b) + return a.dist < b.dist + end + + table.sort(disttable, sortDistance) + + for _,_entry in ipairs(disttable) do + local FG = _entry.FG -- Ops.FlightGroup#FLIGHTGROUP + FG:AddMission(InterceptAuftrag) + local cm = FG:GetMissionCurrent() + if cm then cm:Cancel() end + wingsize = wingsize - 1 + self:I(self.lid..string.format("Assigned to FG %s Distance %dkm",FG:GetName(),_entry.dist)) + if wingsize == 0 then + assigned = true + break + end + end + end + + return assigned, wingsize +end + +--- Add a zone to the rejected zones set. +-- @param #EASYGCICAP self +-- @param Ops.Intelligence#INTEL.Cluster Cluster +-- @return #EASYGCICAP self +function EASYGCICAP:_AssignIntercept(Cluster) + -- Here, we'll decide if we need to launch an intercepting flight, and from where + local overhead = self.overhead + local capspeed = self.capspeed + 100 + local capalt = self.capalt + local maxsize = self.maxinterceptsize + local repeatsonfailure = self.repeatsonfailure + + local wings = self.wings + local ctlpts = self.ManagedCP + local MaxAliveMissions = self.MaxAliveMissions * self.capgrouping + local nogozoneset = self.NoGoZoneSet + local ReadyFlightGroups = self.ReadyFlightGroups + + -- Aircraft? + if Cluster.ctype ~= INTEL.Ctype.AIRCRAFT then return end + -- Threatlevel 0..10 + local contact = self.Intel:GetHighestThreatContact(Cluster) + local name = contact.groupname --#string + local threat = contact.threatlevel --#number + local position = self.Intel:CalcClusterFuturePosition(Cluster,300) + -- calculate closest zone + local bestdistance = 2000*1000 -- 2000km + local targetairwing = nil -- Ops.AirWing#AIRWING + local targetawname = "" -- #string + local clustersize = self.Intel:ClusterCountUnits(Cluster) or 1 + local wingsize = math.abs(overhead * (clustersize+1)) + if wingsize > maxsize then wingsize = maxsize end + -- existing mission, and if so - done? + local retrymission = true + if Cluster.mission and (not Cluster.mission:IsOver()) then + retrymission = false + end + if (retrymission) and (wingsize >= 1) then + MESSAGE:New(string.format("**** %s Interceptors need wingsize %d", UTILS.GetCoalitionName(self.coalition), wingsize),15,"CAPGCI"):ToAllIf(self.debug):ToLog() + for _,_data in pairs (wings) do + local airwing = _data[1] -- Ops.AirWing#AIRWING + local zone = _data[2] -- Core.Zone#ZONE + local zonecoord = zone:GetCoordinate() + local name = _data[3] -- #string + local distance = position:DistanceFromPointVec2(zonecoord) + local airframes = airwing:CountAssets(true) + if distance < bestdistance and airframes >= wingsize then + bestdistance = distance + targetairwing = airwing + targetawname = name + end + end + for _,_data in pairs (ctlpts) do + --local airwing = _data[1] -- Ops.AirWing#AIRWING + --local zone = _data[2] -- Core.Zone#ZONE + --local zonecoord = zone:GetCoordinate() + --local name = _data[3] -- #string + + local data = _data -- #EASYGCICAP.CapPoint + local name = data.AirbaseName + local zonecoord = data.Coordinate + local airwing = wings[name][1] + + local distance = position:DistanceFromPointVec2(zonecoord) + local airframes = airwing:CountAssets(true) + if distance < bestdistance and airframes >= wingsize then + bestdistance = distance + targetairwing = airwing -- Ops.AirWing#AIRWING + targetawname = name + end + end + local text = string.format("Closest Airwing is %s", targetawname) + local m = MESSAGE:New(text,10,"CAPGCI"):ToAllIf(self.debug):ToLog() + -- Do we have a matching airwing? + if targetairwing then + local AssetCount = targetairwing:CountAssetsOnMission(MissionTypes,Cohort) + -- Enough airframes on mission already? + self:T(self.lid.." Assets on Mission "..AssetCount) + if AssetCount <= MaxAliveMissions then + local repeats = repeatsonfailure + local InterceptAuftrag = AUFTRAG:NewINTERCEPT(contact.group) + :SetMissionRange(150) + :SetPriority(1,true,1) + --:SetRequiredAssets(wingsize) + :SetRepeatOnFailure(repeats) + :SetMissionSpeed(UTILS.KnotsToAltKIAS(capspeed,capalt)) + :SetMissionAltitude(capalt) + + if nogozoneset:Count() > 0 then + InterceptAuftrag:AddConditionSuccess( + function(group,zoneset) + local success = false + if group and group:IsAlive() then + local coord = group:GetCoordinate() + if coord and zoneset:IsCoordinateInZone(coord) then + success = true + end + end + return success + end, + contact.group, + nogozoneset + ) + end + local assigned, rest = self:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,contact.group,wingsize) + if not assigned then + InterceptAuftrag:SetRequiredAssets(rest) + targetairwing:AddMission(InterceptAuftrag) + end + Cluster.mission = InterceptAuftrag + end + else + MESSAGE:New("**** Not enough airframes available or max mission limit reached!",15,"CAPGCI"):ToAllIf(self.debug):ToLog() + end + end +end + --- (Internal) Start detection. -- @param #EASYGCICAP self -- @return #EASYGCICAP self @@ -1069,117 +1248,16 @@ function EASYGCICAP:_StartIntel() BlueIntel.debug = true end - -- Here, we'll decide if we need to launch an intercepting flight, and from where - - local overhead = self.overhead - local capspeed = self.capspeed + 100 - local capalt = self.capalt - local maxsize = self.maxinterceptsize - local repeatsonfailure = self.repeatsonfailure - - local wings = self.wings - local ctlpts = self.ManagedCP - local MaxAliveMissions = self.MaxAliveMissions * self.capgrouping - local nogozoneset = self.NoGoZoneSet + local function AssignCluster(Cluster) + self:_AssignIntercept(Cluster) + end function BlueIntel:OnAfterNewCluster(From,Event,To,Cluster) - -- Aircraft? - if Cluster.ctype ~= INTEL.Ctype.AIRCRAFT then return end - -- Threatlevel 0..10 - local contact = self:GetHighestThreatContact(Cluster) - local name = contact.groupname --#string - local threat = contact.threatlevel --#number - local position = self:CalcClusterFuturePosition(Cluster,300) - -- calculate closest zone - local bestdistance = 2000*1000 -- 2000km - local targetairwing = nil -- Ops.AirWing#AIRWING - local targetawname = "" -- #string - local clustersize = self:ClusterCountUnits(Cluster) or 1 - local wingsize = math.abs(overhead * (clustersize+1)) - if wingsize > maxsize then wingsize = maxsize end - -- existing mission, and if so - done? - local retrymission = true - if Cluster.mission and (not Cluster.mission:IsOver()) then - retrymission = false - end - if (retrymission) and (wingsize >= 1) then - MESSAGE:New(string.format("**** %s Interceptors need wingsize %d", UTILS.GetCoalitionName(self.coalition), wingsize),15,"CAPGCI"):ToAllIf(self.debug):ToLog() - for _,_data in pairs (wings) do - local airwing = _data[1] -- Ops.AirWing#AIRWING - local zone = _data[2] -- Core.Zone#ZONE - local zonecoord = zone:GetCoordinate() - local name = _data[3] -- #string - local distance = position:DistanceFromPointVec2(zonecoord) - local airframes = airwing:CountAssets(true) - if distance < bestdistance and airframes >= wingsize then - bestdistance = distance - targetairwing = airwing - targetawname = name - end - end - for _,_data in pairs (ctlpts) do - --local airwing = _data[1] -- Ops.AirWing#AIRWING - --local zone = _data[2] -- Core.Zone#ZONE - --local zonecoord = zone:GetCoordinate() - --local name = _data[3] -- #string - - local data = _data -- #EASYGCICAP.CapPoint - local name = data.AirbaseName - local zonecoord = data.Coordinate - local airwing = wings[name][1] - - local distance = position:DistanceFromPointVec2(zonecoord) - local airframes = airwing:CountAssets(true) - if distance < bestdistance and airframes >= wingsize then - bestdistance = distance - targetairwing = airwing -- Ops.AirWing#AIRWING - targetawname = name - end - end - local text = string.format("Closest Airwing is %s", targetawname) - local m = MESSAGE:New(text,10,"CAPGCI"):ToAllIf(self.debug):ToLog() - -- Do we have a matching airwing? - if targetairwing then - local AssetCount = targetairwing:CountAssetsOnMission(MissionTypes,Cohort) - -- Enough airframes on mission already? - self:T(self.lid.." Assets on Mission "..AssetCount) - if AssetCount <= MaxAliveMissions then - local repeats = repeatsonfailure - local InterceptAuftrag = AUFTRAG:NewINTERCEPT(contact.group) - :SetMissionRange(150) - :SetPriority(1,true,1) - :SetRequiredAssets(wingsize) - :SetRepeatOnFailure(repeats) - :SetMissionSpeed(UTILS.KnotsToAltKIAS(capspeed,capalt)) - :SetMissionAltitude(capalt) - - if nogozoneset:Count() > 0 then - InterceptAuftrag:AddConditionSuccess( - function(group,zoneset) - local success = false - if group and group:IsAlive() then - local coord = group:GetCoordinate() - if coord and zoneset:IsCoordinateInZone(coord) then - success = true - end - end - return success - end, - contact.group, - nogozoneset - ) - end - - targetairwing:AddMission(InterceptAuftrag) - Cluster.mission = InterceptAuftrag - end - else - MESSAGE:New("**** Not enough airframes available or max mission limit reached!",15,"CAPGCI"):ToAllIf(self.debug):ToLog() - end - end + AssignCluster(Cluster) end -self.Intel = BlueIntel -return self + + self.Intel = BlueIntel + return self end ------------------------------------------------------------------------- @@ -1253,6 +1331,24 @@ function EASYGCICAP:onafterStatus(From,Event,To) tankermission = tankermission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.TANKER}) assets = assets + count instock = instock + count2 + local assetsonmission = _wing[1]:GetAssetsOnMission({AUFTRAG.Type.GCICAP,AUFTRAG.Type.PATROLRACETRACK}) + -- update ready groups + self.ReadyFlightGroups = nil + self.ReadyFlightGroups = {} + for _,_asset in pairs(assetsonmission or {}) do + local asset = _asset -- Functional.Warehouse#WAREHOUSE.Assetitem + local FG = asset.flightgroup -- Ops.FlightGroup#FLIGHTGROUP + if FG then + local name = FG:GetName() + local engage = FG:IsEngaging() + local hasmissiles = FG:IsOutOfMissiles() == nil and true or false + local ready = hasmissiles and FG:IsFuelGood() and FG:IsAirborne() + --self:I(string.format("Flightgroup %s Engaging = %s Ready = %s",tostring(name),tostring(engage),tostring(ready))) + if ready then + self.ReadyFlightGroups[name] = FG + end + end + end end if self.Monitor then local threatcount = #self.Intel.Clusters or 0 From 36a6d5fedebc4cac3d2f2a1022574d50e4746aed Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 12 Jan 2024 18:42:26 +0100 Subject: [PATCH 491/603] xxx --- .../Moose/Functional/Stratego.lua | 48 ++++++++++++++---- Moose Development/Moose/Ops/Auftrag.lua | 50 +++++++++++++++++++ 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 43752ea7e..850f80b0f 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -222,6 +222,7 @@ STRATEGO = { -- @field #number points -- @field #number coalition -- @field #string coalitionname +-- @field Core.Point#COORDINATRE coordinate --- -- @type STRATEGO.Type @@ -703,14 +704,21 @@ end --- [USER] Get a list of the nodes with the highest weight. -- @param #STRATEGO self +-- @param #number Coalition (Optional) Find for this coalition only. E.g. coalition.side.BLUE. -- @return #table Table of nodes. -- @return #number Weight The consolidated weight associated with the nodes. -function STRATEGO:GetHighestWeightNodes() +function STRATEGO:GetHighestWeightNodes(Coalition) self:T(self.lid.."GetHighestWeightNodes") local weight = 0 local airbases = {} for _name,_data in pairs(self.airbasetable) do - if _data.weight >= weight then + local okay = true + if Coalition then + if _data.coalition ~= Coalition then + okay = false + end + end + if _data.weight >= weight and okay then weight = _data.weight if not airbases[weight] then airbases[weight]={} end table.insert(airbases[weight],_name) @@ -719,16 +727,24 @@ function STRATEGO:GetHighestWeightNodes() return airbases[weight],weight end ---- [USER] Get a list of the nodes a weight less than the give parameter. +--- [USER] Get a list of the nodes a weight less than the given parameter. -- @param #STRATEGO self +-- @param #number Weight Weight - nodes need to have less than this weight. +-- @param #number Coalition (Optional) Find for this coalition only. E.g. coalition.side.BLUE. -- @return #table Table of nodes. -- @return #number Weight The consolidated weight associated with the nodes. -function STRATEGO:GetNextHighestWeightNodes(Weight) +function STRATEGO:GetNextHighestWeightNodes(Weight, Coalition) self:T(self.lid.."GetNextHighestWeightNodes") local weight = 0 local airbases = {} for _name,_data in pairs(self.airbasetable) do - if _data.weight >= weight and _data.weight < Weight then + local okay = true + if Coalition then + if _data.coalition ~= Coalition then + okay = false + end + end + if _data.weight >= weight and _data.weight < Weight and okay then weight = _data.weight if not airbases[weight] then airbases[weight]={} end table.insert(airbases[weight],_name) @@ -985,7 +1001,9 @@ function STRATEGO:FindStrategicTargets() name = name, dist = dist, points = fpoints, - coalition = coa, + coalition = coa, + coalitionname = UTILS.GetCoalitionName(coa), + coordinate = self.airbasetable[name].coord, } end local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE @@ -1000,7 +1018,9 @@ function STRATEGO:FindStrategicTargets() name = name, dist = dist, points = fpoints, - coalition = coa, + coalition = coa, + coalitionname = UTILS.GetCoalitionName(coa), + coordinate = self.airbasetable[name].coord, } end end @@ -1031,6 +1051,7 @@ function STRATEGO:FindConsolidationTargets() points = fpoints, coalition = coa, coalitionname = UTILS.GetCoalitionName(coa), + coordinate = self.airbasetable[name].coord, } end local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE @@ -1046,7 +1067,8 @@ function STRATEGO:FindConsolidationTargets() dist = dist, points = fpoints, coalition = coa, - coalitionname = UTILS.GetCoalitionName(coa), + coalitionname = UTILS.GetCoalitionName(coa), + coordinate = self.airbasetable[name].coord, } end end @@ -1060,10 +1082,14 @@ end -- @param #boolean Enemies (optional) If true, find only enemy neighbors. -- @param #boolean Friends (optional) If true, find only friendly or neutral neighbors. -- @return #table Neighbors Table of #STRATEGO.DistData entries indexed by neighbor node names. +-- @return #string Nearest Name of the nearest node. +-- @return #number Distance Distance of the nearest node. function STRATEGO:FindNeighborNodes(Name,Enemies,Friends) self:T(self.lid.."FindNeighborNodes") local neighbors = {} local name = string.gsub(Name,"[%p%s]",".") + local shortestdist = 1000*1000 + local nearest = nil for _route,_data in pairs(self.disttable) do if string.find(_route,name,1,true) then local dist = self.disttable[_route] -- #STRATEGO.DistData @@ -1082,9 +1108,13 @@ function STRATEGO:FindNeighborNodes(Name,Enemies,Friends) else neighbors[cname] = dist end + if neighbors[cname] and dist.dist < shortestdist then + shortestdist = dist.dist + nearest = cname + end end end - return neighbors + return neighbors, nearest, shortestdist end --- [USER] Find a route between two nodes. diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 92511531c..24d07803e 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -434,6 +434,7 @@ _AUFTRAGSNR=0 -- @field #string ARMORATTACK Armor attack. -- @field #string CASENHANCED Enhanced CAS. -- @field #string HOVER Hover. +-- @field #string LANDATCOORDINATE Land at coordinate. -- @field #string GROUNDATTACK Ground attack. -- @field #string CARGOTRANSPORT Cargo transport. -- @field #string RELOCATECOHORT Relocate a cohort from one legion to another. @@ -480,6 +481,7 @@ AUFTRAG.Type={ ARMORATTACK="Armor Attack", CASENHANCED="CAS Enhanced", HOVER="Hover", + LANDATCOORDINATE="Land at Coordinate", GROUNDATTACK="Ground Attack", CARGOTRANSPORT="Cargo Transport", RELOCATECOHORT="Relocate Cohort", @@ -1052,6 +1054,42 @@ function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt) return mission end +--- **[AIR ROTARY]** Create an LANDATCOORDINATE mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Where to land. +-- @param #number OuterRadius (Optional) Vary the coordinate by this many feet, e.g. get a new random coordinate between OuterRadius and (optionally) avoiding InnerRadius of the coordinate. +-- @param #number InnerRadius (Optional) Vary the coordinate by this many feet, e.g. get a new random coordinate between OuterRadius and (optionally) avoiding InnerRadius of the coordinate. +-- @param #number Time Time in seconds to stay. Default 300 seconds. +-- @param #number Speed Speed in knots to fly to the target coordinate. Default 150kn. +-- @param #number MissionAlt Altitude to fly towards the mission in feet AGL. Default 1000ft. +-- @return #AUFTRAG self +function AUFTRAG:NewLANDATCOORDINATE(Coordinate, OuterRadius, InnerRadius, Time, Speed, MissionAlt) + + local mission=AUFTRAG:New(AUFTRAG.Type.LANDATCOORDINATE) + + mission:_TargetFromObject(Coordinate) + + mission.stayTime = Time or 300 + mission.stayAt = Coordinate + self:SetMissionSpeed(Speed or 150) + self:SetMissionAltitude(MissionAlt or 1000) + + if OuterRadius then + mission.stayAt = Coordinate:GetRandomCoordinateInRadius(UTILS.FeetToMeters(OuterRadius),UTILS.FeetToMeters(InnerRadius or 0)) + end + + -- Mission options: + mission.missionFraction=0.9 + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.HELICOPTER} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + --- **[AIR]** Create an enhanced orbit race track mission. Planes will keep closer to the track. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Where to start the race track. @@ -6443,7 +6481,19 @@ function AUFTRAG:GetDCSMissionTask() param.missionAltitude = self.missionAltitude DCStask.params=param + + table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.LANDATCOORDINATE then + + --------------------- + -- LANDATCOORDINATE Mission + --------------------- + + local DCStask={} + local Vec2 = self.stayAt:GetVec2() + local DCStask = CONTROLLABLE.TaskLandAtVec2(nil,Vec2,self.stayTime) + table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.ONGUARD or self.type==AUFTRAG.Type.ARMOREDGUARD then From 601c8165d883cc7f3bcd612346b90a5746eb88e3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 14 Jan 2024 14:57:07 +0100 Subject: [PATCH 492/603] Minor changes --- Moose Development/Moose/Ops/Awacs.lua | 3 +++ Moose Development/Moose/Ops/OpsGroup.lua | 2 +- Moose Development/Moose/Ops/PlayerTask.lua | 9 ++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 50fb3ab5b..d84541e17 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -3001,6 +3001,9 @@ function AWACS:_Picture(Group,IsGeneral) if clustersAO == 0 and clustersEWR == 0 then -- clean + local picclean = self.gettext:GetEntry("PICCLEAN",self.locale) + text = string.format(picclean,gcallsign,self.callsigntxt) + textScreen = text self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) else diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index fb9162521..aa50ece1d 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -7176,7 +7176,7 @@ function OPSGROUP:SetLaserTarget(Target) -- Scenery as target. Treat it like a coordinate. Set offset to 1 meter above ground. self.spot.TargetType=0 - self.spot.offsetTarget={x=0, y=1, z=0} + self.spot.offsetTarget={x=0, y=3, z=0} elseif Target:IsInstanceOf("POSITIONABLE") then diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 0b95512e4..e251b1856 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1965,6 +1965,7 @@ end -- @param Ops.FlightGroup#FLIGHTGROUP FlightGroup The FlightGroup (e.g. drone) to be used for lasing (one unit in one group only). -- Can optionally be handed as Ops.ArmyGroup#ARMYGROUP - **Note** might not find an LOS spot or get lost on the way. Cannot island-hop. -- @param #number LaserCode The lasercode to be used. Defaults to 1688. +-- @param Core.Point#COORDINATE HoldingPoint (Optional) Point where the drone should initially circle. If not set, defaults to BullsEye of the coalition. -- @return #PLAYERTASKCONTROLLER self -- @usage -- -- Set up precision bombing, FlightGroup as lasing unit @@ -1979,7 +1980,7 @@ end -- ArmyGroup:Activate() -- taskmanager:EnablePrecisionBombing(ArmyGroup,1688) -- -function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode) +function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode,HoldingPoint) self:T(self.lid.."EnablePrecisionBombing") if FlightGroup then if FlightGroup.ClassName and (FlightGroup.ClassName == "FLIGHTGROUP" or FlightGroup.ClassName == "ARMYGROUP")then @@ -1995,6 +1996,7 @@ function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode) -- let it orbit the BullsEye if FG if self.LasingDrone:IsFlightgroup() then local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( self.Coalition )) + if HoldingPoint then BullsCoordinate = HoldingPoint end local Orbit = AUFTRAG:NewORBIT_CIRCLE(BullsCoordinate,10000,120) self.LasingDrone:AddMission(Orbit) end @@ -2553,10 +2555,11 @@ function PLAYERTASKCONTROLLER:_CheckPrecisionTasks() self.LasingDrone.playertask.reachmessage = false -- move the drone to target if self.LasingDrone:IsFlightgroup() then + self.LasingDrone:CancelAllMissions() local auftrag = AUFTRAG:NewORBIT_CIRCLE(task.Target:GetCoordinate(),10000,120) - local currmission = self.LasingDrone:GetMissionCurrent() + --local currmission = self.LasingDrone:GetMissionCurrent() self.LasingDrone:AddMission(auftrag) - currmission:__Cancel(-2) + --currmission:__Cancel(-2) elseif self.LasingDrone:IsArmygroup() then local tgtcoord = task.Target:GetCoordinate() local tgtzone = ZONE_RADIUS:New("ArmyGroup-"..math.random(1,10000),tgtcoord:GetVec2(),3000) From 5c90afea76f104105f9f749145f4960460589a11 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 16 Jan 2024 15:06:54 +0100 Subject: [PATCH 493/603] xxx --- .../Moose/Functional/Autolase.lua | 32 ++++++++++++++++--- Moose Development/Moose/Ops/PlayerTask.lua | 5 +-- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index e4672fccc..ab9579430 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -74,7 +74,7 @@ -- @image Designation.JPG -- -- Date: 24 Oct 2021 --- Last Update: Oct 2023 +-- Last Update: Jan 2024 -- --- Class AUTOLASE -- @type AUTOLASE @@ -87,6 +87,7 @@ -- @field Core.Set#SET_GROUP RecceSet -- @field #table LaserCodes -- @field #table playermenus +-- @field #boolean smokemenu -- @extends Ops.Intel#INTEL --- @@ -97,6 +98,7 @@ AUTOLASE = { verbose = 0, alias = "", debug = false, + smokemenu = true, } --- Laser spot info @@ -115,7 +117,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.1.22" +AUTOLASE.version = "0.1.23" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -202,6 +204,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self.blacklistattributes = {} self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes self.playermenus = {} + self.smokemenu = true -- Set some string id for output to DCS.log file. self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -329,9 +332,11 @@ function AUTOLASE:SetPilotMenu() local lasetopm = MENU_GROUP:New(Group,"Autolase",nil) self.playermenus[unitname] = lasetopm local lasemenu = MENU_GROUP_COMMAND:New(Group,"Status",lasetopm,self.ShowStatus,self,Group,Unit) - local smoke = (self.smoketargets == true) and "off" or "on" - local smoketext = string.format("Switch smoke targets to %s",smoke) - local smokemenu = MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets)) + if self.smokemenu then + local smoke = (self.smoketargets == true) and "off" or "on" + local smoketext = string.format("Switch smoke targets to %s",smoke) + local smokemenu = MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets)) + end for _,_grp in pairs(self.RecceSet.Set) do local grp = _grp -- Wrapper.Group#GROUP local unit = grp:GetUnit(1) @@ -581,6 +586,23 @@ function AUTOLASE:SetSmokeTargets(OnOff,Color) return self end +--- (User) Show the "Switch smoke target..." menu entry for pilots. On by default. +-- @param #AUTOLASE self +-- @return #AUTOLASE self +function AUTOLASE:EnableSmokeMenu() + self.smokemenu = true + return self +end + +--- (User) Do not show the "Switch smoke target..." menu entry for pilots. +-- @param #AUTOLASE self +-- @return #AUTOLASE self +function AUTOLASE:DisableSmokeMenu() + self.smokemenu = false + return self +end + + --- (Internal) Function to calculate line of sight. -- @param #AUTOLASE self -- @param Wrapper.Unit#UNIT Unit diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index e251b1856..8ae53f1b1 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.22" +PLAYERTASK.version="0.1.23" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -2609,6 +2609,7 @@ function PLAYERTASKCONTROLLER:_CheckPrecisionTasks() -- not done yet local dcoord = self.LasingDrone:GetCoordinate() local tcoord = task.Target:GetCoordinate() + tcoord.y = tcoord.y + 2 local dist = dcoord:Get2DDistance(tcoord) -- close enough? if dist < 3000 and not self.LasingDrone:IsLasing() then @@ -3192,7 +3193,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) local islasing = self.LasingDrone:IsLasing() == true and yes or no local prectext = self.gettext:GetEntry("POINTERTARGETREPORT",self.locale) prectext = string.format(prectext,inreach,islasing) - text = text .. prectext + text = text .. prectext.."("..self.LaserCode..")" end end -- Buddylasing From 68756681fa2cccd8d4eb1f7f4ff14b3ae26bfe2f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 17 Jan 2024 08:10:17 +0100 Subject: [PATCH 494/603] #PLAYERTASKCONTROLLER * Additions for lasing unit being an ARMYGROUP --- Moose Development/Moose/Ops/PlayerTask.lua | 44 ++++++++++++++-------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 8ae53f1b1..406563273 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.23" +PLAYERTASK.version="0.1.24" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -1966,6 +1966,8 @@ end -- Can optionally be handed as Ops.ArmyGroup#ARMYGROUP - **Note** might not find an LOS spot or get lost on the way. Cannot island-hop. -- @param #number LaserCode The lasercode to be used. Defaults to 1688. -- @param Core.Point#COORDINATE HoldingPoint (Optional) Point where the drone should initially circle. If not set, defaults to BullsEye of the coalition. +-- @param #number Alt (Optional) Altitude in feet. Only applies if using a FLIGHTGROUP object! Defaults to 10000. +-- @param #number Speed (Optional) Speed in knots. Only applies if using a FLIGHTGROUP object! Defaults to 120. -- @return #PLAYERTASKCONTROLLER self -- @usage -- -- Set up precision bombing, FlightGroup as lasing unit @@ -1980,7 +1982,7 @@ end -- ArmyGroup:Activate() -- taskmanager:EnablePrecisionBombing(ArmyGroup,1688) -- -function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode,HoldingPoint) +function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode,HoldingPoint, Alt, Speed) self:T(self.lid.."EnablePrecisionBombing") if FlightGroup then if FlightGroup.ClassName and (FlightGroup.ClassName == "FLIGHTGROUP" or FlightGroup.ClassName == "ARMYGROUP")then @@ -1993,11 +1995,20 @@ function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode,Holdi self.LasingDrone:SetLaser(LaserCode) self.LaserCode = LaserCode or 1688 self.LasingDroneTemplate = self.LasingDrone:_GetTemplate(true) + self.LasingDroneAlt = Alt or 10000 + self.LasingDroneSpeed = Speed or 120 -- let it orbit the BullsEye if FG if self.LasingDrone:IsFlightgroup() then + self.LasingDroneIsFlightgroup = true local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( self.Coalition )) if HoldingPoint then BullsCoordinate = HoldingPoint end - local Orbit = AUFTRAG:NewORBIT_CIRCLE(BullsCoordinate,10000,120) + local Orbit = AUFTRAG:NewORBIT_CIRCLE(BullsCoordinate,self.LasingDroneAlt,self.LasingDroneSpeed) + self.LasingDrone:AddMission(Orbit) + elseif self.LasingDrone:IsArmygroup() then + self.LasingDroneIsArmygroup = true + local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( self.Coalition )) + if HoldingPoint then BullsCoordinate = HoldingPoint end + local Orbit = AUFTRAG:NewONGUARD(BullsCoordinate) self.LasingDrone:AddMission(Orbit) end else @@ -2536,10 +2547,16 @@ function PLAYERTASKCONTROLLER:_CheckPrecisionTasks() if self.LasingDrone then self.LasingDrone:_Respawn(1,nil,true) else - -- TODO: Handle ArmyGroup - local FG = FLIGHTGROUP:New(self.LasingDroneTemplate) - FG:Activate() - self:EnablePrecisionBombing(FG,self.LaserCode or 1688) + -- DONE: Handle ArmyGroup + if self.LasingDroneIsFlightgroup then + local FG = FLIGHTGROUP:New(self.LasingDroneTemplate) + FG:Activate() + self:EnablePrecisionBombing(FG,self.LaserCode or 1688) + else + local FG = ARMYGROUP:New(self.LasingDroneTemplate) + FG:Activate() + self:EnablePrecisionBombing(FG,self.LaserCode or 1688) + end end return self end @@ -2554,13 +2571,11 @@ function PLAYERTASKCONTROLLER:_CheckPrecisionTasks() self.LasingDrone.playertask.inreach = false self.LasingDrone.playertask.reachmessage = false -- move the drone to target - if self.LasingDrone:IsFlightgroup() then + if self.LasingDroneIsFlightgroup then self.LasingDrone:CancelAllMissions() - local auftrag = AUFTRAG:NewORBIT_CIRCLE(task.Target:GetCoordinate(),10000,120) - --local currmission = self.LasingDrone:GetMissionCurrent() - self.LasingDrone:AddMission(auftrag) - --currmission:__Cancel(-2) - elseif self.LasingDrone:IsArmygroup() then + local auftrag = AUFTRAG:NewORBIT_CIRCLE(task.Target:GetCoordinate(),self.LasingDroneAlt,self.LasingDroneSpeed) + self.LasingDrone:AddMission(auftrag) + elseif self.LasingDroneIsArmygroup then local tgtcoord = task.Target:GetCoordinate() local tgtzone = ZONE_RADIUS:New("ArmyGroup-"..math.random(1,10000),tgtcoord:GetVec2(),3000) local finalpos=nil -- Core.Point#COORDINATE @@ -2573,11 +2588,10 @@ function PLAYERTASKCONTROLLER:_CheckPrecisionTasks() end end if finalpos then + self.LasingDrone:CancelAllMissions() -- yeah we got one local auftrag = AUFTRAG:NewARMOREDGUARD(finalpos,"Off road") - local currmission = self.LasingDrone:GetMissionCurrent() self.LasingDrone:AddMission(auftrag) - if currmission then currmission:__Cancel(-2) end else -- could not find LOS position! self:E("***Could not find LOS position to post ArmyGroup for lasing!") From 90d825c809bc301364c4c1a5dd3651ff1cc1e6cb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 17 Jan 2024 12:13:41 +0100 Subject: [PATCH 495/603] xxx --- Moose Development/Moose/Ops/AirWing.lua | 122 ++++-------------- Moose Development/Moose/Ops/EasyGCICAP.lua | 16 +++ .../Moose/Wrapper/Controllable.lua | 32 ++++- 3 files changed, 70 insertions(+), 100 deletions(-) diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 96edfcac3..1d2188ba4 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -36,14 +36,12 @@ -- @field Core.Set#SET_ZONE zonesetAWACS Set of AWACS zones. -- @field Core.Set#SET_ZONE zonesetRECON Set of RECON zones. -- @field #number nflightsCAP Number of CAP flights constantly in the air. --- @field #number nflightsCAS Number of CAP flights constantly in the air. -- @field #number nflightsAWACS Number of AWACS flights constantly in the air. -- @field #number nflightsTANKERboom Number of TANKER flights with BOOM constantly in the air. -- @field #number nflightsTANKERprobe Number of TANKER flights with PROBE constantly in the air. -- @field #number nflightsRescueHelo Number of Rescue helo flights constantly in the air. -- @field #number nflightsRecon Number of Recon flights constantly in the air. -- @field #table pointsCAP Table of CAP points. --- @field #table pointsCAS Table of CAS points. -- @field #table pointsTANKER Table of Tanker points. -- @field #table pointsAWACS Table of AWACS points. -- @field #table pointsRecon Table of RECON points. @@ -58,6 +56,8 @@ -- @field #boolean despawnAfterHolding Aircraft are despawned after holding. -- @field #boolean capOptionPatrolRaceTrack Use closer patrol race track or standard orbit auftrag. -- @field #number capFormation If capOptionPatrolRaceTrack is true, set the formation, also. +-- @field #number capOptionVaryStartTime If set, vary mission start time for CAP missions generated random between capOptionVaryStartTime and capOptionVaryEndTime +-- @field #number capOptionVaryEndTime If set, vary mission start time for CAP missions generated random between capOptionVaryStartTime and capOptionVaryEndTime -- -- @extends Ops.Legion#LEGION @@ -128,13 +128,14 @@ AIRWING = { payloads = {}, payloadcounter = 0, pointsCAP = {}, - pointsCAS = {}, pointsTANKER = {}, pointsAWACS = {}, pointsRecon = {}, markpoints = false, capOptionPatrolRaceTrack = false, capFormation = nil, + capOptionVaryStartTime = nil, + capOptionVaryEndTime = nil, } --- Payload data. @@ -186,7 +187,7 @@ AIRWING = { --- AIRWING class version. -- @field #string version -AIRWING.version="0.9.4" +AIRWING.version="0.9.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -229,7 +230,6 @@ function AIRWING:New(warehousename, airwingname) -- Defaults: self.nflightsCAP=0 - self.nflightsCAS=0 self.nflightsAWACS=0 self.nflightsRecon=0 self.nflightsTANKERboom=0 @@ -707,24 +707,6 @@ function AIRWING:SetNumberCAP(n) return self end ---- Set number of CAS flights constantly carried out. --- @param #AIRWING self --- @param #number n Number of flights. Default 1. --- @return #AIRWING self -function AIRWING:SetNumberCAS(n) - self.nflightsCAS=n or 1 - return self -end - ---- Set CAS flight formation. --- @param #AIRWING self --- @param #number Formation Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). --- @return #AIRWING self -function AIRWING:SetCASFormation(Formation) - self.casFormation = Formation - return self -end - --- Set CAP flight formation. -- @param #AIRWING self -- @param #number Formation Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). @@ -743,6 +725,17 @@ function AIRWING:SetCapCloseRaceTrack(OnOff) return self end +--- Set CAP mission start to vary randomly between Start end End seconds. +-- @param #AIRWING self +-- @param #number Start +-- @param #number End +-- @return #AIRWING self +function AIRWING:SetCapStartTimeVariation(Start, End) + self.capOptionVaryStartTime = Start or 5 + self.capOptionVaryEndTime = End or 60 + return self +end + --- Set number of TANKER flights with Boom constantly in the air. -- @param #AIRWING self -- @param #number Nboom Number of flights. Default 1. @@ -877,37 +870,6 @@ function AIRWING:AddPatrolPointCAP(Coordinate, Altitude, Speed, Heading, LegLeng return self end ---- Add a patrol Point for CAS missions. --- @param #AIRWING self --- @param Core.Zone#ZONE_BASE Zone Zone to patrol. --- @param #number Altitude Orbit altitude in feet. --- @param #number Speed Orbit speed in knots. --- @param #number RangeMax Max Range in NM. --- @param Core.Set#SET_ZONE NoEngageZoneSet (Optional) Non engagement zone set --- @param #table TargetTypes (Optional) Types of target attributes that will be engaged. See DCS enum attributes. Default {"Helicopters", "Ground Units", "Light armed ships"}. --- @return #AIRWING self -function AIRWING:AddPatrolPointCAS(Zone, Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes) - - local patrolpoint={} --#AIRWING.PatrolData - patrolpoint.type = "CAS" - patrolpoint.zone = Zone - patrolpoint.altitude=Altitude or math.random(10,20)*1000 - patrolpoint.speed=Speed or 250 - patrolpoint.noccupied=0 - patrolpoint.NoEngageZoneSet=NoEngageZoneSet - patrolpoint.TargetTypes=TargetTypes - patrolpoint.RangeMax=RangeMax or 100 - - if self.markpoints then - patrolpoint.marker=MARKER:New(Coordinate, "New Patrol Point"):ToAll() - AIRWING.UpdatePatrolPointMarker(patrolpoint) - end - - table.insert(self.pointsCAS, patrolpoint) - - return self -end - --- Add a patrol Point for RECON missions. -- @param #AIRWING self -- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. @@ -1067,9 +1029,6 @@ function AIRWING:onafterStatus(From, Event, To) -- Check CAP missions. self:CheckCAP() - - -- Check CAP missions. - self:CheckCAS() -- Check TANKER missions. self:CheckTANKER() @@ -1221,6 +1180,14 @@ function AIRWING:CheckCAP() end + if self.capOptionVaryStartTime then + + local ClockStart = math.random(self.capOptionVaryStartTime, self.capOptionVaryEndTime) + + missionCAP:SetTime(ClockStart) + + end + missionCAP.patroldata=patrol patrol.noccupied=patrol.noccupied+1 @@ -1234,47 +1201,6 @@ function AIRWING:CheckCAP() return self end ---- Check how many CAS missions are assigned and add number of missing missions. --- @param #AIRWING self --- @return #AIRWING self -function AIRWING:CheckCAS() - - local Ncap=0 - - - -- Count CAP missions. - for _,_mission in pairs(self.missionqueue) do - local mission=_mission --Ops.Auftrag#AUFTRAG - - if mission:IsNotOver() and (mission.type==AUFTRAG.Type.CASENHANCED or mission.type == AUFTRAG.Type.PATROLRACETRACK) and mission.patroldata then - Ncap=Ncap+1 - end - - end - - for i=1,self.nflightsCAS-Ncap do - - local patrol=self:_GetPatrolData(self.pointsCAS) - - local altitude=patrol.altitude+500*patrol.noccupied - - local missionCAS = nil -- Ops.Auftrag#AUFTRAG - - missionCAS=AUFTRAG:NewCASENHANCED(patrol.zone,altitude,patrol.speed,patrol.RangeMax,patrol.NoEngageZoneSet,patrol.TargetTypes) - - missionCAS.patroldata=patrol - - patrol.noccupied=patrol.noccupied+1 - - if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end - - self:AddMission(missionCAS) - - end - - return self -end - --- Check how many RECON missions are assigned and add number of missing missions. -- @param #AIRWING self -- @return #AIRWING self diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 79d699a49..1cb3385ee 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -458,6 +458,17 @@ function EASYGCICAP:SetDefaultOverhead(Overhead) return self end +--- Set CAP mission start to vary randomly between Start end End seconds. +-- @param #EASYGCICAP self +-- @param #number Start +-- @param #number End +-- @return #EASYGCICAP self +function EASYGCICAP:SetCapStartTimeVariation(Start, End) + self.capOptionVaryStartTime = Start or 5 + self.capOptionVaryEndTime = End or 60 + return self +end + --- Add an AirWing to the manager -- @param #EASYGCICAP self -- @param #string Airbasename @@ -511,6 +522,11 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) CAP_Wing:SetRespawnAfterDestroyed() CAP_Wing:SetNumberCAP(self.capgrouping) CAP_Wing:SetCapCloseRaceTrack(true) + + if self.capOptionVaryStartTime then + CAP_Wing:SetCapStartTimeVariation(self.capOptionVaryStartTime,self.capOptionVaryEndTime) + end + if CapFormation then CAP_Wing:SetCAPFormation(CapFormation) end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 384281c32..4f291db8e 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -938,20 +938,22 @@ end -- @param #CONTROLLABLE self -- @param #number Frequency Radio frequency in MHz. -- @param #number Modulation Radio modulation. Default `radio.modulation.AM`. +-- @param #number Power (Optional) Power of the Radio in Watts. Defaults to 10. -- @param #number Delay (Optional) Delay in seconds before the frequency is set. Default is immediately. -- @return #CONTROLLABLE self -function CONTROLLABLE:CommandSetFrequency( Frequency, Modulation, Delay ) +function CONTROLLABLE:CommandSetFrequency( Frequency, Modulation, Power, Delay ) local CommandSetFrequency = { id = 'SetFrequency', params = { frequency = Frequency * 1000000, modulation = Modulation or radio.modulation.AM, + power=Power or 10, }, } if Delay and Delay > 0 then - SCHEDULER:New( nil, self.CommandSetFrequency, { self, Frequency, Modulation }, Delay ) + SCHEDULER:New( nil, self.CommandSetFrequency, { self, Frequency, Modulation, Power } ) else self:SetCommand( CommandSetFrequency ) end @@ -959,6 +961,32 @@ function CONTROLLABLE:CommandSetFrequency( Frequency, Modulation, Delay ) return self end +--- [AIR] Set radio frequency. See [DCS command EPLRS](https://wiki.hoggitworld.com/view/DCS_command_setFrequencyForUnit) +-- @param #CONTROLLABLE self +-- @param #number Frequency Radio frequency in MHz. +-- @param #number Modulation Radio modulation. Default `radio.modulation.AM`. +-- @param #number Power (Optional) Power of the Radio in Watts. Defaults to 10. +-- @param #UnitID UnitID (Optional, if your object is a UNIT) The UNIT ID this is for. +-- @param #number Delay (Optional) Delay in seconds before the frequency is set. Default is immediately. +-- @return #CONTROLLABLE self +function CONTROLLABLE:CommandSetFrequencyForUnit(Frequency,Modulation,Power,UnitID,Delay) + local CommandSetFrequencyForUnit={ + id='SetFrequencyForUnit', + params={ + frequency=Frequency*1000000, + modulation=Modulation or radio.modulation.AM, + unitId=UnitID or self:GetID(), + power=Power or 10, + }, + } + if Delay and Delay>0 then + SCHEDULER:New(nil,self.CommandSetFrequencyForUnit,{self,Frequency,Modulation,Power,UnitID}) + else + self:SetCommand(CommandSetFrequencyForUnit) + end + return self +end + --- Set EPLRS data link on/off. -- @param #CONTROLLABLE self -- @param #boolean SwitchOnOff If true (or nil) switch EPLRS on. If false switch off. From f5f86e9ba6913edc9a6455581645b36921478a73 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 18 Jan 2024 14:27:09 +0100 Subject: [PATCH 496/603] xxx --- Moose Development/Moose/Ops/ATIS.lua | 2 +- Moose Development/Moose/Wrapper/Airbase.lua | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index d07c751f1..a98245b0b 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -700,7 +700,7 @@ ATIS.Messages = { EN = { HOURS = "hours", - TIME = "hours", + TIME = "Hours", NOCLOUDINFO = "Cloud coverage information not available", OVERCAST = "Overcast", BROKEN = "Broken clouds", diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 76da83c94..6f1ecd91a 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1899,8 +1899,17 @@ function AIRBASE:_InitRunways(IncludeInverse) local heading=math.deg(bearing) -- Data table. - local runway={} --#AIRBASE.Runway - runway.name=string.format("%02d", tonumber(name)) + local runway={} --#AIRBASE.Runway + + local namefromheading = math.floor(heading/10) + + if self.AirbaseName == AIRBASE.Syria.Beirut_Rafic_Hariri and math.abs(namefromheading-name) > 1 then + runway.name=string.format("%02d", tonumber(namefromheading)) + else + runway.name=string.format("%02d", tonumber(name)) + end + + --runway.name=string.format("%02d", tonumber(name)) runway.magheading=tonumber(runway.name)*10 runway.heading=heading runway.width=width or 0 From 4762b88980f9b00be2e5d3d15fed1245d492f31c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 19 Jan 2024 19:06:34 +0100 Subject: [PATCH 497/603] AVoid for pairs error --- Moose Development/Moose/Core/Set.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 52c899bec..70fe7eaad 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1204,7 +1204,7 @@ do if not DontSetCargoBayLimit then -- I set the default cargo bay weight limit each time a new group is added to the set. -- TODO Why is this here in the first place? - for UnitID, UnitData in pairs( group:GetUnits() ) do + for UnitID, UnitData in pairs( group:GetUnits() or {} ) do if UnitData and UnitData:IsAlive() then UnitData:SetCargoBayWeightLimit() end From c408485c5b58d33b9471786114ad6a34a0bafd14 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 19 Jan 2024 19:08:42 +0100 Subject: [PATCH 498/603] Minor fixes --- Moose Development/Moose/Functional/Stratego.lua | 3 ++- Moose Development/Moose/Ops/Awacs.lua | 2 ++ Moose Development/Moose/Ops/Intelligence.lua | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 850f80b0f..2d9e9db04 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -435,7 +435,8 @@ function STRATEGO:AnalyseBases() if not abzone then abzone = ZONE_RADIUS:New(abname,ab:GetVec2(),500) end - local coa = ab:GetCoalition() + 1 + local coa = ab:GetCoalition() or 0 + coa = coa+1 local abtype = "AIRBASE" if ab:IsShip() then numrwys = 1 diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index d84541e17..28c710ccb 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -121,6 +121,7 @@ do -- @field #number TacticalIncrFreq -- @field #number TacticalModulation -- @field #number TacticalInterval +-- @field Core.Set#SET_GROUP DetectionSet -- @extends Core.Fsm#FSM @@ -603,6 +604,7 @@ AWACS = { TacticalIncrFreq = 0.5, TacticalModulation = radio.modulation.AM, TacticalInterval = 120, + DetectionSet = nil, } --- diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index 4e71d65b3..7751a140b 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -578,7 +578,7 @@ function INTEL:AddAgent(AgentGroup) end -- Add to detection set. - self.detectionset:AddGroup(AgentGroup) + self.detectionset:AddGroup(AgentGroup,true) return self end From 290609d5812b3a62376d4319fc610d39620671a3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 19 Jan 2024 19:30:48 +0100 Subject: [PATCH 499/603] FARP issue --- Moose Development/Moose/Functional/Stratego.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 2d9e9db04..8b9438783 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -435,7 +435,8 @@ function STRATEGO:AnalyseBases() if not abzone then abzone = ZONE_RADIUS:New(abname,ab:GetVec2(),500) end - local coa = ab:GetCoalition() or 0 + local coa = ab:GetCoalition() + if coa == nil then return end -- Spawned FARPS issue - these have no tangible data coa = coa+1 local abtype = "AIRBASE" if ab:IsShip() then From d5322466e9fafd2cf6aaa726c0dbaacae616cecf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 21 Jan 2024 16:44:31 +0100 Subject: [PATCH 500/603] SCENERY add-ons --- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Wrapper/Scenery.lua | 28 ++++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 70fe7eaad..0045ee7fa 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -8399,7 +8399,7 @@ do -- SET_SCENERY --- Calculate current relative lifepoints of the SET objects, i.e. Life divided by Life0 as percentage value, eg 75 meaning 75% alive. -- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Hence we will adjust the Life0 value to 120% -- of the last life value if life exceeds life0 ata any point. - -- Thus will will get a smooth percentage decrease, if you use this e.g. as success criteria for a bombing task. + -- Thus we will get a smooth percentage decrease, if you use this e.g. as success criteria for a bombing task. -- @param #SET_SCENERY self -- @return #number LifePoints function SET_SCENERY:GetRelativeLife() diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 1ef31a039..2706aad71 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -123,18 +123,38 @@ end --- Check if SCENERY Object is alive. --@param #SCENERY self +--@param #number Threshold (Optional) If given, SCENERY counts as alive above this relative life in percent (1..100). --@return #number life -function SCENERY:IsAlive() - return self:GetLife() >= 1 and true or false +function SCENERY:IsAlive(Threshold) + if not Threshold then + return self:GetLife() >= 1 and true or false + else + return self:GetRelativeLife() > Threshold and true or false + end end --- Check if SCENERY Object is dead. --@param #SCENERY self +--@param #number Threshold (Optional) If given, SCENERY counts as dead below this relative life in percent (1..100). --@return #number life -function SCENERY:IsDead() - return self:GetLife() < 1 and true or false +function SCENERY:IsDead(Threshold) + if not Threshold then + return self:GetLife() < 1 and true or false + else + return self:GetRelativeLife() <= Threshold and true or false + end end +--- Get SCENERY relative life in percent, e.g. 75. +--@param #SCENERY self +--@return #number rlife +function SCENERY:GetRelativeLife() + local life = self:GetLife() + local life0 = self:GetLife0() + local rlife = math.floor((life/life0)*100) + return rlife +end + --- Get the threat level of a SCENERY object. Always 0 as scenery does not pose a threat to anyone. --@param #SCENERY self --@return #number Threat level 0. From b03add80c6b63cabf0c232f9f1b76b7dadd3f13d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 21 Jan 2024 16:45:48 +0100 Subject: [PATCH 501/603] SCENERY additions --- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- Moose Development/Moose/Ops/Target.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 406563273..1e1fd5c9a 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -3207,7 +3207,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) local islasing = self.LasingDrone:IsLasing() == true and yes or no local prectext = self.gettext:GetEntry("POINTERTARGETREPORT",self.locale) prectext = string.format(prectext,inreach,islasing) - text = text .. prectext.."("..self.LaserCode..")" + text = text .. prectext.." ("..self.LaserCode..")" end end -- Buddylasing diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index c5b9ca558..abba2f886 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -1107,7 +1107,7 @@ function TARGET:GetTargetLife(Target) elseif Target.Type==TARGET.ObjectType.SCENERY then - if Target.Object and Target.Object:IsAlive() then + if Target.Object and Target.Object:IsAlive(25) then local life = Target.Object:GetLife() return life else From c4d8b64713f0fd58a780ff0b9e0ff6843bdfc291 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 23 Jan 2024 09:50:34 +0100 Subject: [PATCH 502/603] Stratego --- .../Moose/Functional/Stratego.lua | 20 +++++++++++-------- .../Moose/Functional/Warehouse.lua | 9 ++++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 8b9438783..3c48cb7a0 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -176,7 +176,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.3", + version = "0.2.4", portweight = 3, POIweight = 1, maxrunways = 3, @@ -948,22 +948,22 @@ function STRATEGO:FindClosestConsolidationTarget(Startpoint,BaseWeight) return shortest,target, weight, coa end ---- [USER] Get the next best strategic target node with same or higher BaseWeight. +--- [USER] Get the next best strategic target node with same or higher Consolidated Weight. -- @param #STRATEGO self -- @param #string Startpoint Name of start point. --- @param #number BaseWeight Base weight of the node, e.g. the number of runways of an airbase or the weight of ports or POIs. +-- @param #number Weight Consolidated Weight of the node, i.e. the calculated weight of the node based on number of runways, connections and a weight factor. -- @return #number ShortestDist Shortest distance found. -- @return #string Name Name of the target node. -- @return #number Weight Consolidated weight of the target node, zero if none found. -- @return #number Coalition Coaltion of the target. -function STRATEGO:FindClosestStrategicTarget(Startpoint,BaseWeight) - self:T(self.lid.."FindClosestStrategicTarget") +function STRATEGO:FindClosestStrategicTarget(Startpoint,Weight) + self:T(self.lid.."FindClosestStrategicTarget for "..Startpoint.." Weight "..Weight or 0) -- find existing routes local shortest = 1000*1000 local target = nil local weight = 0 local coa = nil - if not BaseWeight then BaseWeight = self.maxrunways end + if not Weight then Weight = self.maxrunways end local startpoint = string.gsub(Startpoint,"[%p%s]",".") for _,_route in pairs(self.routexists) do if string.find(_route,startpoint,1,true) then @@ -971,7 +971,11 @@ function STRATEGO:FindClosestStrategicTarget(Startpoint,BaseWeight) local tname = string.gsub(_route,startpoint,"") local tname = string.gsub(tname,";","") local cname = self.easynames[tname] - if dist < shortest and self.airbasetable[cname].coalition ~= self.coalition and self.airbasetable[cname].baseweight >= BaseWeight then + local coa = self.airbasetable[cname].coalition + local tweight = self.airbasetable[cname].baseweight + local ttweight = self.airbasetable[cname].weight + self:T("Start -> End: "..startpoint.." -> "..cname) + if (dist < shortest) and (coa ~= self.coalition) and (tweight >= Weight) then shortest = dist target = cname weight = self.airbasetable[cname].weight @@ -991,7 +995,7 @@ function STRATEGO:FindStrategicTargets() for _,_data in pairs(self.airbasetable) do local data = _data -- #STRATEGO.Data if data.coalition == self.coalition then - local dist, name, points, coa = self:FindClosestStrategicTarget(data.name,self.maxrunways) + local dist, name, points, coa = self:FindClosestStrategicTarget(data.name,data.weight) if coa == coalition.side.NEUTRAL and points ~= 0 then local fpoints = points + self.NeutralBenefit local tries = 1 diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 86c1255c7..686bede21 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3414,7 +3414,7 @@ end -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- On after Start event. Starts the warehouse. Addes event handlers and schedules status updates of reqests and queue. +--- On after Start event. Starts the warehouse. Adds event handlers and schedules status updates of reqests and queue. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -3595,6 +3595,7 @@ function WAREHOUSE:onafterStatus(From, Event, To) local Trepair=self:GetRunwayRepairtime() self:I(self.lid..string.format("Runway destroyed! Will be repaired in %d sec", Trepair)) if Trepair==0 then + self.runwaydestroyed = nil self:RunwayRepaired() end end @@ -5392,7 +5393,8 @@ function WAREHOUSE:onafterRunwayDestroyed(From, Event, To) self:_InfoMessage(text) self.runwaydestroyed=timer.getAbsTime() - + + return self end --- On after "RunwayRepaired" event. @@ -5407,7 +5409,8 @@ function WAREHOUSE:onafterRunwayRepaired(From, Event, To) self:_InfoMessage(text) self.runwaydestroyed=nil - + + return self end From fb16a293921ba7e7fc9a1911b27f4573607b4be5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 27 Jan 2024 14:41:49 +0100 Subject: [PATCH 503/603] M2K specific helper for tostringLL --- Moose Development/Moose/Utilities/Utils.lua | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 98425d7c8..259f6d223 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -825,6 +825,64 @@ UTILS.tostringLL = function( lat, lon, acc, DMS) end end +--[[acc: +in DM: decimal point of minutes. +In DMS: decimal point of seconds. +position after the decimal of the least significant digit: +So: +42.32 - acc of 2. +]] +UTILS.tostringLLM2KData = function( lat, lon, acc) + + local latHemi, lonHemi + if lat > 0 then + latHemi = 'N' + else + latHemi = 'S' + end + + if lon > 0 then + lonHemi = 'E' + else + lonHemi = 'W' + end + + lat = math.abs(lat) + lon = math.abs(lon) + + local latDeg = math.floor(lat) + local latMin = (lat - latDeg)*60 + + local lonDeg = math.floor(lon) + local lonMin = (lon - lonDeg)*60 + + -- degrees, decimal minutes. + latMin = UTILS.Round(latMin, acc) + lonMin = UTILS.Round(lonMin, acc) + + if latMin == 60 then + latMin = 0 + latDeg = latDeg + 1 + end + + if lonMin == 60 then + lonMin = 0 + lonDeg = lonDeg + 1 + end + + local minFrmtStr -- create the formatting string for the minutes place + if acc <= 0 then -- no decimal place. + minFrmtStr = '%02d' + else + local width = 3 + acc -- 01.310 - that's a width of 6, for example. + minFrmtStr = '%0' .. width .. '.' .. acc .. 'f' + end + + -- 024 23'N or 024 23.123'N + return latHemi..string.format('%02d:', latDeg) .. string.format(minFrmtStr, latMin), lonHemi..string.format('%02d:', lonDeg) .. string.format(minFrmtStr, lonMin) + +end + -- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. UTILS.tostringMGRS = function(MGRS, acc) --R2.1 From 11551a9816e6d6acefba8cc0d1b4f19e09ea1b92 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 3 Feb 2024 09:10:25 +0100 Subject: [PATCH 504/603] Sam Data --- Moose Development/Moose/Functional/Mantis.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 6ed0d8458..021971eb8 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -347,9 +347,9 @@ MANTIS.SamType = { -- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range) -- @field #string Radar Radar typename on unit level (used as key) MANTIS.SamData = { - ["Hawk"] = { Range=44, Blindspot=0, Height=9, Type="Medium", Radar="Hawk" }, -- measures in km - ["NASAMS"] = { Range=14, Blindspot=0, Height=3, Type="Short", Radar="NSAMS" }, - ["Patriot"] = { Range=99, Blindspot=0, Height=9, Type="Long", Radar="Patriot" }, + ["Hawk"] = { Range=44, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km + ["NASAMS"] = { Range=14, Blindspot=0, Height=8, Type="Short", Radar="NSAMS" }, + ["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot" }, ["Rapier"] = { Range=6, Blindspot=0, Height=3, Type="Short", Radar="rapier" }, ["SA-2"] = { Range=40, Blindspot=7, Height=25, Type="Medium", Radar="S_75M_Volhov" }, ["SA-3"] = { Range=18, Blindspot=6, Height=18, Type="Short", Radar="5p73 s-125 ln" }, @@ -357,7 +357,7 @@ MANTIS.SamData = { ["SA-6"] = { Range=25, Blindspot=0, Height=8, Type="Medium", Radar="1S91" }, ["SA-10"] = { Range=119, Blindspot=0, Height=18, Type="Long" , Radar="S-300PS 4"}, ["SA-11"] = { Range=35, Blindspot=0, Height=20, Type="Medium", Radar="SA-11" }, - ["Roland"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Roland" }, + ["Roland"] = { Range=8, Blindspot=0, Height=5, Type="Short", Radar="Roland" }, ["HQ-7"] = { Range=12, Blindspot=0, Height=3, Type="Short", Radar="HQ-7" }, ["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Strela" }, ["SA-8"] = { Range=10, Blindspot=0, Height=5, Type="Short", Radar="Osa 9A33" }, From a66ad1f086010cdedd6cee41d7735a3fd4af6e74 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 3 Feb 2024 12:23:18 +0100 Subject: [PATCH 505/603] MANTIS --- Moose Development/Moose/Functional/Mantis.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index a85c1f218..df982fcc0 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -347,17 +347,17 @@ MANTIS.SamType = { -- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range) -- @field #string Radar Radar typename on unit level (used as key) MANTIS.SamData = { - ["Hawk"] = { Range=44, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km - ["NASAMS"] = { Range=14, Blindspot=0, Height=8, Type="Short", Radar="NSAMS" }, + ["Hawk"] = { Range=35, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km + ["NASAMS"] = { Range=14, Blindspot=0, Height=7, Type="Short", Radar="NSAMS" }, -- AIM 120B ["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot" }, - ["Rapier"] = { Range=6, Blindspot=0, Height=3, Type="Short", Radar="rapier" }, + ["Rapier"] = { Range=10, Blindspot=0, Height=3, Type="Short", Radar="rapier" }, ["SA-2"] = { Range=40, Blindspot=7, Height=25, Type="Medium", Radar="S_75M_Volhov" }, ["SA-3"] = { Range=18, Blindspot=6, Height=18, Type="Short", Radar="5p73 s-125 ln" }, ["SA-5"] = { Range=250, Blindspot=7, Height=40, Type="Long", Radar="5N62V" }, ["SA-6"] = { Range=25, Blindspot=0, Height=8, Type="Medium", Radar="1S91" }, ["SA-10"] = { Range=119, Blindspot=0, Height=18, Type="Long" , Radar="S-300PS 4"}, ["SA-11"] = { Range=35, Blindspot=0, Height=20, Type="Medium", Radar="SA-11" }, - ["Roland"] = { Range=8, Blindspot=0, Height=5, Type="Short", Radar="Roland" }, + ["Roland"] = { Range=5, Blindspot=0, Height=5, Type="Short", Radar="Roland" }, ["HQ-7"] = { Range=12, Blindspot=0, Height=3, Type="Short", Radar="HQ-7" }, ["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Strela" }, ["SA-8"] = { Range=10, Blindspot=0, Height=5, Type="Short", Radar="Osa 9A33" }, From 969a658e15803c75f4ab1b66dd98a6a79444f7bf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 4 Feb 2024 13:11:57 +0100 Subject: [PATCH 506/603] CLIENTMENUMANAGER - docu --- Moose Development/Moose/Core/ClientMenu.lua | 24 +++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 525b3533d..a87583be9 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -78,7 +78,7 @@ CLIENTMENU_ID = 0 --- Create an new CLIENTMENU object. -- @param #CLIENTMENU self --- @param Wrapper.Client#CLIENT Client The client for whom this entry is. +-- @param Wrapper.Client#CLIENT Client The client for whom this entry is. Leave as nil for a generic entry. -- @param #string Text Text of the F10 menu entry. -- @param #CLIENTMENU Parent The parent menu entry. -- @param #string Function (optional) Function to call when the entry is used. @@ -324,6 +324,22 @@ end -- -- Many functions can either change the tree for one client or for all clients. -- +-- ## Conceptual remarks +-- +-- There's a couple of things to fully understand: +-- +-- 1) **CLIENTMENUMANAGER** manages a set of entries from **CLIENTMENU**, it's main purpose is to administer the *shadow menu tree*, ie. a menu structure which is not +-- (yet) visible to any client +-- 2) The entries are **CLIENTMENU** objects, which are linked in a tree form. There's two ways to create them: +-- A) in the manager with ":NewEntry()" which initially +-- adds it to the shadow menu **only** +-- B) stand-alone directly as `CLIENTMENU:NewEntry()` - here it depends on whether or not you gave a CLIENT object if the entry is created as generic entry or pushed +-- a **specific** client. **Be aware** though that the entries are not managed by the CLIENTMANAGER before the next step! +-- A generic entry can be added to the manager (and the shadow tree) with `:AddEntry()` - this will also push it to all clients(!) if no client is given, or a specific client only. +-- 3) Pushing only works for alive clients. +-- 4) Live and shadow tree entries are managed via the CLIENTMENUMANAGER object. +-- 5) `Propagate()`refreshes the menu tree for all, or a single client. +-- -- ## Create a base reference tree and send to all clients -- -- local clientset = SET_CLIENT:New():FilterStart() @@ -492,7 +508,7 @@ function CLIENTMENUMANAGER:_EventHandler(EventData) return self end ---- Set this Client Manager to auto-propagate menus to newly joined players. Useful if you have **one** menu structure only. +--- Set this Client Manager to auto-propagate menus **once** to newly joined players. Useful if you have **one** menu structure only. Does not automatically push follow-up changes to the client(s). -- @param #CLIENTMENUMANAGER self -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:InitAutoPropagation() @@ -507,7 +523,7 @@ function CLIENTMENUMANAGER:InitAutoPropagation() return self end ---- Create a new entry in the generic structure. +--- Create a new entry in the **generic** structure. -- @param #CLIENTMENUMANAGER self -- @param #string Text Text of the F10 menu entry. -- @param #CLIENTMENU Parent The parent menu entry. @@ -695,7 +711,7 @@ function CLIENTMENUMANAGER:Propagate(Client) return self end ---- Push a single previously created entry into the menu structure of all clients. +--- Push a single previously created entry into the F10 menu structure of all clients. -- @param #CLIENTMENUMANAGER self -- @param #CLIENTMENU Entry The entry to add. -- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. From 25a5e821efd86c28811ab0b7588c70c4380d3832 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 8 Feb 2024 11:58:46 +0100 Subject: [PATCH 507/603] PLAYERTASK - small fix for possibly missing coordinate on the taskinfo function --- Moose Development/Moose/Ops/PlayerTask.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 643355ed8..19d7a1ffb 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -411,6 +411,15 @@ function PLAYERTASK:IsDone() return IsDone end +--- [User] Check if PLAYERTASK has clients assigned to it. +-- @param #PLAYERTASK self +-- @return #boolean hasclients +function PLAYERTASK:HasClients() + self:T(self.lid.."HasClients?") + local hasclients = self:CountClients() > 0 and true or false + return hasclients +end + --- [User] Get client names assigned as table of #strings -- @param #PLAYERTASK self -- @return #table clients @@ -1552,7 +1561,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.64" +PLAYERTASKCONTROLLER.version="0.1.65" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -3173,7 +3182,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale) local taskname = string.format(tname,task.Type,task.PlayerTaskNr) local ttstaskname = string.format(ttsname,task.TTSType,task.PlayerTaskNr) - local Coordinate = task.Target:GetCoordinate() + local Coordinate = task.Target:GetCoordinate() or COORDINATE:New(0,0,0) local CoordText = "" local CoordTextLLDM = nil if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then From a1151584e1fffece3265d2f06cc74aabc4c2fe20 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 12 Feb 2024 18:33:18 +0100 Subject: [PATCH 508/603] Nicer Self:I() etc output --- Moose Development/Moose/Core/Base.lua | 51 +++++++-------------- Moose Development/Moose/Utilities/Utils.lua | 18 ++++++-- 2 files changed, 29 insertions(+), 40 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 595f89279..dede44ad3 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -1144,6 +1144,18 @@ function BASE:TraceClassMethod( Class, Method ) self:I( "Tracing method " .. Method .. " of class " .. Class ) end +--- (Internal) Serialize arguments +-- @param #BASE self +-- @param #table Arguments +-- @return #string Text +function BASE:_Serialize(Arguments) + local text = UTILS.PrintTableToLog({Arguments}, 0, true) + text = string.gsub(text,"\n","") + text = string.gsub(text,"%(%(","%(") + text = string.gsub(text,"%)%)","%)") + return text +end + --- Trace a function call. This function is private. -- @param #BASE self -- @param Arguments A #table or any field. @@ -1168,7 +1180,7 @@ function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) if DebugInfoFrom then LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, BASE:_Serialize(Arguments) ) ) end end end @@ -1242,7 +1254,7 @@ function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) if DebugInfoFrom then LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s", LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, UTILS.BasicSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s", LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, BASE:_Serialize(Arguments) ) ) end end end @@ -1314,7 +1326,7 @@ function BASE:E( Arguments ) env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) ) else - env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, UTILS.BasicSerialize( Arguments ) ) ) + env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, BASE:_Serialize(Arguments) ) ) end end @@ -1341,39 +1353,8 @@ function BASE:I( Arguments ) env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) ) else - env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.OneLineSerialize( Arguments ) ) ) + env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, BASE:_Serialize(Arguments)) ) end end ---- old stuff - --- function BASE:_Destructor() --- --self:E("_Destructor") --- --- --self:EventRemoveAll() --- end - --- THIS IS WHY WE NEED LUA 5.2 ... --- function BASE:_SetDestructor() --- --- -- TODO: Okay, this is really technical... --- -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... --- -- Therefore, I am parking this logic until I've properly discussed all this with the community. --- --- local proxy = newproxy(true) --- local proxyMeta = getmetatable(proxy) --- --- proxyMeta.__gc = function () --- env.info("In __gc for " .. self:GetClassNameAndID() ) --- if self._Destructor then --- self:_Destructor() --- end --- end --- --- -- keep the userdata from newproxy reachable until the object --- -- table is about to be garbage-collected - then the __gc hook --- -- will be invoked and the destructor called --- rawset( self, '__proxy', proxy ) --- --- end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 259f6d223..6005dcbce 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -444,10 +444,11 @@ end --- Print a table to log in a nice format -- @param #table table The table to print -- @param #number indent Number of indents +-- @param #boolean noprint Don't log but return text -- @return #string text Text created on the fly of the log output -function UTILS.PrintTableToLog(table, indent) +function UTILS.PrintTableToLog(table, indent, noprint) local text = "\n" - if not table then + if not table or type(table) ~= "table" then env.warning("No table passed!") return nil end @@ -455,11 +456,16 @@ function UTILS.PrintTableToLog(table, indent) for k, v in pairs(table) do if string.find(k," ") then k='"'..k..'"'end if type(v) == "table" then - env.info(string.rep(" ", indent) .. tostring(k) .. " = {") + if not noprint then + env.info(string.rep(" ", indent) .. tostring(k) .. " = {") + end text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n" text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n" - env.info(string.rep(" ", indent) .. "},") + if not noprint then + env.info(string.rep(" ", indent) .. "},") + end text = text .. string.rep(" ", indent) .. "},\n" + elseif type(v) == "function" then else local value if tostring(v) == "true" or tostring(v) == "false" or tonumber(v) ~= nil then @@ -467,7 +473,9 @@ function UTILS.PrintTableToLog(table, indent) else value = '"'..tostring(v)..'"' end - env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n") + if not noprint then + env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n") + end text = text .. string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n" end end From 48b20299b2a77f527a014ffd5922a13fd8682f75 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 13 Feb 2024 16:45:20 +0100 Subject: [PATCH 509/603] xx --- Moose Development/Moose/Wrapper/Group.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 52741e2c2..a37c32132 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2780,6 +2780,14 @@ end -- @param #boolean switch If true, Invisible is enabled. If false, Invisible is disabled. -- @return #GROUP self function GROUP:SetCommandInvisible(switch) + return self:CommandSetInvisible(switch) +end + +--- Switch on/off invisible flag for the group. +-- @param #GROUP self +-- @param #boolean switch If true, Invisible is enabled. If false, Invisible is disabled. +-- @return #GROUP self +function GROUP:CommandSetInvisible(switch) self:F2( self.GroupName ) if switch==nil then switch=false @@ -2794,6 +2802,14 @@ end -- @param #boolean switch If true, Immortal is enabled. If false, Immortal is disabled. -- @return #GROUP self function GROUP:SetCommandImmortal(switch) + return self:CommandSetImmortal(switch) +end + +--- Switch on/off immortal flag for the group. +-- @param #GROUP self +-- @param #boolean switch If true, Immortal is enabled. If false, Immortal is disabled. +-- @return #GROUP self +function GROUP:CommandSetImmortal(switch) self:F2( self.GroupName ) if switch==nil then switch=false From 3b139a92fba32386c330c69aabdb99626dd96118 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 16 Feb 2024 14:09:42 +0100 Subject: [PATCH 510/603] xxx --- .../Moose/Functional/Stratego.lua | 175 +++++++++--------- 1 file changed, 90 insertions(+), 85 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 3c48cb7a0..2655500f8 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -41,6 +41,7 @@ -- @field #boolean usebudget -- @field #number CaptureUnits -- @field #number CaptureThreatlevel +-- @field #boolean ExcludeShips -- @extends Core.Base#BASE -- @extends Core.Fsm#FSM @@ -176,7 +177,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.4", + version = "0.2.5", portweight = 3, POIweight = 1, maxrunways = 3, @@ -195,6 +196,7 @@ STRATEGO = { usebudget = false, CaptureUnits = 3, CaptureThreatlevel = 1, + ExcludeShips = true, } --- @@ -256,6 +258,7 @@ function STRATEGO:New(Name,Coalition,MaxDist) self.maxdist = MaxDist or 150 -- km self.disttable = {} self.routexists = {} + self.ExcludeShips = true self.lid = string.format("STRATEGO %s %s | ",self.name,self.version) @@ -427,6 +430,7 @@ function STRATEGO:AnalyseBases() self.bases:ForEach( function(afb) local ab = afb -- Wrapper.Airbase#AIRBASE + if self.ExcludeShips and ab:IsShip() then return end local abname = ab:GetName() local runways = ab:GetRunways() local numrwys = #runways @@ -438,14 +442,14 @@ function STRATEGO:AnalyseBases() local coa = ab:GetCoalition() if coa == nil then return end -- Spawned FARPS issue - these have no tangible data coa = coa+1 - local abtype = "AIRBASE" + local abtype = STRATEGO.Type.AIRBASE if ab:IsShip() then numrwys = 1 - abtype = "SHIP" + abtype = STRATEGO.Type.SHIP end if ab:IsHelipad() then numrwys = 1 - abtype = "FARP" + abtype = STRATEGO.Type.FARP end local coord = ab:GetCoordinate() if debug then @@ -481,10 +485,10 @@ function STRATEGO:UpdateNodeCoalitions() local newtable = {} for _id,_data in pairs(self.airbasetable) do local data = _data -- #STRATEGO.Data - if data.type == "AIRBASE" or data.type == "FARP" then - data.coalition = AIRBASE:FindByName(data.name):GetCoalition() + if data.type == STRATEGO.Type.AIRBASE or data.type == STRATEGO.Type.FARP or data.type == STRATEGO.Type.SHIP then + data.coalition = AIRBASE:FindByName(data.name):GetCoalition() or 0 else - data.coalition = data.opszone:GetOwner() + data.coalition = data.opszone:GetOwner() or 0 end newtable[_id] = _data end @@ -937,11 +941,13 @@ function STRATEGO:FindClosestConsolidationTarget(Startpoint,BaseWeight) local cname = self.easynames[tname] local targetweight = self.airbasetable[cname].baseweight coa = self.airbasetable[cname].coalition + --self:T("Start -> End: "..startpoint.." -> "..cname) if (dist < shortest) and (coa ~= self.coalition) and (BaseWeight >= targetweight) then + self:T("Found Consolidation Target: "..cname) shortest = dist target = cname weight = self.airbasetable[cname].weight - coa = self.airbasetable[cname].coalition + coa = coa end end end @@ -974,8 +980,9 @@ function STRATEGO:FindClosestStrategicTarget(Startpoint,Weight) local coa = self.airbasetable[cname].coalition local tweight = self.airbasetable[cname].baseweight local ttweight = self.airbasetable[cname].weight - self:T("Start -> End: "..startpoint.." -> "..cname) + --self:T("Start -> End: "..startpoint.." -> "..cname) if (dist < shortest) and (coa ~= self.coalition) and (tweight >= Weight) then + self:T("Found Strategic Target: "..cname) shortest = dist target = cname weight = self.airbasetable[cname].weight @@ -996,38 +1003,31 @@ function STRATEGO:FindStrategicTargets() local data = _data -- #STRATEGO.Data if data.coalition == self.coalition then local dist, name, points, coa = self:FindClosestStrategicTarget(data.name,data.weight) - if coa == coalition.side.NEUTRAL and points ~= 0 then - local fpoints = points + self.NeutralBenefit - local tries = 1 - while targets[fpoints] or tries < 100 do - fpoints = points + (self.NeutralBenefit+math.random(1,100)) - tries = tries + 1 - end - targets[fpoints] = { - name = name, - dist = dist, - points = fpoints, - coalition = coa, - coalitionname = UTILS.GetCoalitionName(coa), - coordinate = self.airbasetable[name].coord, - } + if points > 0 then + self:T({dist=dist, name=name, points=points, coa=coa}) end - local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE - if coa == enemycoa and points ~= 0 then - local fpoints = points - local tries = 1 - while targets[fpoints] or tries < 100 do - fpoints = points + (math.random(1,100)) - tries = tries + 1 + if points ~= 0 then + local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE + self:T("Enemycoa = "..enemycoa) + if coa == coalition.side.NEUTRAL then + local tdata = {} + tdata.name = name + tdata.dist = dist + tdata.points = points + self.NeutralBenefit + tdata.coalition = coa + tdata.coalitionname = UTILS.GetCoalitionName(coa) + tdata.coordinate = self.airbasetable[name].coord + table.insert(targets,tdata) + else + local tdata = {} + tdata.name = name + tdata.dist = dist + tdata.points = points + tdata.coalition = coa + tdata.coalitionname = UTILS.GetCoalitionName(coa) + tdata.coordinate = self.airbasetable[name].coord + table.insert(targets,tdata) end - targets[fpoints] = { - name = name, - dist = dist, - points = fpoints, - coalition = coa, - coalitionname = UTILS.GetCoalitionName(coa), - coordinate = self.airbasetable[name].coord, - } end end end @@ -1044,38 +1044,31 @@ function STRATEGO:FindConsolidationTargets() local data = _data -- #STRATEGO.Data if data.coalition == self.coalition then local dist, name, points, coa = self:FindClosestConsolidationTarget(data.name,self.maxrunways-1) - if coa == coalition.side.NEUTRAL and points ~= 0 then - local fpoints = points + self.NeutralBenefit - local tries = 1 - while targets[fpoints] or tries < 100 do - fpoints = points - (self.NeutralBenefit+math.random(1,100)) - tries = tries + 1 - end - targets[fpoints] = { - name = name, - dist = dist, - points = fpoints, - coalition = coa, - coalitionname = UTILS.GetCoalitionName(coa), - coordinate = self.airbasetable[name].coord, - } + if points > 0 then + self:T({dist=dist, name=name, points=points, coa=coa}) end - local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE - if coa == enemycoa and points ~= 0 then - local fpoints = points - local tries = 1 - while targets[fpoints] or tries < 100 do - fpoints = points - (math.random(1,100)) - tries = tries + 1 + if points ~= 0 then + local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE + self:T("Enemycoa = "..enemycoa) + if coa == coalition.side.NEUTRAL then + local tdata = {} + tdata.name = name + tdata.dist = dist + tdata.points = points + self.NeutralBenefit + tdata.coalition = coa + tdata.coalitionname = UTILS.GetCoalitionName(coa) + tdata.coordinate = self.airbasetable[name].coord + table.insert(targets,tdata) + else + local tdata = {} + tdata.name = name + tdata.dist = dist + tdata.points = points + tdata.coalition = coa + tdata.coalitionname = UTILS.GetCoalitionName(coa) + tdata.coordinate = self.airbasetable[name].coord + table.insert(targets,tdata) end - targets[fpoints] = { - name = name, - dist = dist, - points = fpoints, - coalition = coa, - coalitionname = UTILS.GetCoalitionName(coa), - coordinate = self.airbasetable[name].coord, - } end end end @@ -1245,13 +1238,15 @@ end -- @return #table Target Table with #STRATEGO.Target data or nil if none found. function STRATEGO:FindAffordableStrategicTarget() self:T(self.lid.."FindAffordableStrategicTarget") - local targets = self:FindStrategicTargets() -- #table of #STRATEGO.Target + local Stargets = self:FindStrategicTargets() -- #table of #STRATEGO.Target + --UTILS.PrintTableToLog(Stargets,1) local budget = self.Budget --local leftover = self.Budget - local target = nil -- #STRATEGO.Target + local ftarget = nil -- #STRATEGO.Target local Targets = {} - for _,_data in pairs(targets) do + for _,_data in pairs(Stargets) do local data = _data -- #STRATEGO.Target + self:T("Considering Strategic Target "..data.name) --if data.points <= budget and budget-data.points < leftover then if data.points <= budget then --leftover = budget-data.points @@ -1259,14 +1254,18 @@ function STRATEGO:FindAffordableStrategicTarget() self:T(self.lid.."Affordable strategic target: "..data.name) end end - if not targets then + if #Targets == 0 then self:T(self.lid.."No suitable target found!") return nil end - target = Targets[math.random(1,#Targets)] - if target then - self:T(self.lid.."Final affordable strategic target: "..target.name) - return target + if #Targets > 1 then + ftarget = Targets[math.random(1,#Targets)] + else + ftarget = Targets[1] + end + if ftarget then + self:T(self.lid.."Final affordable strategic target: "..ftarget.name) + return ftarget else return nil end @@ -1277,13 +1276,15 @@ end -- @return #table Target Table with #STRATEGO.Target data or nil if none found. function STRATEGO:FindAffordableConsolidationTarget() self:T(self.lid.."FindAffordableConsolidationTarget") - local targets = self:FindConsolidationTargets() -- #table of #STRATEGO.Target + local Ctargets = self:FindConsolidationTargets() -- #table of #STRATEGO.Target + --UTILS.PrintTableToLog(Ctargets,1) local budget = self.Budget --local leftover = self.Budget - local target = nil -- #STRATEGO.Target + local ftarget = nil -- #STRATEGO.Target local Targets = {} - for _,_data in pairs(targets) do + for _,_data in pairs(Ctargets) do local data = _data -- #STRATEGO.Target + self:T("Considering Consolidation Target "..data.name) --if data.points <= budget and budget-data.points < leftover then if data.points <= budget then --leftover = budget-data.points @@ -1291,14 +1292,18 @@ function STRATEGO:FindAffordableConsolidationTarget() self:T(self.lid.."Affordable consolidation target: "..data.name) end end - if not targets then + if #Targets == 0 then self:T(self.lid.."No suitable target found!") return nil end - target = Targets[math.random(1,#Targets)] - if target then - self:T(self.lid.."Final affordable consolidation target: "..target.name) - return target + if #Targets > 1 then + ftarget = Targets[math.random(1,#Targets)] + else + ftarget = Targets[1] + end + if ftarget then + self:T(self.lid.."Final affordable consolidation target: "..ftarget.name) + return ftarget else return nil end From 15cee84695a24abfe25e264564c57bd6d405e6ce Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 20 Feb 2024 12:07:08 +0100 Subject: [PATCH 511/603] Minor fixes --- Moose Development/Moose/Core/Spawn.lua | 2 +- Moose Development/Moose/Core/Zone.lua | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index a6651a7c1..c34c8ccbd 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -724,7 +724,7 @@ end -- @param #number Country Country id as number or enumerator: -- -- * @{DCS#country.id.RUSSIA} --- * @{DCS#county.id.USA} +-- * @{DCS#country.id.USA} -- -- @return #SPAWN self function SPAWN:InitCountry( Country ) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 3317df260..db176e783 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -899,7 +899,8 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) local Point = {} local Vec2 = self:GetVec2() - + local countryID = CountryID or country.id.USA + Points = Points and Points or 360 local Angle @@ -910,7 +911,7 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() - local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + local CountryName = _DATABASE.COUNTRY_NAME[countryID] local Tire = { ["country"] = CountryName, @@ -925,7 +926,7 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) ["heading"] = 0, } -- end of ["group"] - local Group = coalition.addStaticObject( CountryID, Tire ) + local Group = coalition.addStaticObject( countryID, Tire ) if UnBound and UnBound == true then Group:destroy() end From 1f74899f8a9c3814c964b43672f6487076bc68ac Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 20 Feb 2024 14:27:54 +0100 Subject: [PATCH 512/603] SPAWN/DATABASE - try to ensure unique STN/SADL numbers --- Moose Development/Moose/Core/Database.lua | 93 ++++++++++++++++++++++- Moose Development/Moose/Core/Spawn.lua | 86 ++++++++++++++++----- 2 files changed, 158 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 14b99fc76..8ffedba7b 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -37,6 +37,8 @@ -- @field #table Templates Templates: Units, Groups, Statics, ClientsByName, ClientsByID. -- @field #table CLIENTS Clients. -- @field #table STORAGES DCS warehouse storages. +-- @field #table STNS Used Link16 octal numbers for F16/15/18/AWACS planes. +-- @field #table SADL Used Link16 octal numbers for A10/C-II planes. -- @extends Core.Base#BASE --- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator. @@ -93,6 +95,8 @@ DATABASE = { OPSZONES = {}, PATHLINES = {}, STORAGES = {}, + STNS={}, + SADL={}, } local _DATABASECoalition = @@ -1029,10 +1033,23 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category self.Templates.ClientsByName[UnitTemplate.name].CountryID = CountryID self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate end + + if UnitTemplate.AddPropAircraft then + if UnitTemplate.AddPropAircraft.STN_L16 then + local stn = UTILS.OctalToDecimal(UnitTemplate.AddPropAircraft.STN_L16) + self.STNS[stn] = UnitTemplate.name + self:I("Register STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name) + end + if UnitTemplate.AddPropAircraft.SADL_TN then + local sadl = UTILS.OctalToDecimal(UnitTemplate.AddPropAircraft.SADL_TN) + self.SADL[sadl] = UnitTemplate.name + self:I("Register SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name) + end + end UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName end - + -- Debug info. self:T( { Group = self.Templates.Groups[GroupTemplateName].GroupName, Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID, @@ -1043,6 +1060,80 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category ) end +--- Get next (consecutive) free STN as octal number. +-- @param #DATABASE self +-- @param #number octal Starting octal. +-- @param #string unitname Name of the associated unit. +-- @return #number Octal +function DATABASE:GetNextSTN(octal,unitname) + local first = UTILS.OctalToDecimal(octal) + if self.STNS[first] == unitname then return octal end + local nextoctal = 77777 + local found = false + if 32767-first < 10 then + first = 0 + end + for i=first+1,32767 do + if self.STNS[i] == nil then + found = true + nextoctal = UTILS.DecimalToOctal(i) + self.STNS[i] = unitname + self:T("Register STN "..tostring(nextoctal).." for ".. unitname) + break + end + end + if not found then + self:E(string.format("WARNING: No next free STN past %05d found!",octal)) + -- cleanup + local NewSTNS = {} + for _id,_name in pairs(self.STNS) do + if self.UNITS[_name] ~= nil then + NewSTNS[_id] = _name + end + end + self.STNS = nil + self.STNS = NewSTNS + end + return nextoctal +end + +--- Get next (consecutive) free SADL as octal number. +-- @param #DATABASE self +-- @param #number octal Starting octal. +-- @param #string unitname Name of the associated unit. +-- @return #number Octal +function DATABASE:GetNextSADL(octal,unitname) + local first = UTILS.OctalToDecimal(octal) + if self.SADL[first] == unitname then return octal end + local nextoctal = 7777 + local found = false + if 4095-first < 10 then + first = 0 + end + for i=first+1,4095 do + if self.STNS[i] == nil then + found = true + nextoctal = UTILS.DecimalToOctal(i) + self.SADL[i] = unitname + self:T("Register SADL "..tostring(nextoctal).." for ".. unitname) + break + end + end + if not found then + self:E(string.format("WARNING: No next free SADL past %04d found!",octal)) + -- cleanup + local NewSTNS = {} + for _id,_name in pairs(self.SADL) do + if self.UNITS[_name] ~= nil then + NewSTNS[_id] = _name + end + end + self.SADL = nil + self.SADL = NewSTNS + end + return nextoctal +end + --- Get group template. -- @param #DATABASE self -- @param #string GroupName Group name. diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index c34c8ccbd..97d30dfe5 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -780,6 +780,34 @@ function SPAWN:InitSkill( Skill ) return self end +--- [Airplane - F15/16/18/AWACS/B1B/Tanker only] Set the STN Link16 starting number of the Group; each unit of the spawned group will have a consecutive STN set. +-- @param #SPAWN self +-- @param #number Octal The octal number (digits 1..7, max 5 digits, i.e. 77777) to set the STN to. Every STN needs to be unique! +-- @return #SPAWN self +function SPAWN:InitSTN(Octal) + self:F( { Octal = Octal } ) + self.SpawnInitSTN = Octal or 77777 + local num = UTILS.OctalToDecimal(Octal) + if _DATABASE.STNS[num] ~= nil then + self:E("WARNING - STN already assigned: "..tostring(Octal).." is used for ".._DATABASE.STNS[Octal]) + end + return self +end + +--- [Airplane - A10-C II only] Set the SADL TN starting number of the Group; each unit of the spawned group will have a consecutive SADL set. +-- @param #SPAWN self +-- @param #number Octal The octal number (digits 1..7, max 4 digits, i.e. 7777) to set the SADL to. Every SADL needs to be unique! +-- @return #SPAWN self +function SPAWN:InitSADL(Octal) + self:F( { Octal = Octal } ) + self.SpawnInitSADL = Octal or 7777 + local num = UTILS.OctalToDecimal(Octal) + if _DATABASE.SADL[num] ~= nil then + self:E("WARNING - SADL already assigned: "..tostring(Octal).." is used for ".._DATABASE.SADL[Octal]) + end + return self +end + --- Sets the radio communication on or off. Same as checking/unchecking the COMM box in the mission editor. -- @param #SPAWN self -- @param #number switch If true (or nil), enables the radio communication. If false, disables the radio for the spawned group. @@ -3400,30 +3428,48 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft if AddProps then if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then - -- 4 digit octal with leading 0 - if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) ~= nil then - local octal = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 - local decimal = UTILS.OctalToDecimal(octal)+UnitID-1 - SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",UTILS.DecimalToOctal(decimal)) - else -- ED bug - chars in here - local STN = math.floor(UTILS.RandomGaussian(4088/2,nil,1000,4088)) - STN = STN+UnitID-1 - local OSTN = UTILS.DecimalToOctal(STN) - SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",OSTN) + if self.SpawnInitSTN then + local octal = self.SpawnInitSTN + if UnitID > 1 then + octal = _DATABASE:GetNextSTN(self.SpawnInitSTN,SpawnTemplate.units[UnitID].name) + end + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",octal) + else + -- 5 digit octal with leading 0 + if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) ~= nil then + local octal = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 + local num = UTILS.OctalToDecimal(octal) + if _DATABASE.STNS[num] ~= nil or UnitID > 1 then -- STN taken or next unit + octal = _DATABASE:GetNextSTN(octal,SpawnTemplate.units[UnitID].name) + end + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",octal) + else -- ED bug - chars in here + local OSTN = _DATABASE:GetNextSTN(1,SpawnTemplate.units[UnitID].name) + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",OSTN) + end end end -- A10CII if SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN then - -- 3 digit octal with leading 0 - if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then - local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN - local decimal = UTILS.OctalToDecimal(octal)+UnitID-1 - SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",UTILS.DecimalToOctal(decimal)) - else -- ED bug - chars in here - local STN = math.floor(UTILS.RandomGaussian(504/2,nil,100,504)) - STN = STN+UnitID-1 - local OSTN = UTILS.DecimalToOctal(STN) - SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",OSTN) + -- 4 digit octal with leading 0 + if self.SpawnInitSADL then + local octal = self.SpawnInitSADL + if UnitID > 1 then + octal = _DATABASE:GetNextSADL(self.SpawnInitSADL,SpawnTemplate.units[UnitID].name) + end + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%04d",octal) + else + if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then + local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN + local num = UTILS.OctalToDecimal(octal) + if _DATABASE.SADL[num] ~= nil or UnitID > 1 then -- SADL taken or next unit + octal = _DATABASE:GetNextSADL(self.SpawnInitSADL,SpawnTemplate.units[UnitID].name) + end + SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",octal) + else -- ED bug - chars in here + local OSTN = _DATABASE:GetNextSADL(1,SpawnTemplate.units[UnitID].name) + SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",OSTN) + end end end -- VoiceCallsignNumber From fdfc21187cf52c15460ca7ccf9dc7ba4faf5b504 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 22 Feb 2024 11:52:54 +0100 Subject: [PATCH 513/603] sss --- Moose Development/Moose/Core/Spawn.lua | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 705da5929..65f7dba21 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -204,6 +204,17 @@ -- -- *{#SPAWN.InitSTN}(): Set the STN of the first unit in the group. All other units will have consecutive STNs, provided they have not been used yet. -- *{#SPAWN.InitSADL}(): Set the SADL of the first unit in the group. All other units will have consecutive SADLs, provided they have not been used yet. +-- +-- ### Callsigns +-- +-- *{#SPAWN.InitRandomizeCallsign}(): Set a random callsign name per spawn. +-- *{#SPAWN.SpawnInitCallSign}(): Set a specific callsign for a spawned group. +-- +-- ### Speed +-- +-- *{#SPAWN.InitSpeedMps}(): Set the initial speed on spawning in meters per second. +-- *{#SPAWN.InitSpeedKph}(): Set the initial speed on spawning in kilometers per hour. +-- *{#SPAWN.InitSpeedKnots}(): Set the initial speed on spawning in knots. -- -- ## SPAWN **Spawn** methods -- @@ -821,6 +832,46 @@ function SPAWN:InitSADL(Octal) return self end +--- [Airplane] Set the initial speed on spawning in meters per second. Useful when spawning in-air only. +-- @param #SPAWN self +-- @param #number MPS The speed in MPS to use. +-- @return #SPAWN self +function SPAWN:InitSpeedMps(MPS) +self:F( { MPS = MPS } ) + if MPS == nil or tonumber(MPS)<0 then + MPS=125 + end + self.InitSpeed = MPS + return self +end + +--- [Airplane] Set the initial speed on spawning in knots. Useful when spawning in-air only. +-- @param #SPAWN self +-- @param #number Knots The speed in knots to use. +-- @return #SPAWN self +function SPAWN:InitSpeedKnots(Knots) +self:F( { Knots = Knots } ) + if Knots == nil or tonumber(Knots)<0 then + Knots=300 + end + self.InitSpeed = UTILS.KnotsToMps(Knots) + return self +end + +--- [Airplane] Set the initial speed on spawning in kilometers per hour. Useful when spawning in-air only. +-- @param #SPAWN self +-- @param #number KPH The speed in KPH to use. +-- @return #SPAWN self +function SPAWN:InitSpeedKph(KPH) + self:F( { KPH = KPH } ) + if KPH == nil or tonumber(KPH)<0 then + KPH=UTILS.KnotsToKmph(300) + end + self.InitSpeed = UTILS.KmphToMps(KPH) + return self +end + + --- Sets the radio communication on or off. Same as checking/unchecking the COMM box in the mission editor. -- @param #SPAWN self -- @param #number switch If true (or nil), enables the radio communication. If false, disables the radio for the spawned group. @@ -3437,6 +3488,10 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex end end + -- Speed + if self.InitSpeed then + SpawnTemplate.units[UnitID].speed = self.InitSpeed + end -- Link16 local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft if AddProps then From a734cb8a7ec572754520552d0497839c037e1058 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 22 Feb 2024 12:14:03 +0100 Subject: [PATCH 514/603] xxx --- Moose Development/Moose/Core/Database.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 47884fae0..65281c860 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1047,7 +1047,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category if UnitTemplate.AddPropAircraft.SADL_TN then local sadl = UTILS.OctalToDecimal(UnitTemplate.AddPropAircraft.SADL_TN) if sadl == nil or sadl < 1 then - self:E("WARNING: Invalid SADL "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name) + self:E("WARNING: Invalid SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name) else self.SADL[sadl] = UnitTemplate.name self:I("Register SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 65f7dba21..68e3a10a8 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3525,7 +3525,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 if UnitID > 1 then octal = _DATABASE:GetNextSADL(self.SpawnInitSADL,SpawnTemplate.units[UnitID].name) end - SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%04d",octal) + SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",octal) else if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN From b9521617946881ae3f12b1a6d6c5f54d093b277d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 22 Feb 2024 17:28:09 +0100 Subject: [PATCH 515/603] warning --- Moose Development/Moose/Ops/Awacs.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index c3cfc95d8..b7bbb1990 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1384,7 +1384,7 @@ end -- Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- [User] Set the tactical information option, create 10 radio channels groups can subscribe and get Bogey Dope on a specific frequency automatically. +--- [User] Set the tactical information option, create 10 radio channels groups can subscribe and get Bogey Dope on a specific frequency automatically. You **need** to set up SRS first before using this! -- @param #AWACS self -- @param #number BaseFreq Base Frequency to use, defaults to 130. -- @param #number Increase Increase to use, defaults to 0.5, thus channels created are 130, 130.5, 131 .. etc. @@ -1394,6 +1394,10 @@ end -- @return #AWACS self function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number) self:T(self.lid.."SetTacticalRadios") + if not self.AwacsSRS then + MESSAGE:New("AWACS: Setup SRS in your code BEFORE trying to add tac radios please!",30,"ERROR",true):ToLog():ToAll() + return self + end self.TacticalMenu = true self.TacticalBaseFreq = BaseFreq or 130 self.TacticalIncrFreq = Increase or 0.5 From 6e12e8f9b90b44657594e6ffe37c72a41cd0049b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 23 Feb 2024 11:15:29 +0100 Subject: [PATCH 516/603] #AIRBASE - New Syria airbases added --- Moose Development/Moose/Wrapper/Airbase.lua | 226 ++++++++++---------- 1 file changed, 116 insertions(+), 110 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 2799d17e3..490edcba7 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -427,132 +427,138 @@ AIRBASE.TheChannel = { --- Airbases of the Syria map: -- -- * AIRBASE.Syria.Kuweires --- * AIRBASE.Syria.Marj_Ruhayyil --- * AIRBASE.Syria.Kiryat_Shmona --- * AIRBASE.Syria.Marj_as_Sultan_North --- * AIRBASE.Syria.Eyn_Shemer -- * AIRBASE.Syria.Incirlik --- * AIRBASE.Syria.Damascus --- * AIRBASE.Syria.Bassel_Al_Assad --- * AIRBASE.Syria.Rosh_Pina +-- * AIRBASE.Syria.King_Abdullah_II +-- * AIRBASE.Syria.Akrotiri -- * AIRBASE.Syria.Aleppo --- * AIRBASE.Syria.Al_Qusayr --- * AIRBASE.Syria.Wujah_Al_Hajar --- * AIRBASE.Syria.Al_Dumayr --- * AIRBASE.Syria.Gazipasa +-- * AIRBASE.Syria.Abu_al_Duhur -- * AIRBASE.Syria.Hatay --- * AIRBASE.Syria.Nicosia [Deactivated by ED as of June/2023] --- * AIRBASE.Syria.Pinarbashi -- * AIRBASE.Syria.Paphos +-- * AIRBASE.Syria.At_Tanf +-- * AIRBASE.Syria.Tal_Siman +-- * AIRBASE.Syria.Rayak +-- * AIRBASE.Syria.Muwaffaq_Salti +-- * AIRBASE.Syria.Naqoura +-- * AIRBASE.Syria.Gaziantep +-- * AIRBASE.Syria.Al_Qusayr +-- * AIRBASE.Syria.Al_Dumayr -- * AIRBASE.Syria.Kingsfield --- * AIRBASE.Syria.Thalah +-- * AIRBASE.Syria.Marj_as_Sultan_North +-- * AIRBASE.Syria.Beirut_Rafic_Hariri +-- * AIRBASE.Syria.Palmyra +-- * AIRBASE.Syria.Hama +-- * AIRBASE.Syria.Eyn_Shemer +-- * AIRBASE.Syria.Sanliurfa +-- * AIRBASE.Syria.Amman +-- * AIRBASE.Syria.Deir_ez_Zor +-- * AIRBASE.Syria.Taftanaz +-- * AIRBASE.Syria.Damascus +-- * AIRBASE.Syria.Gazipasa +-- * AIRBASE.Syria.Herzliya +-- * AIRBASE.Syria.H4 +-- * AIRBASE.Syria.Tiyas +-- * AIRBASE.Syria.Lakatamia +-- * AIRBASE.Syria.Kharab_Ishk -- * AIRBASE.Syria.Haifa -- * AIRBASE.Syria.Khalkhalah -- * AIRBASE.Syria.Megiddo --- * AIRBASE.Syria.Lakatamia --- * AIRBASE.Syria.Rayak --- * AIRBASE.Syria.Larnaca +-- * AIRBASE.Syria.An_Nasiriyah +-- * AIRBASE.Syria.Bassel_Al_Assad +-- * AIRBASE.Syria.Ruwayshid -- * AIRBASE.Syria.Mezzeh -- * AIRBASE.Syria.Gecitkale --- * AIRBASE.Syria.Akrotiri --- * AIRBASE.Syria.Naqoura --- * AIRBASE.Syria.Gaziantep --- * AIRBASE.Syria.Sayqal --- * AIRBASE.Syria.Tiyas --- * AIRBASE.Syria.Shayrat --- * AIRBASE.Syria.Taftanaz --- * AIRBASE.Syria.H4 --- * AIRBASE.Syria.King_Hussein_Air_College --- * AIRBASE.Syria.Rene_Mouawad --- * AIRBASE.Syria.Jirah +-- * AIRBASE.Syria.Nicosia -- * AIRBASE.Syria.Ramat_David --- * AIRBASE.Syria.Qabr_as_Sitt --- * AIRBASE.Syria.Minakh --- * AIRBASE.Syria.Adana_Sakirpasa --- * AIRBASE.Syria.Palmyra --- * AIRBASE.Syria.Hama --- * AIRBASE.Syria.Ercan --- * AIRBASE.Syria.Marj_as_Sultan_South --- * AIRBASE.Syria.Tabqa --- * AIRBASE.Syria.Beirut_Rafic_Hariri --- * AIRBASE.Syria.An_Nasiriyah --- * AIRBASE.Syria.Abu_al_Duhur --- * AIRBASE.Syria.At_Tanf --- * AIRBASE.Syria.H3 +-- * AIRBASE.Syria.Tha_lah -- * AIRBASE.Syria.H3_Northwest +-- * AIRBASE.Syria.Sayqal +-- * AIRBASE.Syria.Jirah +-- * AIRBASE.Syria.Shayrat +-- * AIRBASE.Syria.Adana_Sakirpasa +-- * AIRBASE.Syria.Wujah_Al_Hajar +-- * AIRBASE.Syria.Pinarbashi -- * AIRBASE.Syria.H3_Southwest --- * AIRBASE.Syria.Kharab_Ishk --- * AIRBASE.Syria.Raj_al_Issa_East (deleted by ED) --- * AIRBASE.Syria.Raj_al_Issa_West (deleted by ED) --- * AIRBASE.Syria.Ruwayshid --- * AIRBASE.Syria.Sanliurfa --- * AIRBASE.Syria.Tal_Siman --- * AIRBASE.Syria.Deir_ez_Zor +-- * AIRBASE.Syria.Rosh_Pina +-- * AIRBASE.Syria.Kiryat_Shmona +-- * AIRBASE.Syria.H3 +-- * AIRBASE.Syria.Qabr_as_Sitt +-- * AIRBASE.Syria.Prince_Hassan +-- * AIRBASE.Syria.Larnaca +-- * AIRBASE.Syria.King_Hussein_Air_College +-- * AIRBASE.Syria.Ercan +-- * AIRBASE.Syria.Marj_Ruhayyil +-- * AIRBASE.Syria.Tabqa +-- * AIRBASE.Syria.Marj_as_Sultan_South +-- * AIRBASE.Syria.Rene_Mouawad +-- * AIRBASE.Syria.Minakh -- --@field Syria AIRBASE.Syria={ - ["Kuweires"]="Kuweires", - ["Marj_Ruhayyil"]="Marj Ruhayyil", - ["Kiryat_Shmona"]="Kiryat Shmona", - ["Marj_as_Sultan_North"]="Marj as Sultan North", - ["Eyn_Shemer"]="Eyn Shemer", - ["Incirlik"]="Incirlik", - ["Damascus"]="Damascus", - ["Bassel_Al_Assad"]="Bassel Al-Assad", - ["Rosh_Pina"]="Rosh Pina", - ["Aleppo"]="Aleppo", - ["Al_Qusayr"]="Al Qusayr", - ["Wujah_Al_Hajar"]="Wujah Al Hajar", - ["Al_Dumayr"]="Al-Dumayr", - ["Gazipasa"]="Gazipasa", - ["Hatay"]="Hatay", - --["Nicosia"]="Nicosia", - ["Pinarbashi"]="Pinarbashi", - ["Paphos"]="Paphos", - ["Kingsfield"]="Kingsfield", - ["Thalah"]="Tha'lah", - ["Haifa"]="Haifa", - ["Khalkhalah"]="Khalkhalah", - ["Megiddo"]="Megiddo", - ["Lakatamia"]="Lakatamia", - ["Rayak"]="Rayak", - ["Larnaca"]="Larnaca", - ["Mezzeh"]="Mezzeh", - ["Gecitkale"]="Gecitkale", - ["Akrotiri"]="Akrotiri", - ["Naqoura"]="Naqoura", - ["Gaziantep"]="Gaziantep", - ["Sayqal"]="Sayqal", - ["Tiyas"]="Tiyas", - ["Shayrat"]="Shayrat", - ["Taftanaz"]="Taftanaz", - ["H4"]="H4", - ["King_Hussein_Air_College"]="King Hussein Air College", - ["Rene_Mouawad"]="Rene Mouawad", - ["Jirah"]="Jirah", - ["Ramat_David"]="Ramat David", - ["Qabr_as_Sitt"]="Qabr as Sitt", - ["Minakh"]="Minakh", - ["Adana_Sakirpasa"]="Adana Sakirpasa", - ["Palmyra"]="Palmyra", - ["Hama"]="Hama", - ["Ercan"]="Ercan", - ["Marj_as_Sultan_South"]="Marj as Sultan South", - ["Tabqa"]="Tabqa", - ["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri", - ["An_Nasiriyah"]="An Nasiriyah", - ["Abu_al_Duhur"]="Abu al-Duhur", - ["At_Tanf"]="At Tanf", - ["H3"]="H3", - ["H3_Northwest"]="H3 Northwest", - ["H3_Southwest"]="H3 Southwest", - ["Kharab_Ishk"]="Kharab Ishk", - -- ["Raj_al_Issa_East"]="Raj al Issa East", - -- ["Raj_al_Issa_West"]="Raj al Issa West", - ["Ruwayshid"]="Ruwayshid", - ["Sanliurfa"]="Sanliurfa", - ["Tal_Siman"]="Tal Siman", - ["Deir_ez_Zor"] = "Deir ez-Zor", + ["Kuweires"] = "Kuweires", + ["Incirlik"] = "Incirlik", + ["King_Abdullah_II"] = "King Abdullah II", + ["Akrotiri"] = "Akrotiri", + ["Aleppo"] = "Aleppo", + ["Abu_al_Duhur"] = "Abu al-Duhur", + ["Hatay"] = "Hatay", + ["Paphos"] = "Paphos", + ["At_Tanf"] = "At Tanf", + ["Tal_Siman"] = "Tal Siman", + ["Rayak"] = "Rayak", + ["Muwaffaq_Salti"] = "Muwaffaq Salti", + ["Naqoura"] = "Naqoura", + ["Gaziantep"] = "Gaziantep", + ["Al_Qusayr"] = "Al Qusayr", + ["Al_Dumayr"] = "Al-Dumayr", + ["Kingsfield"] = "Kingsfield", + ["Marj_as_Sultan_North"] = "Marj as Sultan North", + ["Beirut_Rafic_Hariri"] = "Beirut-Rafic Hariri", + ["Palmyra"] = "Palmyra", + ["Hama"] = "Hama", + ["Eyn_Shemer"] = "Eyn Shemer", + ["Sanliurfa"] = "Sanliurfa", + ["Amman"] = "Amman", + ["Deir_ez_Zor"] = "Deir ez-Zor", + ["Taftanaz"] = "Taftanaz", + ["Damascus"] = "Damascus", + ["Gazipasa"] = "Gazipasa", + ["Herzliya"] = "Herzliya", + ["H4"] = "H4", + ["Tiyas"] = "Tiyas", + ["Lakatamia"] = "Lakatamia", + ["Kharab_Ishk"] = "Kharab Ishk", + ["Haifa"] = "Haifa", + ["Khalkhalah"] = "Khalkhalah", + ["Megiddo"] = "Megiddo", + ["An_Nasiriyah"] = "An Nasiriyah", + ["Bassel_Al_Assad"] = "Bassel Al-Assad", + ["Ruwayshid"] = "Ruwayshid", + ["Mezzeh"] = "Mezzeh", + ["Gecitkale"] = "Gecitkale", + ["Nicosia"] = "Nicosia", + ["Ramat_David"] = "Ramat David", + ["Tha_lah"] = "Tha'lah", + ["H3_Northwest"] = "H3 Northwest", + ["Sayqal"] = "Sayqal", + ["Jirah"] = "Jirah", + ["Shayrat"] = "Shayrat", + ["Adana_Sakirpasa"] = "Adana Sakirpasa", + ["Wujah_Al_Hajar"] = "Wujah Al Hajar", + ["Pinarbashi"] = "Pinarbashi", + ["H3_Southwest"] = "H3 Southwest", + ["Rosh_Pina"] = "Rosh Pina", + ["Kiryat_Shmona"] = "Kiryat Shmona", + ["H3"] = "H3", + ["Qabr_as_Sitt"] = "Qabr as Sitt", + ["Prince_Hassan"] = "Prince Hassan", + ["Larnaca"] = "Larnaca", + ["King_Hussein_Air_College"] = "King Hussein Air College", + ["Ercan"] = "Ercan", + ["Marj_Ruhayyil"] = "Marj Ruhayyil", + ["Tabqa"] = "Tabqa", + ["Marj_as_Sultan_South"] = "Marj as Sultan South", + ["Rene_Mouawad"] = "Rene Mouawad", + ["Minakh"] = "Minakh", } --- Airbases of the Mariana Islands map: From d291e7417dbb51044e0397a186ab736d670d176d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 27 Feb 2024 10:19:48 +0100 Subject: [PATCH 517/603] CTLD --- Moose Development/Moose/Core/Set.lua | 4 +- Moose Development/Moose/Ops/CTLD.lua | 66 ++++++++++++++++++---------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 0045ee7fa..212bfc434 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2997,7 +2997,7 @@ do -- SET_UNIT local velocity = self:GetVelocity() or 0 Coordinate:SetHeading( heading ) Coordinate:SetVelocity( velocity ) - self:I(UTILS.PrintTableToLog(Coordinate)) + self:T(UTILS.PrintTableToLog(Coordinate)) end return Coordinate @@ -4521,7 +4521,7 @@ do -- SET_CLIENT if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup and Event.IniGroup:IsGround() then -- CA Slot entered local ObjectName, Object = self:AddInDatabase( Event ) - self:I( ObjectName, UTILS.PrintTableToLog(Object) ) + self:T( ObjectName, UTILS.PrintTableToLog(Object) ) if Object and self:IsIncludeObject( Object ) then self:Add( ObjectName, Object ) end diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 2176dc5d7..9f5b54d16 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -24,7 +24,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update December 2023 +-- Last Update February 2024 do @@ -44,6 +44,7 @@ do -- @field #number PerCrateMass Mass in kg. -- @field #number Stock Number of builds available, -1 for unlimited. -- @field #string Subcategory Sub-category name. +-- @field #boolean DontShowInMenu Show this item in menu or not. -- @extends Core.Base#BASE --- @@ -62,6 +63,7 @@ CTLD_CARGO = { PerCrateMass = 0, Stock = nil, Mark = nil, + DontShowInMenu = false, } --- Define cargo types. @@ -97,8 +99,9 @@ CTLD_CARGO = { -- @param #number PerCrateMass Mass in kg -- @param #number Stock Number of builds available, nil for unlimited -- @param #string Subcategory Name of subcategory, handy if using > 10 types to load. + -- @param #boolean DontShowInMenu Show this item in menu or not (default: false == show it). -- @return #CTLD_CARGO self - function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory) + function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory,DontShowInMenu) -- Inherit everything from BASE class. local self=BASE:Inherit(self, BASE:New()) -- #CTLD_CARGO self:T({ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped}) @@ -115,6 +118,7 @@ CTLD_CARGO = { self.Stock = Stock or nil --#number self.Mark = nil self.Subcategory = Subcategory or "Other" + self.DontShowInMenu = DontShowInMenu or false return self end @@ -1228,7 +1232,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.45" +CTLD.version="1.0.46" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3024,7 +3028,7 @@ end function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template) local Positions = {} local template = _DATABASE:GetGroupTemplate(Template) - UTILS.PrintTableToLog(template) + --UTILS.PrintTableToLog(template) local numbertroops = #template.units local newcenter = Coordinate:Translate(Radius,((Heading+270)%360)) for i=1,360,math.floor(360/numbertroops) do @@ -3038,7 +3042,7 @@ function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template) } table.insert(Positions,p1t) end - UTILS.PrintTableToLog(Positions) + --UTILS.PrintTableToLog(Positions) return Positions end @@ -3700,14 +3704,20 @@ function CTLD:_RefreshF10Menus() for _,_entry in pairs(self.Cargo_Troops) do local entry = _entry -- #CTLD_CARGO local subcat = entry.Subcategory - menucount = menucount + 1 - menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,subcatmenus[subcat],self._LoadTroops, self, _group, _unit, entry) + local noshow = entry.DontShowInMenu + if not noshow then + menucount = menucount + 1 + menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,subcatmenus[subcat],self._LoadTroops, self, _group, _unit, entry) + end end else for _,_entry in pairs(self.Cargo_Troops) do local entry = _entry -- #CTLD_CARGO - menucount = menucount + 1 - menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry) + local noshow = entry.DontShowInMenu + if not noshow then + menucount = menucount + 1 + menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry) + end end end local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh() @@ -3728,33 +3738,45 @@ function CTLD:_RefreshF10Menus() for _,_entry in pairs(self.Cargo_Crates) do local entry = _entry -- #CTLD_CARGO local subcat = entry.Subcategory - menucount = menucount + 1 - local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) - menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry) + local noshow = entry.DontShowInMenu + if not noshow then + menucount = menucount + 1 + local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry) + end end for _,_entry in pairs(self.Cargo_Statics) do local entry = _entry -- #CTLD_CARGO local subcat = entry.Subcategory - menucount = menucount + 1 - local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) - menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry) + local noshow = entry.DontShowInMenu + if not noshow then + menucount = menucount + 1 + local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry) + end end else for _,_entry in pairs(self.Cargo_Crates) do local entry = _entry -- #CTLD_CARGO - menucount = menucount + 1 - local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) - menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) + local noshow = entry.DontShowInMenu + if not noshow then + menucount = menucount + 1 + local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) + end end for _,_entry in pairs(self.Cargo_Statics) do local entry = _entry -- #CTLD_CARGO - menucount = menucount + 1 - local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) - menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) + local noshow = entry.DontShowInMenu + if not noshow then + menucount = menucount + 1 + local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) + end end end listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit) - removecrates = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu, self._RemoveCratesNearby, self, _group, _unit) + local removecrates = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu, self._RemoveCratesNearby, self, _group, _unit) local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit) if not self.nobuildmenu then local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit) From 1e954de951c17beb5b2bde65a7e2d58dfa03cf83 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 27 Feb 2024 18:13:41 +0100 Subject: [PATCH 518/603] #BRIGADE Fixes to save/loadback assets for persistence --- Moose Development/Moose/Ops/Brigade.lua | 6 +++--- Moose Development/Moose/Ops/Legion.lua | 4 ++-- Moose Development/Moose/Utilities/Utils.lua | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/Brigade.lua b/Moose Development/Moose/Ops/Brigade.lua index 3242dc389..76e442df3 100644 --- a/Moose Development/Moose/Ops/Brigade.lua +++ b/Moose Development/Moose/Ops/Brigade.lua @@ -313,8 +313,8 @@ end -- -- local Path = FilePath or "C:\\Users\\\\Saved Games\\DCS\\Missions\\" -- example path -- local BlueOpsFilename = BlueFileName or "ExamplePlatoonSave.csv" -- example filename --- local BlueSaveOps = SET_GROUP:New():FilterCoalitions("blue"):FilterPrefixes("AID"):FilterCategoryGround():FilterOnce() --- UTILS.SaveSetOfGroups(BlueSaveOps,Path,BlueOpsFilename) +-- local BlueSaveOps = SET_OPSGROUP:New():FilterCoalitions("blue"):FilterCategoryGround():FilterOnce() +-- UTILS.SaveSetOfOpsGroups(BlueSaveOps,Path,BlueOpsFilename) -- -- where Path and Filename are strings, as chosen by you. -- You can then load back the assets at the start of your next mission run. Be aware that it takes a couple of seconds for the @@ -324,7 +324,7 @@ end -- local Path = FilePath or "C:\\Users\\\\Saved Games\\DCS\\Missions\\" -- example path -- local BlueOpsFilename = BlueFileName or "ExamplePlatoonSave.csv" -- example filename -- if UTILS.CheckFileExists(Path,BlueOpsFilename) then --- local loadback = UTILS.LoadSetOfGroups(Path,BlueOpsFilename,false) +-- local loadback = UTILS.LoadSetOfOpsGroups(Path,BlueOpsFilename,false) -- for _,_platoondata in pairs (loadback) do -- local groupname = _platoondata.groupname -- #string -- local coordinate = _platoondata.coordinate -- Core.Point#COORDINATE diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 9c5861800..69c620dd1 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -3190,14 +3190,14 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu elseif (currmission.type==AUFTRAG.Type.ONGUARD or currmission.type==AUFTRAG.Type.PATROLZONE) and (MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK) then score=score+25 elseif currmission.type==AUFTRAG.Type.NOTHING then - score=score+25 + score=score+30 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. + -- We better take a fresh asset. Sometimes spawned assets do something else, which is difficult to check. score=score-10 else -- Combat mission. diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 6005dcbce..7b717b668 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -2644,6 +2644,9 @@ function UTILS.SaveSetOfGroups(Set,Path,Filename,Structured) if group and group:IsAlive() then local name = group:GetName() local template = string.gsub(name,"-(.+)$","") + if string.find(name,"AID") then + template = string.gsub(name,"(.AID.%d+$","") + end if string.find(template,"#") then template = string.gsub(name,"#(%d+)$","") end From d9cfaaaa0d5d651bf6a904a5ed1cc499a45d96d9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 29 Feb 2024 09:53:46 +0100 Subject: [PATCH 519/603] SCORING --- .../Moose/Functional/Scoring.lua | 42 +++++++++++++++---- Moose Development/Moose/Wrapper/Unit.lua | 2 + 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index b8c7218cf..4682628a4 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -78,7 +78,8 @@ -- ### Authors: **FlightControl** -- -- ### Contributions: --- +-- +-- * **Applevangelist**: Additional functionality, fixes. -- * **Wingthor (TAW)**: Testing & Advice. -- * **Dutch-Baron (TAW)**: Testing & Advice. -- * **Whisper**: Testing and Advice. @@ -116,11 +117,13 @@ -- Special targets can be set that will give extra scores to the players when these are destroyed. -- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Wrapper.Unit}s. -- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Wrapper.Static}s. --- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Wrapper.Group}s. +-- Use the method @{#SCORING.AddScoreSetGroup}() to specify a special additional score for a specific @{Wrapper.Group}s gathered in a @{Core.Set#SET_GROUP}. -- -- local Scoring = SCORING:New( "Scoring File" ) -- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) -- Scoring:AddStaticScore( STATIC:FindByName( "Static #1" ), 100 ) +-- local GroupSet = SET_GROUP:New():FilterPrefixes("RAT"):FilterStart() +-- Scoring:AddScoreSetGroup( GroupSet, 100) -- -- The above grants an additional score of 200 points for Unit #001 and an additional 100 points of Static #1 if these are destroyed. -- Note that later in the mission, one can remove these scores set, for example, when the a goal achievement time limit is over. @@ -226,7 +229,7 @@ SCORING = { ClassID = 0, Players = {}, AutoSave = true, - version = "1.17.1" + version = "1.18.1" } local _SCORINGCoalition = { @@ -428,6 +431,31 @@ function SCORING:AddScoreGroup( ScoreGroup, Score ) return self end +--- Specify a special additional score for a @{Core.Set#SET_GROUP}. +-- @param #SCORING self +-- @param Core.Set#SET_GROUP Set The @{Core.Set#SET_GROUP} for which each @{Wrapper.Unit} in each Group a Score is given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddScoreSetGroup(Set, Score) + local set = Set:GetSetObjects() + + for _,_group in pairs (set) do + if _group and _group:IsAlive() then + self:AddScoreGroup(_group,Score) + end + end + + local function AddScore(group) + self:AddScoreGroup(group,Score) + end + + function Set:OnAfterAdded(From,Event,To,ObjectName,Object) + AddScore(Object) + end + + return self +end + --- Add a @{Core.Zone} to define additional scoring when any object is destroyed in that zone. -- Note that if a @{Core.Zone} with the same name is already within the scoring added, the @{Core.Zone} (type) and Score will be replaced! -- This allows for a dynamic destruction zone evolution within your mission. @@ -1935,9 +1963,9 @@ end --- Handles the event when one player kill another player -- @param #SCORING self --- @param #PLAYER Player the ataching player +-- @param #Wrapper.Client#CLIENT Player the atacking player -- @param #string TargetPlayerName the name of the killed player --- @param #bool IsTeamKill true if this kill was a team kill +-- @param #boolean IsTeamKill true if this kill was a team kill -- @param #number TargetThreatLevel Thread level of the target -- @param #number PlayerThreatLevelThread level of the player -- @param #number Score The score based on both threat levels @@ -1946,9 +1974,9 @@ function SCORING:OnKillPvP(Player, TargetPlayerName, IsTeamKill, TargetThreatLev end --- Handles the event when one player kill another player -- @param #SCORING self --- @param #PLAYER Player the ataching player +-- @param #Wrapper.Client#CLIENT Player the atacking player -- @param #string TargetUnitName the name of the killed unit --- @param #bool IsTeamKill true if this kill was a team kill +-- @param #boolean IsTeamKill true if this kill was a team kill -- @param #number TargetThreatLevel Thread level of the target -- @param #number PlayerThreatLevelThread level of the player -- @param #number Score The score based on both threat levels diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 322d23540..781c71d48 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -1244,7 +1244,9 @@ function UNIT:GetThreatLevel() if Attributes["Fighters"] then ThreatLevel = 10 elseif Attributes["Multirole fighters"] then ThreatLevel = 9 + elseif Attributes["Interceptors"] then ThreatLevel = 9 elseif Attributes["Battleplanes"] then ThreatLevel = 8 + elseif Attributes["Battle airplanes"] then ThreatLevel = 8 elseif Attributes["Attack helicopters"] then ThreatLevel = 7 elseif Attributes["Strategic bombers"] then ThreatLevel = 6 elseif Attributes["Bombers"] then ThreatLevel = 5 From b34597d73290fe86d960eb254412d94ab5394c1b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 2 Mar 2024 14:25:56 +0100 Subject: [PATCH 520/603] xxx --- .../Moose/Functional/Scoring.lua | 4 ++-- Moose Development/Moose/Ops/Awacs.lua | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 5a8e6d87f..15d23ba84 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -1354,14 +1354,14 @@ function SCORING:_EventOnDeadOrCrash( Event ) else Player.PlayerKills = 1 end - self:OnKillPvP(Player, TargetPlayerName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore) + self:OnKillPvP(PlayerName, TargetPlayerName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore) MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. "Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty, MESSAGE.Type.Information ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) else - self:OnKillPvE(Player, TargetUnitName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore) + self:OnKillPvE(PlayerName, TargetUnitName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore) MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. "Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty, MESSAGE.Type.Information ) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index b7bbb1990..1fd51026b 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -508,7 +508,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.62", -- #string + version = "0.2.63", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -1411,7 +1411,7 @@ function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number) self.TacticalFrequencies[freq] = freq end if self.AwacsSRS then - self.TacticalSRS = MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation) + self.TacticalSRS = MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation,self.Backend) self.TacticalSRS:SetCoalition(self.coalition) self.TacticalSRS:SetGender(self.Gender) self.TacticalSRS:SetCulture(self.Culture) @@ -2089,8 +2089,9 @@ end -- @param #number Volume Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS; if you use a config file for MSRS, hand in nil here. -- @param #string AccessKey (Optional) Your Google API access key. This is necessary if DCS-gRPC is used as backend; if you use a config file for MSRS, hand in nil here. +-- @param #string Backend (Optional) Your MSRS Backend if different from your config file settings, e.g. MSRS.Backend.SRSEXE or MSRS.Backend.GRPC -- @return #AWACS self -function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey) +function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Backend) self:T(self.lid.."SetSRS") self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" self.Gender = Gender or MSRS.gender or "male" @@ -2100,8 +2101,9 @@ function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey self.PathToGoogleKey = PathToGoogleKey self.AccessKey = AccessKey self.Volume = Volume or 1.0 - - self.AwacsSRS = MSRS:New(self.PathToSRS,self.MultiFrequency,self.MultiModulation) + self.Backend = Backend or MSRS.backend + BASE:I({backend = self.Backend}) + self.AwacsSRS = MSRS:New(self.PathToSRS,self.MultiFrequency,self.MultiModulation,self.Backend) self.AwacsSRS:SetCoalition(self.coalition) self.AwacsSRS:SetGender(self.Gender) self.AwacsSRS:SetCulture(self.Culture) @@ -3154,9 +3156,13 @@ end function AWACS:_ShowAwacsInfo(Group) self:T(self.lid.."_ShowAwacsInfo") local report = REPORT:New("Info") + local STN = self.STN report:Add("====================") report:Add(string.format("AWACS %s",self.callsigntxt)) report:Add(string.format("Radio: %.3f %s",self.Frequency,UTILS.GetModulationName(self.Modulation))) + if STN then + report:Add(string.format("Link-16 STN: %s",STN)) + end report:Add(string.format("Bulls Alias: %s",self.AOName)) report:Add(string.format("Coordinate: %s",self.AOCoordinate:ToStringLLDDM())) report:Add("====================") @@ -5993,6 +5999,10 @@ function AWACS:_CheckAwacsStatus() local awacs = nil -- Wrapper.Group#GROUP if self.AwacsFG then awacs = self.AwacsFG:GetGroup() -- Wrapper.Group#GROUP + local unit = awacs:GetUnit(1) + if unit then + self.STN = tostring(unit:GetSTN()) + end end local monitoringdata = self.MonitoringData -- #AWACS.MonitoringData @@ -6672,7 +6682,7 @@ function AWACS:onafterCheckTacticalQueue(From,Event,To) end -- end while - if self:Is("Running") then + if not self:Is("Stopped") then self:__CheckTacticalQueue(-self.TacticalInterval) end return self From b3f799126944e818b1fde55dba56dc89f2d61de7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 5 Mar 2024 10:34:27 +0100 Subject: [PATCH 521/603] xxx --- Moose Development/Moose/Ops/CTLD.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 9f5b54d16..9124f3c94 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1232,7 +1232,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.46" +CTLD.version="1.0.47" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3030,7 +3030,8 @@ function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template) local template = _DATABASE:GetGroupTemplate(Template) --UTILS.PrintTableToLog(template) local numbertroops = #template.units - local newcenter = Coordinate:Translate(Radius,((Heading+270)%360)) + local slightshift = math.abs(math.random(0,200)/100) + local newcenter = Coordinate:Translate(Radius+slightshift,((Heading+270)%360)) for i=1,360,math.floor(360/numbertroops) do local phead = ((Heading+270+i)%360) local post = newcenter:Translate(Radius,phead) From a51329d3e79d882b0488952b9411e1f05ad1a086 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 5 Mar 2024 16:39:08 +0100 Subject: [PATCH 522/603] xxx --- Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua | 2 +- Moose Development/Moose/Cargo/CargoGroup.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua index 653d3a64f..6fc670e40 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua @@ -160,7 +160,7 @@ AI_CARGO_DISPATCHER_SHIP = { -- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart() -- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- NEED MORE THOUGHT - ShippingLane is part of Warehouse....... --- local ShippingLane = GROUP:New():FilterPrefixes( "ShippingLane" ):FilterStart() +-- local ShippingLane = SET_GROUP:New():FilterPrefixes( "ShippingLane" ):FilterOnce():GetSetObjects() -- -- AICargoDispatcherShip = AI_CARGO_DISPATCHER_SHIP:New( SetShip, SetCargoInfantry, SetPickupZones, SetDeployZones, ShippingLane ) -- AICargoDispatcherShip:Start() diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index ca6a96a69..0af776c05 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -598,7 +598,7 @@ do -- CARGO_GROUP end - --- Get the amount of cargo units in the group. + --- Get the underlying GROUP object from the CARGO_GROUP. -- @param #CARGO_GROUP self -- @return #CARGO_GROUP function CARGO_GROUP:GetGroup( Cargo ) From d7fd9d09687fdd8c474859dbbca97f958cd75ebc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Mar 2024 10:20:02 +0100 Subject: [PATCH 523/603] xxx --- Moose Development/Moose/Wrapper/Controllable.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index a7c4e235a..8ede41e95 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -3871,6 +3871,10 @@ end -- @param #CONTROLLABLE self -- @param #table WayPoints If WayPoints is given, then use the route. -- @return #CONTROLLABLE self +-- @usage Intended Workflow is: +-- mygroup:WayPointInitialize() +-- mygroup:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... ) +-- mygroup:WayPointExecute() function CONTROLLABLE:WayPointInitialize( WayPoints ) self:F( { WayPoints } ) @@ -3902,9 +3906,15 @@ end -- @param #number WayPointIndex When defining multiple WayPoint functions for one WayPoint, use WayPointIndex to set the sequence of actions. -- @param #function WayPointFunction The waypoint function to be called when the controllable moves over the waypoint. The waypoint function takes variable parameters. -- @return #CONTROLLABLE self +-- @usage Intended Workflow is: +-- mygroup:WayPointInitialize() +-- mygroup:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... ) +-- mygroup:WayPointExecute() function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... ) self:F2( { WayPoint, WayPointIndex, WayPointFunction } ) - + if not self.WayPoints then + self:WayPointInitialize() + end table.insert( self.WayPoints[WayPoint].task.params.tasks, WayPointIndex ) self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPointFunction, arg ) return self @@ -3917,6 +3927,10 @@ end -- @param #number WayPoint The WayPoint from where to execute the mission. -- @param #number WaitTime The amount seconds to wait before initiating the mission. -- @return #CONTROLLABLE self +-- @usage Intended Workflow is: +-- mygroup:WayPointInitialize() +-- mygroup:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... ) +-- mygroup:WayPointExecute() function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime ) self:F( { WayPoint, WaitTime } ) From fc5d540ec6a4ce93724c524cdf70d82993326b1a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Mar 2024 16:18:47 +0100 Subject: [PATCH 524/603] #MARKEROPS * Coalition in events fix --- Moose Development/Moose/Core/Event.lua | 1 + Moose Development/Moose/Core/MarkerOps_Base.lua | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index d3a105631..5908c032b 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -1378,6 +1378,7 @@ function EVENT:onEvent( Event ) Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos) Event.MarkText=Event.text Event.MarkCoalition=Event.coalition + Event.IniCoalition=Event.coalition Event.MarkGroupID = Event.groupID end diff --git a/Moose Development/Moose/Core/MarkerOps_Base.lua b/Moose Development/Moose/Core/MarkerOps_Base.lua index 664ad3c03..3bc950416 100644 --- a/Moose Development/Moose/Core/MarkerOps_Base.lua +++ b/Moose Development/Moose/Core/MarkerOps_Base.lua @@ -50,7 +50,7 @@ MARKEROPS_BASE = { ClassName = "MARKEROPS", Tag = "mytag", Keywords = {}, - version = "0.1.2", + version = "0.1.3", debug = false, Casesensitive = true, } @@ -158,10 +158,10 @@ function MARKEROPS_BASE:OnEventMark(Event) local text = tostring(Event.text) local m = MESSAGE:New(string.format("Mark added at %s with text: %s",coordtext,text),10,"Info",false):ToAll() end - local coalition = Event.IniCoalition + local coalition = Event.MarkCoalition -- decision if Event.id==world.event.S_EVENT_MARK_ADDED then - self:T({event="S_EVENT_MARK_ADDED", carrier=self.groupname, vec3=Event.pos}) + self:T({event="S_EVENT_MARK_ADDED", carrier=Event.IniGroupName, vec3=Event.pos}) -- Handle event local Eventtext = tostring(Event.text) if Eventtext~=nil then @@ -171,7 +171,7 @@ function MARKEROPS_BASE:OnEventMark(Event) end end elseif Event.id==world.event.S_EVENT_MARK_CHANGE then - self:T({event="S_EVENT_MARK_CHANGE", carrier=self.groupname, vec3=Event.pos}) + self:T({event="S_EVENT_MARK_CHANGE", carrier=Event.IniGroupName, vec3=Event.pos}) -- Handle event. local Eventtext = tostring(Event.text) if Eventtext~=nil then @@ -181,7 +181,7 @@ function MARKEROPS_BASE:OnEventMark(Event) end end elseif Event.id==world.event.S_EVENT_MARK_REMOVED then - self:T({event="S_EVENT_MARK_REMOVED", carrier=self.groupname, vec3=Event.pos}) + self:T({event="S_EVENT_MARK_REMOVED", carrier=Event.IniGroupName, vec3=Event.pos}) -- Hande event. local Eventtext = tostring(Event.text) if Eventtext~=nil then From 5881aa0ad3d97f5d2121710e421a50dfec9d75a9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Mar 2024 16:52:03 +0100 Subject: [PATCH 525/603] #SPAWN * Fix an issue for SPAWN:NewFromTemplate when re-using same template over and again --- Moose Development/Moose/Core/Database.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 65281c860..cbfa62555 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -932,7 +932,7 @@ function DATABASE:Spawn( SpawnTemplate ) SpawnTemplate.CountryID = nil SpawnTemplate.CategoryID = nil - self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID ) + self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID, SpawnTemplate.name ) self:T3( SpawnTemplate ) coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate ) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 68e3a10a8..01b8d11d9 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -536,7 +536,7 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr end if SpawnTemplate then - self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! + self.SpawnTemplate = UTILS.DeepCopy(SpawnTemplate) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! self.SpawnTemplatePrefix = SpawnTemplatePrefix self.SpawnAliasPrefix = SpawnAliasPrefix or SpawnTemplatePrefix self.SpawnTemplate.name = SpawnTemplatePrefix From ebb58cd976481007858dee9be079c92d955ad880 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 11 Mar 2024 18:18:00 +0100 Subject: [PATCH 526/603] xxx --- Moose Development/Moose/Core/Spawn.lua | 16 +++++-- Moose Development/Moose/Wrapper/Static.lua | 49 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 01b8d11d9..19af529b0 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3396,7 +3396,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end end end - + if self.SpawnInitKeepUnitNames == false then for UnitID = 1, #SpawnTemplate.units do SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID ) @@ -3404,9 +3404,17 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end else for UnitID = 1, #SpawnTemplate.units do - local UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" ) - self:T( { UnitPrefix, Rest } ) - + local SpawnInitKeepUnitIFF = false + if string.find(SpawnTemplate.units[UnitID].name,"#IFF_",1,true) then --Razbam IFF hack for F15E etc + SpawnInitKeepUnitIFF = true + end + local UnitPrefix, Rest + if SpawnInitKeepUnitIFF == false then + UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" ) + self:T( { UnitPrefix, Rest } ) + else + UnitPrefix=SpawnTemplate.units[UnitID].name + end SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID ) SpawnTemplate.units[UnitID].unitId = nil end diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index a6bd9c1d5..916b78a84 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -281,3 +281,52 @@ function STATIC:ReSpawnAt(Coordinate, Heading, Delay) return self end + +--- Find the first(!) STATIC matching using patterns. Note that this is **a lot** slower than `:FindByName()`! +-- @param #STATIC self +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA. +-- @return #STATIC The STATIC. +-- @usage +-- -- Find a static with a partial static name +-- local grp = STATIC:FindByMatching( "Apple" ) +-- -- will return e.g. a static named "Apple-1-1" +-- +-- -- using a pattern +-- local grp = STATIC:FindByMatching( ".%d.%d$" ) +-- -- will return the first static found ending in "-1-1" to "-9-9", but not e.g. "-10-1" +function STATIC:FindByMatching( Pattern ) + local GroupFound = nil + + for name,static in pairs(_DATABASE.STATICS) do + if string.match(name, Pattern ) then + GroupFound = static + break + end + end + + return GroupFound +end + +--- Find all STATIC objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`! +-- @param #STATIC self +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA. +-- @return #table Groups Table of matching #STATIC objects found +-- @usage +-- -- Find all static with a partial static name +-- local grptable = STATIC:FindAllByMatching( "Apple" ) +-- -- will return all statics with "Apple" in the name +-- +-- -- using a pattern +-- local grp = STATIC:FindAllByMatching( ".%d.%d$" ) +-- -- will return the all statics found ending in "-1-1" to "-9-9", but not e.g. "-10-1" or "-1-10" +function STATIC:FindAllByMatching( Pattern ) + local GroupsFound = {} + + for name,static in pairs(_DATABASE.STATICS) do + if string.match(name, Pattern ) then + GroupsFound[#GroupsFound+1] = static + end + end + + return GroupsFound +end From ada8629c01c7ff2012ded22a4abdbbf9867ca2ab Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 13 Mar 2024 07:38:56 +0100 Subject: [PATCH 527/603] Fix for speed nil --- Moose Development/Moose/Ops/ArmyGroup.lua | 2 +- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- Moose Development/Moose/Ops/NavyGroup.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index cdd686524..0e10ab195 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -2069,7 +2069,7 @@ function ARMYGROUP:_InitGroup(Template) self.speedMax=self.group:GetSpeedMax() -- Is group mobile? - if self.speedMax>3.6 then + if self.speedMax and self.speedMax>3.6 then self.isMobile=true else self.isMobile=false diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 1d40e3383..cae49ff7d 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -3799,7 +3799,7 @@ function FLIGHTGROUP:_InitGroup(Template) self.speedMax=group:GetSpeedMax() -- Is group mobile? - if self.speedMax>3.6 then + if self.speedMax and self.speedMax>3.6 then self.isMobile=true else self.isMobile=false diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 929225ee6..a3d5ebaa8 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -1800,7 +1800,7 @@ function NAVYGROUP:_InitGroup(Template) self.speedMax=self.group:GetSpeedMax() -- Is group mobile? - if self.speedMax>3.6 then + if self.speedMax and self.speedMax>3.6 then self.isMobile=true else self.isMobile=false From b66ff35b638dca86817cc041b58111fd35b32199 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 15 Mar 2024 10:25:58 +0100 Subject: [PATCH 528/603] #minor fixes --- Moose Development/Moose/Core/Set.lua | 22 +++++++++++++++++++-- Moose Development/Moose/Core/Zone.lua | 22 ++++++++++----------- Moose Development/Moose/Functional/Sead.lua | 3 --- Moose Development/Moose/Wrapper/Group.lua | 2 +- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 212bfc434..af0dfe661 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1097,6 +1097,7 @@ do GroupPrefixes = nil, Zones = nil, Functions = nil, + Alive = nil, }, FilterMeta = { Coalitions = { @@ -1470,7 +1471,7 @@ do end - --- Builds a set of groups that are only active. + --- Builds a set of groups that are active, ie in the mission but not yet activated (false) or actived (true). -- Only the groups that are active will be included within the set. -- @param #SET_GROUP self -- @param #boolean Active (Optional) Include only active groups to the set. @@ -1495,6 +1496,14 @@ do self.Filter.Active = Active return self end + + --- Build a set of groups that are alive. + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:FilterAlive() + self.Filter.Alive = true + return self + end --- Starts the filtering. -- @param #SET_GROUP self @@ -1993,7 +2002,16 @@ do function SET_GROUP:IsIncludeObject( MGroup ) self:F2( MGroup ) local MGroupInclude = true - + + if self.Filter.Alive == true then + local MGroupAlive = false + self:F( { Active = self.Filter.Active } ) + if MGroup and MGroup:IsAlive() then + MGroupAlive = true + end + MGroupInclude = MGroupInclude and MGroupAlive + end + if self.Filter.Active ~= nil then local MGroupActive = false self:F( { Active = self.Filter.Active } ) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 12df09b32..d2432703a 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -330,14 +330,14 @@ function ZONE_BASE:GetRandomVec2() return nil end ---- Define a random @{Core.Point#POINT_VEC2} within the zone. +--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table. -- @param #ZONE_BASE self -- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. function ZONE_BASE:GetRandomPointVec2() return nil end ---- Define a random @{Core.Point#POINT_VEC3} within the zone. +--- Define a random @{Core.Point#POINT_VEC3} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table. -- @param #ZONE_BASE self -- @return Core.Point#POINT_VEC3 The PointVec3 coordinates. function ZONE_BASE:GetRandomPointVec3() @@ -1515,7 +1515,7 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes) return point end ---- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. +--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table. -- @param #ZONE_RADIUS self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. @@ -1546,7 +1546,7 @@ function ZONE_RADIUS:GetRandomVec3( inner, outer ) end ---- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone. +--- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table. -- @param #ZONE_RADIUS self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. @@ -1990,7 +1990,7 @@ function ZONE_GROUP:GetRandomVec2() return Point end ---- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. +--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table. -- @param #ZONE_GROUP self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. @@ -2834,7 +2834,7 @@ function ZONE_POLYGON_BASE:GetRandomVec2() end end ---- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. +--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table. -- @param #ZONE_POLYGON_BASE self -- @return @{Core.Point#POINT_VEC2} function ZONE_POLYGON_BASE:GetRandomPointVec2() @@ -2847,7 +2847,7 @@ function ZONE_POLYGON_BASE:GetRandomPointVec2() return PointVec2 end ---- Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. +--- Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table. -- @param #ZONE_POLYGON_BASE self -- @return @{Core.Point#POINT_VEC3} function ZONE_POLYGON_BASE:GetRandomPointVec3() @@ -3835,18 +3835,18 @@ function ZONE_OVAL:GetRandomVec2() return {x=rx, y=ry} end ---- Define a random @{Core.Point#POINT_VEC2} within the zone. +--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table. -- @param #ZONE_OVAL self -- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. function ZONE_OVAL:GetRandomPointVec2() return POINT_VEC2:NewFromVec2(self:GetRandomVec2()) end ---- Define a random @{Core.Point#POINT_VEC2} within the zone. +--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table. -- @param #ZONE_OVAL self -- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. function ZONE_OVAL:GetRandomPointVec3() - return POINT_VEC2:NewFromVec3(self:GetRandomVec2()) + return POINT_VEC3:NewFromVec3(self:GetRandomVec2()) end --- Draw the zone on the F10 map. @@ -3986,7 +3986,7 @@ do -- ZONE_AIRBASE return ZoneVec2 end - --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. + --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table. -- @param #ZONE_AIRBASE self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 7b1958fbb..c50e2d310 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -320,9 +320,6 @@ function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADG end local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterZones({targetzone}):FilterOnce() - local tgtcoord = targetzone:GetRandomPointVec2() - --if tgtcoord and tgtcoord.ClassName == "COORDINATE" then - --local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord) local tgtgrp = seadset:GetRandom() local _targetgroup = nil local _targetgroupname = "none" diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 6beaa3b00..a15005b3c 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -426,7 +426,7 @@ function GROUP:IsActive() local DCSGroup = self:GetDCSObject() -- DCS#Group - if DCSGroup then + if DCSGroup and DCSGroup:isExist() then local unit = DCSGroup:getUnit(1) if unit then local GroupIsActive = unit:isActive() From e86afefe797aa5c978f700a0a3f795bad0b91569 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 22 Mar 2024 09:27:58 +0100 Subject: [PATCH 529/603] xxx --- Moose Development/Moose/Core/Spawn.lua | 1 + .../Moose/Functional/Scoring.lua | 4 +- .../Moose/Functional/Tactics.lua | 3071 +++++++++++++++++ .../Moose/Functional/Tiresias.lua | 2 +- Moose Development/Moose/Ops/Awacs.lua | 36 +- 5 files changed, 3102 insertions(+), 12 deletions(-) create mode 100644 Moose Development/Moose/Functional/Tactics.lua diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 19af529b0..78ec373d8 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3806,6 +3806,7 @@ end -- @param #number SpawnIndex Spawn index. -- @return #number self.SpawnIndex function SPAWN:_GetSpawnIndex( SpawnIndex ) + self:T("_GetSpawnIndex") self:F2( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnMaxGroups, self.SpawnMaxUnitsAlive, self.AliveUnits, #self.SpawnTemplate.units } ) if (self.SpawnMaxGroups == 0) or (SpawnIndex <= self.SpawnMaxGroups) then diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 15d23ba84..42dc8005a 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -229,7 +229,7 @@ SCORING = { ClassID = 0, Players = {}, AutoSave = true, - version = "1.18.2" + version = "1.18.3" } local _SCORINGCoalition = { @@ -1062,7 +1062,7 @@ function SCORING:_EventOnHit( Event ) if PlayerHit.UNIT.ThreatType == nil then PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() -- if this fails for some reason, set a good default value - if PlayerHit.ThreatType == nil then + if PlayerHit.ThreatType == nil or PlayerHit.ThreatType == "" then PlayerHit.ThreatLevel = 1 PlayerHit.ThreatType = "Unknown" end diff --git a/Moose Development/Moose/Functional/Tactics.lua b/Moose Development/Moose/Functional/Tactics.lua new file mode 100644 index 000000000..7186905e2 --- /dev/null +++ b/Moose Development/Moose/Functional/Tactics.lua @@ -0,0 +1,3071 @@ +--- **Functional** - Improve the autonomous behaviour of ground AI. +-- +-- === +-- +-- ## Features: +-- +-- * Mechanized Infantry Tactics +-- * Move and attack +-- * etc +-- +-- === +-- +-- ## Missions: +-- +-- ## [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS) +-- +-- === +-- +-- Short description +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) +-- +-- === +-- +-- ### Author: **Statua** +-- +-- ### Contributions: FlightControl +-- +-- === +-- +-- @module Functional.Tactics +-- @image Tactics.JPG + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- TACTICS class +-- @type TACTICS +-- @field #string ClassName Name of the class. +-- @field #boolean Debug Write Debug messages to DCS log file and send Debug messages to all players. +-- @field #string lid String for DCS log file. +-- @extends Core.Fsm#FSM +-- + +--- Improve the autonomous behaviour of ground AI. +-- +-- ## Title +-- +-- Body +-- +-- ### Subtitle +-- +-- Sub body +-- +-- ![Process](..\Presentations\TACTICS\image.png) +-- +-- Talking about this function/method @{#TACTICS.OnAfterTroopsDropped}() +-- +-- # Examples +-- +-- ## Mechanized Infantry +-- This example shows how to set up a basic mechanized infantry group which will respond to enemy contact by dispersing, deploying troops, and using the troops to fight back. +-- +-- ![Process](..\Presentations\TACTICS\Tactics_Example_01.png) +-- +-- +-- # Customization and Fine Tuning +-- The following user functions can be used to change the default values +-- +-- * @{#TACTICS.DefaultTroopAttackDist}() can be used to set the default maximum distance troops will move to attack a target after disembarking +-- +-- +-- @field #TACTICS +TACTICS = {} +TACTICS_UTILS = {} +TACTICS.ClassName = "TACTICS" +TACTICS.Debug = false +TACTICS.lid = nil +TACTICS_UTILS.GroupsRed = {} +TACTICS_UTILS.GroupsBlue = {} +TACTICS_UTILS.SetGroups = nil +TACTICS_UTILS.SetActive = false + +--- TACTICS version. +-- @field #number version +TACTICS.version="0.1.0" + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--TODO list +--Check timer cutoffs in deadResponse +--Check nil for dead groups in all timers +--Check stuck in movement loop timers + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--[[Event Callbacks]] +--UnitLost(EventData) +--GroupDead(EventData) + +--StartTravel(StartPoint, EndPoint) +--Redirect(CurrentPoint, EndPoint) +--EndTravel(ActualPoint, Endpoint) + +--StartEngaging(WasHit, UnitName, WeaponName, EventData) +--EngageToIdle +--EngageToTravel +--EngageToRearming +--EngageToWinchester + +--IsWinchester +--StartRearming +--RearmedToIdle +--RearmedToTravel + +--StartAttacking +--AttackToIdle +--AttackToTravel + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Creates a new object to handle Tactics for a given Wrapper.Group#GROUP object. +-- @param #TACTICS self +-- @param Wrapper.Group#GROUP group The GROUP object for which tactics should be applied. +-- @return #TACTICS self +function TACTICS:New(group) + + ----------[INITIALIZATION]---------- + TACTICS_UTILS.SetGroups = SET_GROUP:new() + --Start Set + if not TACTICS_UTILS.SetActive then + TACTICS_UTILS.SetActive = true + TACTICS_UTILS.SetGroups:FilterStart() + end + + --Inherit from MOOSE + local self=BASE:Inherit(self, FSM:New()) -- #TACTICS + + + --Validate Wrapper.GROUP + if group:GetCoordinate() ~=nil then + self.lid=string.format("TACTICS %s | ", tostring(group:GetName())) + self:T("TACTICS version "..TACTICS.version..": Starting tactics handler for "..group:GetName()) + else + self:E("TACTICS: Requested group does not exist! (Has to be a MOOSE group that is also alive)") + return nil + end + + + --Warn on non-ground group + if group:IsGround() == false then + self:E("TACTICS: WARNING! "..group:GetName().." is not a ground group. Tactics are designed for ground units only and may result in errors or strange behaviour with this group.") + end + + + --Establish FSM Capabilities + self:SetStartState( "Stopped" ) + self:AddTransition( "Stopped", "Start", "Idle" ) + self:AddTransition( "*", "Stop", "Stopped" ) + self:AddTransition( "*", "GroupDead", "Dead" ) + self:AddTransition( "*", "UnitLost", "*" ) + self:AddTransition( "*", "Winchester", "*" ) + self:AddTransition( "*", "FullyRearmed", "*" ) + self:AddTransition( "*", "TargetSpotted", "*" ) + self:AddTransition( "*", "AttackManeuver", "*" ) + self:AddTransition( "*", "TroopsDropped", "*" ) + self:AddTransition( "*", "TroopsReturned", "*" ) + self:AddTransition( "*", "TroopsExtracted", "*" ) + self:AddTransition ("*", "SupportRequested", "*") + self:AddTransition ("*", "Supporting", "*") + self:AddTransition ("*", "Abandoned", "*") + self:AddTransition ("*", "CrewRecovered", "*") + + + + -- (Travel) + self:AddTransition( "Idle", "StartTravel", "Travelling" ) + self:AddTransition( "Travelling", "Redirect", "Travelling" ) + self:AddTransition( "Travelling", "EndTravel", "Idle" ) + + -- (Engage) + self:AddTransition( {"Idle", "Travelling","Attacking","Rearming"}, "StartEngaging", "Engaging" ) + self:AddTransition( "Engaging", "EngageToIdle", "Idle" ) + self:AddTransition( "Engaging", "EngageToTravel", "Travelling" ) + + -- (Rearming) + self:AddTransition( "*", "RequestRearming", "Rearming" ) + self:AddTransition( "Rearming", "RearmedToIdle", "Idle" ) + self:AddTransition( "Rearming", "RearmedToTravel", "Travelling" ) + + -- (Attacking) + self:AddTransition( {"Idle","Travelling"}, "StartAttack", "Attacking" ) + self:AddTransition( "Attacking", "AttackToIdle", "Idle" ) + self:AddTransition( "Attacking", "AttackToTravel", "Travelling" ) + self:AddTransition( {"Idle","Travelling"}, "StartHold", "Holding" ) + self:AddTransition( "Holding", "HoldToIdle", "Idle" ) + self:AddTransition( "Holding", "HoldToTravel", "Travelling" ) + self:AddTransition( {"Idle","Travelling"}, "StartAvoid", "Avoiding" ) + self:AddTransition( "Avoiding", "AvoidToIdle", "Idle" ) + self:AddTransition( "Avoiding", "AvoidToTravel", "Travelling" ) + + -- (Retreating) + self:AddTransition( "*", "StartRetreat", "Retreating" ) + self:AddTransition( "Retreating", "EndRetreat", "Idle" ) + + + --Initialize Group Data + self.TickRate = 1 + self.Group = group --The Wrapper.Group#GROUP object of the group. + self.Groupname = group:GetName() --The DCS GroupName of the Wrapper.Group#GROUP object + self.groupCoalition = self.Group:GetCoalition() + self.enemyCoalition = coalition.side.RED + self.groupCoalitionString = "blue" + self.enemyCoalitionString = "red" + if self.groupCoalition == coalition.side.RED then + self.enemyCoalition = coalition.side.BLUE + self.groupCoalitionString = "red" + self.enemyCoalitionString = "blue" + end + self.MessageOutput = false + self.MessageDuration = 10 + self.MessageSound = "squelch2.ogg" + self.MessageCallsign = "ANONYMOUS" + self.Sleeping = false + + + --(Group Units) + self.UnitTable = self.Group:GetUnits() --A table containing all of the Wrapper.Unit#UNIT objects in the group with nested data. + for i = 1, #self.UnitTable do + self.UnitTable[i].UnitName = self.UnitTable[i]:GetName() --Name of the Wrapper.Unit#UNIT object + self.UnitTable[i].TroopTransport = false --Is the unit a troop transport? + self.UnitTable[i].TroopsBoarded = false --Are any troops on board? + self.UnitTable[i].TroopGroup = nil --The Wrapper.Group#GROUP object of deployed troops when dropped + self.UnitTable[i].TroopsAlive = nil --Number of troops alive from the original template + end + + --(Ammunition/Rearming) + self.groupAmmo = {} + self.LowAmmoPercent = 0.25 + self.FullAmmoPercent = 0.95 + self.IsWinchester = false + self.IsRearming = false + self.RearmTemplate = nil + self.RearmSpawnCoord = nil + self.AllowRearming = false + self.DespawnRearmingGroup = false + self.AllowRearmRespawn = true + self.RearmGroup = nil + self.RearmGroupBase = nil + self.RearmGroupRTB = true + self.RearmGroupUseRoads = true + self.RearmGroupSpeed = 60 + self.RearmGroupFormation = "Cone" + self.RearmDrawing = false + self.DrawDataR = {} + + --(Troop Transport) + self.TroopTransport = false --Set to true to enable troop transport. Additional options required + self.TransportUnitPrefix = nil --Provide a #string to identify specific units that will carry troops. Leave as nil to enable troop transport for all units of the group + self.TroopTemplate = nil --A late activated Wrapper.Group#GROUP object to use as the template for troop deployment + self.TroopAttackDist = 1000 --How far troops deployed will move to attack the closest enemy (also affects when an attacking group stops to deploy troops) + self.TroopDismountDistMin = 100 -- If engaging beyond of TroopAttackDist, how far will troops move away from the carriers when disembarking (min random value) + self.TroopDismountDistMax = 300 -- If engaging beyond of TroopAttackDist, how far will troops move away from the carriers when disembarking (max random value) + self.TroopFormation = "Diamond" -- Formation the troops will move in when attacking + self.TroopMoveSpeed = 24 + self.TroopsAttacking = false + self.DeployedTroops = {} + self.AttackingTroops = {} + self.ExtractTimeLimit = 300 + + --(Travel) + self.Destination = nil --The last provided destination in the TravelTo call. + self.DestinationRate = 30 --How often in seconds to check up on the traveling group and see if its at its destination or is stuck + self.DestinationRadius = 100 --How close the group needs to be to its destination to consider it as arrived + self.FixStuck = true --Allow the group to make short movements in different directions if they've stopped before reaching their destination without switching states + self.UnstuckDist = 100 --How far to place a waypoint for the group to try and get it unstuck + self.MaxUnstuckAttempts = 10 --How many attempts to make to get unstuck before it stops trying. Returns state to Idle if DestroyStuck is false + self.DestroyStuck = false --Set to true to remove the group from the game if MaxUnstuckAttempts has been reached. + self.StuckHeadingIncrements = 60 + self.InclineLimit = 35 + + self.travelToStored = nil + self.stuckPosit = nil + self.lastStuckHeading = nil + self.countStuck = 0 + + --(Detection) + self.UseDetection = true + self.DetectionRate = 30 + self.TacticalROE = "Attack" --Options = "Attack", "Avoid", "Hold", "Ignore" + self.UseLOS = true + self.UseDetectionChance = true + self.MaxDetection = 6000 + self.FullDetection = 500 + self.FilterDetectionZones = nil + self.FilterEnemyType = nil --Options = "Helicopter", "Airplane", "Ground Unit", "Ship", "Train" + self.ManageAlarmState = true + self.DefaultAlarmState = "Auto" + self.ClosestEnemy = nil + self.DrawEnemySpots = true + self.EnemySpotStale = 120 + self.markSpot = nil + self.markSpotTimer = nil + self.zoneDetection = ZONE_RADIUS:New("DetZone_"..self.Groupname, self.Group:GetVec2(), self.MaxDetection, true) + + --(Attack) + self.CombatDistance = 2000 + self.AttackFarUsesRoads = false + self.AttackPositionDist = 200 + self.attackLastCoord = nil + self.AttackSpeed = 20 + self.AttackSpeedFar = 60 + self.AttackFormationFar = "Diamond" + self.AttackFormation = "Rank" + self.AttackTimeout = 300 + self.LastTargetThreshold = 25 + + self.HoldingTime = 120 + self.HoldDistance = 4000 + + self.AvoidDistance = 2000 + self.AvoidAzimuthMax = 30 + self.AvoidUseRoads = false + self.AvoidSpeed = 60 + self.AvoidFormation = 'Cone' + self.avoidDest = nil + self.AvoidRate = 30 + + --(Engagement) + self.ReactToHits = true + self.ReactAfterShooting = true + self.EvadeDistance = 25 + self.EngageCooldown = 120 + self.DisperseOnShoot = true + self.DisperseOnHit = true + self.DrawDataE = {} + self.EngageDrawing = false + self.EngageDrawingFresh = 120 + self.EngageDrawingStale = 300 + + --(Retreating) + self.RetreatAfterLosses = nil + self.RetreatZone = nil + self.RetreatSpeed = 60 + self.RetreatOnRoads = true + self.RetreatFormation = "Diamond" + self.DespawnAfterRetreat = false + self.retreatDestination = nil + + --(SUPPORT REQUEST) + self.AllowSupportRequests = true + self.SupportRadius = 4000 + self.SupportGroupLimit = 3 + self.RespondToSupport = true + self.SupportLevel = 3 --1 = On Spotted, 2 = On Engage, 3 = On Hit, 4 = On Unit Loss + self.supportTable = {} + self.SupportCooldown = 300 + + --(ABANDON VEHICLE) + self.AbandonEnabled = false + self.CrewTemplate = nil + self.AbandonHealth = 0.5 + self.AbandonDistance = 1000 + self.AllowSelfRecover = true + self.RecoveryVehicle = nil + self.RecoverySpawnZone = nil + self.RecoveryUseRoads = true + self.RecoverySpeed = 60 + self.RecoveryFormation = "Diamond" + self.SmokeAbandoned = false + self.StaticSmokeTimeout = 300 + + --Event Handling + self.Group:HandleEvent(EVENTS.Hit) + self.Group:HandleEvent(EVENTS.ShootingStart) + self.Group:HandleEvent(EVENTS.Shot) + self.Group:HandleEvent(EVENTS.Dead) + + local function handleContact(type, EventData) + if not self.Sleeping then + if not self:Is("Retreating") then + if type == "Hit" then + if self.ReactToHits then + if not self:Is("Engaging") then self:_InitEngagement(true,EventData) end + self.TimeEngageActive = true + self.TimeEngageStamp = timer.getAbsTime() + if self.SupportLevel <= 3 and not self.TimeSupportActive and self.AllowSupportRequests then + self:RequestSupport(EventData.IniUnit) + end + end + end + if type == "Shooting" then + if self.ReactAfterShooting then + if not self:Is("Engaging") then self:_InitEngagement(false,EventData) end + self.TimeEngageActive = true + self.TimeEngageStamp = timer.getAbsTime() + if self.SupportLevel <= 2 and not self.TimeSupportActive and self.AllowSupportRequests then + self:RequestSupport(EventData.TgtUnit) + end + end + end + if type == "Shot" then + if self.ReactAfterShooting then + if not self:Is("Engaging") then self:_InitEngagement(false,EventData) end + self.TimeEngageActive = true + self.TimeEngageStamp = timer.getAbsTime() + if self.SupportLevel <= 2 and not self.TimeSupportActive and self.AllowSupportRequests then + self:RequestSupport(EventData.TgtUnit) + end + end + end + end + end + if type == "Dead" then + self:_DeadResponse(EventData) + end + end + + function self.Group:OnEventHit(EventData) + handleContact("Hit", EventData) + end + + function self.Group:OnEventShot(EventData) + handleContact("Shot", EventData) + end + + function self.Group:OnEventShootingStart(EventData) + handleContact("Shooting", EventData) + end + + function self.Group:OnEventDead(EventData) + handleContact("Dead", EventData) + end + + + + --Build group ammunition table if able + for i = 1, #self.UnitTable do + local tableAmmo = self.UnitTable[i]:GetAmmo() + if tableAmmo then + for a = 1, #tableAmmo do + self:T( self.UnitTable[i].UnitName .. " has " .. tableAmmo[a]["count"] .. " " .. tableAmmo[a]["desc"]["typeName"]) + end + table.insert(self.groupAmmo, tableAmmo) + else + self:T( self.UnitTable[i].UnitName .. " does not use ammunition.") + table.insert(self.groupAmmo, nil) + end + end + + --Set Group Initialization + -- TIMER:New(function() + -- self.setEnemy = SET_UNIT:New():FilterCoalitions(self.enemyCoalitionString):FilterActive() + -- if self.FilterEnemyType then self.setEnemy:FilterCategories(self.FilterEnemyType) end + -- if self.FilterDetectionZones then self.setEnemy:FilterZones(self.FilterDetectionZones) end + -- self.setEnemy:FilterStart() + -- end):Start(1) + + + + + ----------[TIMER BASED FUNCTIONS]---------- + + + --Master Time Tracking + local initialTime = timer.getAbsTime() + self.TimeTravelActive = false + self.TimeTravelStamp = initialTime + self.TimeDetectionActive = false + self.TimeDetectionStamp = initialTime + self.TimeRearmActive = false + self.TimeRearmStamp = initialTime + self.TimeRetreatActive = false + self.TimeRetreatStamp = initialTime + self.TimeAvoidActive = false + self.TimeAvoidStamp = initialTime + + self.TimeEngageActive = false + self.TimeEngageStamp = initialTime + self.TimeHoldActive = false + self.TimeHoldStamp = initialTime + self.TimeAttackActive = false + self.TimeAttackStamp = initialTime + self.TimeSupportActive = false + self.TimeSupportStamp = initialTime + + + --Master Timer + self.MasterTime = TIMER:New(function() + local timeNow = timer.getAbsTime() + + ----{LOOPS}---- + --Travel Tracker + if self.TimeTravelActive then + if timeNow >= self.TimeTravelStamp + self.DestinationRate then + self.TimeTravelStamp = timer.getAbsTime() + self:_TravelCheck() + end + end + + --Detection + if self.TimeDetectionActive then + if timeNow >= self.TimeDetectionStamp + self.DetectionRate then + self.TimeDetectionStamp = timer.getAbsTime() + self:_DetectionCycle() + end + end + + --Avoid + if self.TimeAvoidActive then + if timeNow >= self.TimeAvoidStamp + self.AvoidRate then + self.TimeAvoidStamp = timer.getAbsTime() + self:_AvoidTimer() + end + end + + --Rearmed + if self.TimeRearmActive then + if timeNow >= self.TimeRearmStamp + 60 then + self.TimeRearmStamp = timer.getAbsTime() + self:_RearmCheckTimer() + end + end + + --Retreat + if self.TimeRetreatActive then + if timeNow >= self.TimeRetreatStamp + 60 then + self.TimeRetreatStamp = timer.getAbsTime() + self:_RetreatTimer() + end + end + + + ----{COOLDOWNS}---- + --Engagement + if self.TimeEngageActive then + if timeNow >= self.TimeEngageStamp + self.EngageCooldown then + self.TimeEngageActive = false + self:_EngageCooldown() + end + end + + --Hold + if self.TimeHoldActive then + if timeNow >= self.TimeHoldStamp + self.HoldingTime then + self.TimeHoldActive = false + self:_HoldTimer() + end + end + + --Attack + if self.TimeAttackActive then + if timeNow >= self.TimeAttackStamp + self.AttackTimeout then + self.TimeAttackActive = false + self:_AttackTimer() + end + end + + --Support + if self.TimeSupportActive then + if timeNow >= self.TimeSupportStamp + self.SupportCooldown then + self.TimeSupportActive = false + end + end + end) + + + ----------[USER FUNCTIONS]---------- + + + + + + + --Constructor related stuff + if self.groupCoalition == coalition.side.RED then + table.insert(TACTICS_UTILS.GroupsRed, self) + else + table.insert(TACTICS_UTILS.GroupsBlue, self) + end + + self:__Start(1) + return self +end + + + + +--------[INTERNAL FSM CALLBACKS]--------- + +--- [Internal] FSM Function onafterStart +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterStart(From,Event,To) + self:T({From, Event, To}) + self.MasterTime:Start(self.TickRate,self.TickRate) + if self.UseDetection == true then self.TimeDetectionActive = true end + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is now active and available for tasking.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end + return self +end + +--- [Internal] FSM Function onafterStop +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterStop(From,Event,To) + self:T({From, Event, To}) + self.MasterTime:Stop() + return self +end + +--- [Internal] FSM Function onafterStartTravel +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterStartTravel(From,Event,To,CoordGroup,CoordDest) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is initiating travel to our assigned destination.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterRedirect +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterRedirect(From,Event,To,CoordGroup,CoordDest) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." received. Redirecting to the new destination.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterEndTravel +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterEndTravel(From,Event,To,GroupCoord,DestCoord) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." has arrived at our destination. Ready for tasking.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterStartAvoid +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterStartAvoid(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is avoiding the target.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterAvoidToTravel +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterAvoidToTravel(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is no longer avoiding the target. Resuming travel to destination.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterAvoidToIdle +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterAvoidToIdle(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is no longer avoiding the target. Ready for tasking.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterEngageToTravel +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterEngageToTravel(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is no longer engaged with the enemy. Resuming travel to destination.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterEngageToIdle +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterEngageToIdle(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is no longer engaged with the enemy. Ready for tasking.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterHoldToTravel +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterHoldToTravel(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is no longer holding from the enemy contact. Resuming travel to destination.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterHoldToIdle +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterHoldToIdle(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is no longer holding from the enemy contact. Ready for tasking.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterStartAttack +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterStartAttack(From,Event,To,_enemy,distance,hdgEnemy) + if self.MessageOutput then + local tgtType = "enemy target" + if _enemy:GetClassName() and not _enemy:GetClassName() == "COORDINATE" then tgtType = _enemy:GetTypeName() end + MESSAGE:New(self.MessageCallsign.." is moving to attack a "..tostring(tgtType).." at "..math.floor(hdgEnemy).." for "..math.floor(distance).."m.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterAttackToTravel +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterAttackToTravel(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is no longer attacking the enemy. Resuming travel to destination.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterAttackToIdle +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterAttackToIdle(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is no longer attacking the enemy. Ready for tasking.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterStartEngaging +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterStartEngaging(From,Event,To,WasHit,UnitName,WeaponName,EventData) + if self.MessageOutput then + if WasHit then + if EventData.IniTypeName then + MESSAGE:New(self.MessageCallsign.." is engaged with and receiving fire from a "..tostring(EventData.IniTypeName).."!",self.MessageDuration):ToCoalition(self.groupCoalition) + else + MESSAGE:New(self.MessageCallsign.." is engaged with and receiving fire from the enemy!",self.MessageDuration):ToCoalition(self.groupCoalition) + end + else + if EventData.TgtTypeName then + MESSAGE:New(self.MessageCallsign.." is engaged with and attacking a "..tostring(EventData.TgtTypeName).."!",self.MessageDuration):ToCoalition(self.groupCoalition) + else + MESSAGE:New(self.MessageCallsign.." is engaged with and attacking the enemy!",self.MessageDuration):ToCoalition(self.groupCoalition) + end + end + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterRetreat +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterRetreat(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." has taken significant losses and is retreating!",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterEndRetreat +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterEndRetreat(From,Event,To,CoordGroup,CoordRetreat) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is no longer retreating.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterGroupDead +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterGroupDead(From,Event,To,EventData) + if self.MessageOutput and EventData then + MESSAGE:New(self.MessageCallsign.." is KIA!",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterWinchester +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterWinchester(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is winchester!",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterRequestRearming +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterRequestRearming(From,Event,To,GroupRearm) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." requesting rearming.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterFullyRearmed +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterFullyRearmed(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." is fully rearmed.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterTroopsDropped +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterTroopsDropped(From,Event,To,TargetCoord,IsAttacking) + if self.MessageOutput then + local attackMessage = " to attack a target" + if not IsAttacking then attackMessage = "" end + MESSAGE:New(self.MessageCallsign.."'s infantry are now disembarked"..attackMessage..".",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterTroopsReturned +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterTroopsReturned(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." has embarked all of our troops.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterTroopsExtracted +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterTroopsExtracted(From,Event,To) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." has extracted troops.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterSupportRequested +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterSupportRequested(From,Event,To,CoordTarget,UnitTarget) + if self.MessageOutput then + local addMessage = "" + if UnitTarget then addMessage = " Our target is a "..tostring(UnitTarget:GetTypeName()).."." end + MESSAGE:New(self.MessageCallsign.." requesting support!"..addMessage,self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterSupporting +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterSupporting(From,Event,To,Group,CoordTarget,Callsign) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." will provide support for "..tostring(Callsign)..".",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterAbandoned +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterAbandoned(From,Event,To,UnitCoord,UnitName,UnitType) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." has abandoned a "..UnitType.." due to excessive damage.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + +--- [Internal] FSM Function onafterCrewRecovered +-- @param #TACTICS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TACTICS self +function TACTICS:onafterCrewRecovered(From,Event,To,RecoveryUnit,RecoveryName) + if self.MessageOutput then + MESSAGE:New(self.MessageCallsign.." the abandoning crew has been recovered by another vehicle.",self.MessageDuration):ToCoalition(self.groupCoalition) + USERSOUND:New(self.MessageSound):ToCoalition(self.groupCoalition) + end +end + + + + +--------[TIMER METHODS]--------- + +---LOOP: Travel Tracker +--- [Internal] Travek Chek +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_TravelCheck() + if self:Is("Travelling") then + self:T2("TACTICS: "..self.Groupname.." travel check timer.") + local coordGroup = self.Group:GetAverageCoordinate() + if not coordGroup then coordGroup = self.Group:GetCoordinate() end + if not coordGroup then + if not self:Is("Dead") then + self:_DeadResponse() + end + elseif self.Destination then + local distGroup = coordGroup:Get3DDistance(self.Destination) + local speedGroup = self.Group:GetVelocityKMH() + + --Arrived at destination + if distGroup <= self.DestinationRadius then + self.Group:RouteStop() + self:T("TACTICS: "..self.Groupname.." arrived at their destination.") + self.TimeTravelActive = false + self.travelToStored = nil + self:EndTravel(self.Group:GetAverageCoordinate(), self.Destination) + self.Destination = nil + elseif speedGroup < 1 and self.FixStuck or self.countStuck > 0 then -- Attempt to get unstuck if it's stopped before arriving at the destination. + self.countStuck = self.countStuck + 1 + self:T("TACTICS: "..self.Groupname.." is stopped when it should be moving. Check #"..self.countStuck) + if self.countStuck == 1 then -- Initial unstuck check + self.stuckPosit = self.Group:GetAverageCoordinate() + elseif self.countStuck > 1 and self.countStuck < self.MaxUnstuckAttempts then + local distStuck = coordGroup:Get3DDistance(self.stuckPosit) + if distStuck <= self.UnstuckDist/4 then -- Attempt to get unstuck + if self.lastStuckHeading == nil then + self.lastStuckHeading = (self.stuckPosit:HeadingTo(self.Destination) + self.StuckHeadingIncrements) % 360 + else + self.lastStuckHeading = (self.lastStuckHeading + self.StuckHeadingIncrements) % 360 + end + local unstuckDest = coordGroup:Translate(self.UnstuckDist,self.lastStuckHeading,false,false) + self.Group:RouteGroundTo(unstuckDest, 10, 'Cone', 1) + self:T("TACTICS: "..self.Groupname.." is attempting to get unstuck by driving "..self.UnstuckDist .."m toward heading "..self.lastStuckHeading) + else -- Group has moved enough to resume the route + self:T("TACTICS: "..self.Groupname.." has gotten unstuck. Resuming on the path...") + self.countStuck = 0 + self.stuckPosit = nil + self.TimeTravelActive = false + self:_InitiateTravel() + end + elseif self.countStuck >= self.MaxUnstuckAttempts then -- Max stuck attempts exceeded + if self.DestroyStuck then --EITHER Destroy the group + self:E("TACTICS: "..self.Groupname.." has been stuck for too long and was removed from the game...") + self.Group:Destroy() + self:_DeadResponse(false) + else --OR Return the group to Idle + self:E("TACTICS: "..self.Groupname.." has been stuck for too long and was returned to the idle state...") + self.Group:RouteStop() + self.TimeTravelActive = false + self.travelToStored = nil + self:EndTravel(self.Group:GetAverageCoordinate(), self.Destination) + self.Destination = nil + end + end + else + self.countStuck = 0 + self.stuckPosit = nil + end + else + self:E("TACTCS ERROR: "..self.Groupname.." attempted to compare distance to travel destination but 'self.Destination' is a nil value!") + end + else + self.TimeTravelActive = false + self.stuckPosit = nil + self.lastStuckHeading = nil + self.countStuck = 0 + end + return self +end + + +--- [Internal] Detection Cycle +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_DetectionCycle() + if not self:Is("Engaging") then + self:T2("TACTICS: ---- Detection Cycle ---- - [GRP: "..self.Groupname.."]") + local coordGroup = self.Group:GetAverageCoordinate() + if not coordGroup then coordGroup = self.Group:GetCoordinate() end + if not coordGroup then + if not self:Is("Dead") then + self:_DeadResponse() + end + end + if coordGroup then + --Try to detect an enemy. + local distClosestEnemy = 9999999 + local newSpot = false + local hdgClosestEnemy = 0 + self.zoneDetection:SetVec2(coordGroup:GetVec2()) + + TACTICS_UTILS.SetGroups:ForEachGroupAnyInZone(self.zoneDetection, function(_setgroup) + local coordEnemy = _setgroup:GetCoordinate() + local validUnit = true + if not coordEnemy then + validUnit = false + elseif _setgroup:GetCoalition() == self.groupCoalition then + validUnit = false + elseif self.FilterEnemyType and not _setgroup:GetCategoryName() == self.FilterEnemyType then + validUnit = false + elseif self.FilterDetectionZones and type(self.FilterDetectionZones) == "table" then + local inZone = false + for i = 1, #self.FilterDetectionZones do + if _setgroup:IsAnyInZone(self.FilterDetectionZones[i]) then + inZone = true + break + end + end + validUnit = inZone + end + + if validUnit then + local tableEnemyUnits = _setgroup:GetUnits() + for i = 1,#tableEnemyUnits do + local _enemy = tableEnemyUnits[i] + if _enemy:IsInZone(self.zoneDetection) then + local speedEnemy = _enemy:GetVelocityKMH() + local distEnemy = coordGroup:Get3DDistance(coordEnemy) + self:T2("TACTICS: Checking "..tostring(_enemy:GetName()).." at a range of "..tostring(distEnemy).."m / "..tostring(self.MaxDetection).."m") + if distEnemy < self.MaxDetection then + local checkLOS = true + if self.UseLOS then checkLOS = coordGroup:IsLOS(coordEnemy) end + self:T2("TACTICS: "..tostring(_enemy:GetName()).." is LOS = "..tostring(checkLOS)) + if distEnemy < self.FullDetection and distEnemy < distClosestEnemy then + self:T2("TACTICS: "..tostring(_enemy:GetName()).." is the closest target and is within Full Detection range ("..tostring(self.FullDetection).."m)") + distClosestEnemy = distEnemy + hdgClosestEnemy = coordGroup:HeadingTo(coordEnemy) + self.ClosestEnemy = _enemy + newSpot = true + elseif checkLOS then + local distVarChance = 2 + if self.UseDetectionChance then distVarChance = (self.MaxDetection - distEnemy)/self.MaxDetection end + local checkChance = math.random(1,100) + if self.FilterEnemyType == "ground" then + if speedEnemy > 5 and speedEnemy < 60 then checkChance = checkChance / 2 end + end + if self.FilterEnemyType == "helicopter" and self.UseDetectionChance or self.FilterEnemyType == "airplane" and self.UseDetectionChance then + local altVarChance = 0.05 + local altHelo = _enemy:GetAltitude() + local altGroup = _grp:GetAltitude() + local altDiff = altHelo - altGroup + local degHelo = 0 + if altDiff > 0 then + local angHelo = math.asin(altDiff/distEnemy) + degHelo = math.deg(angHelo) + if degHelo > 15 then altVarChance = 1.0 elseif degHelo > 5 then altVarChance = (degHelo/15) end + end + distVarChance = distVarChance + altVarChance + end + + self:T2("TACTICS: Detection roll: Is "..tostring(checkChance).." <= "..tostring((distVarChance/2)*100).."?") + if checkChance <= (distVarChance/2)*100 and distEnemy < distClosestEnemy then + self:T2("TACTICS: "..tostring(_enemy:GetName()).." was detected by "..self.Groupname.." and is the closest target") + distClosestEnemy = distEnemy + hdgClosestEnemy = coordGroup:HeadingTo(coordEnemy) + self.ClosestEnemy = _enemy + newSpot = true + end + end + end + end + end + end + end) + if newSpot then + self:_SpotTarget(self.ClosestEnemy,distClosestEnemy) + self:TargetSpotted(self.ClosestEnemy,distClosestEnemy,hdgClosestEnemy) + if self.DrawEnemySpots then + self:_ContactMark(self.ClosestEnemy:GetCoordinate(),2) + end + end + end + end + return self +end + +--- [Internal] Avoid Timer Function +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_AvoidTimer() + self:T("TACTICS: >> AVOID TIMER << - [GRP: "..self.Groupname.."]") + if not self.Group:GetCoordinate() then + if not self:Is("Dead") then + self:_DeadResponse() + end + else + if self:Is("Engaging") then + self.TimeAvoidActive = false + else + local coordGroup = self.Group:GetAverageCoordinate() + if not coordGroup then coordGroup = self.Group:GetCoordinate() end + local distGroup = coordGroup:Get3DDistance(self.avoidDest) + if distGroup <= self.DestinationRadius then + self:I("TACTICS: "..self.Groupname.." is no longer avoiding the threat.") + if self.ManageAlarmState then + if self.DefaultAlarmState == "Auto" then + self.Group:OptionAlarmStateAuto() + elseif self.DefaultAlarmState == "Red" then + self.Group:OptionAlarmStateRed() + elseif self.DefaultAlarmState == "Green" then + self.Group:OptionAlarmStateGreen() + end + end + if self.travelToStored then + self:AvoidToTravel() + self:_InitiateTravel() + else + self.Group:RouteStop() + self:AvoidToIdle() + end + self.TimeAvoidActive = false + end + end + end + return self +end + +--- [Internal] Rearm check timer +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_RearmCheckTimer() + self:T("TACTICS: >> REARM CHECK TIMER << - [GRP: "..self.Groupname.."]") + if not self.Group:GetCoordinate() then + if not self:Is("Dead") then + self:_DeadResponse() + end + else + --Finish up if full + local isWinchester, isFull = self:CheckAmmo() + if isFull or self:Is("Retreating") then + if not self:Is("Retreating") then + self:I("TACTICS: "..self.Groupname.." is fully rearmed.") + else + self:I("TACTICS: "..self.Groupname.." interrupted rearming to retreat.") + end + self.TimeRearmActive = false + self.IsWinchester = false + self.IsRearming = false + self:FullyRearmed() + self:_RTBRearmGroup() + if self.DrawDataR.DrawText then + trigger.action.removeMark(self.DrawDataR.DrawText) + self.DrawDataR.DrawText = nil + end + if self.DrawDataR.DrawPoly then + trigger.action.removeMark(self.DrawDataR.DrawPoly) + self.DrawDataR.DrawPoly = nil + end + + --Return to activity + if self.Destination and not self:Is("Retreating") then + self:RearmedToTravel() + self:_InitiateTravel() + elseif not self:Is("Retreating") then + self:RearmedToIdle() + end + end + + --Dispatch another group if lost + if not self:Is("Retreating") then + if not self.RearmGroup and not self.RearmGroupBase then + self:T("TACTICS: "..self.Groupname.." does not have a rearm group or base. ") + elseif not self.RearmGroup:GetCoordinate() and self.AllowRearmRespawn then + self:_CallForRearm(true) + elseif not self.RearmGroup:GetCoordinate() and not self.AllowRearmRespawn then + self:I("TACTICS: "..self.Groupname.." can no longer be rearmed.") + self.TimeRearmActive = false + self.IsWinchester = false + self.IsRearming = false + self.AllowRearming = false + + --Return to activity + if self.Destination then + self:RearmedToTravel() + self:_InitiateTravel() + else + self:RearmedToIdle() + end + end + end + end + return self +end + +--- [Internal] Retreat check timer +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_RetreatTimer() + self:T("TACTICS: >> RETREAT CHECK TIMER << - [GRP: "..self.Groupname.."]") + if not self.Group:GetCoordinate() then + if not self:Is("Dead") then + self:_DeadResponse() + end + elseif self:Is("Retreating") then + local unitsRemain, selfRecover = self:CheckUnitLife() + if unitsRemain > 0 then + local coordGroup = self.Group:GetAverageCoordinate() + if not coordGroup then coordGroup = self.Group:GetCoordinate() end + local distGroup = coordGroup:Get3DDistance(self.retreatDestination) + local speedGroup = self.Group:GetVelocityKMH() + + --Arrived at destination + if distGroup <= self.DestinationRadius then + self.Group:RouteStop() + self:T("TACTICS: "..self.Groupname.." arrived at their destination.") + self.RetreatTimer:Stop() + self.travelToStored = nil + self:EndRetreat(self.Group:GetAverageCoordinate(), self.retreatDestination) + self.retreatDestination = nil + if self.DespawnAfterRetreat then + self.Group:Destroy() + self:_DeadResponse(false) + else + if self.UseDetection == true then + self.TimeDetectionActive = true + self.TimeDetectionStamp = timer.getAbsTime() + end + end + elseif speedGroup < 1 and self.FixStuck or self.countStuck > 0 then -- Attempt to get unstuck if it's stopped before arriving at the destination. + self.countStuck = self.countStuck + 1 + self:T("TACTICS: "..self.Groupname.." is stopped when it should be moving. Check #"..self.countStuck) + if self.countStuck == 1 then -- Initial unstuck check + self.stuckPosit = self.Group:GetAverageCoordinate() + elseif self.countStuck > 1 and self.countStuck < self.MaxUnstuckAttempts then + local distStuck = coordGroup:Get3DDistance(self.stuckPosit) + if distStuck <= self.UnstuckDist/4 then -- Attempt to get unstuck + if self.lastStuckHeading == nil then + self.lastStuckHeading = (self.stuckPosit:HeadingTo(self.retreatDestination) + self.StuckHeadingIncrements) % 360 + else + self.lastStuckHeading = (self.lastStuckHeading + self.StuckHeadingIncrements) % 360 + end + local unstuckDest = coordGroup:Translate(self.UnstuckDist,self.lastStuckHeading,false,false) + self.Group:RouteGroundTo(unstuckDest, 10, 'Cone', 1) + self:T("TACTICS: "..self.Groupname.." is attempting to get unstuck by driving "..self.UnstuckDist .."m toward heading "..self.lastStuckHeading) + else -- Group has moved enough to resume the route + self:T("TACTICS: "..self.Groupname.." has gotten unstuck. Resuming on the path...") + self.countStuck = 0 + self.stuckPosit = nil + if not self.TroopsAttacking then + if self.RetreatOnRoads then + self.Group:RouteGroundOnRoad(self.retreatDestination, self.RetreatSpeed, 1, self.RetreatFormation) + else + self.Group:RouteGroundTo(self.retreatDestination, self.a, self.RetreatFormation, 1) + end + end + end + elseif self.countStuck >= self.MaxUnstuckAttempts then -- Max stuck attempts exceeded + if self.DestroyStuck then --EITHER Destroy the group + self:E("TACTICS: "..self.Groupname.." has been stuck for too long and was removed from the game...") + self:EndRetreat(self.Group:GetAverageCoordinate(), self.retreatDestination) + self.Group:Destroy() + self:_DeadResponse(false) + else --OR Return the group to Idle + self:E("TACTICS: "..self.Groupname.." has been stuck for too long and was returned to the idle state...") + self.Group:RouteStop() + self.RetreatTimer:Stop() + self.travelToStored = nil + self:EndRetreat(self.Group:GetAverageCoordinate(), self.retreatDestination) + self.retreatDestination = nil + if self.DespawnAfterRetreat then + self.Group:Destroy() + self:_DeadResponse(false) + else + if self.UseDetection == true then + self.TimeDetectionActive = true + self.TimeDetectionStamp = timer.getAbsTime() + end + end + end + end + else + self.countStuck = 0 + self.stuckPosit = nil + end + end + else + self.TimeTravelActive = false + self.stuckPosit = nil + self.lastStuckHeading = nil + self.countStuck = 0 + end + return self +end + +--- [Internal] Cooldown engagement +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_EngageCooldown() + self:T("TACTICS: >> ENGAGE COOLDOWN TIMER << - [GRP: "..self.Groupname.."]") + if not self.Group:GetCoordinate() then + if not self:Is("Dead") then + self:_DeadResponse() + end + else + local unitsRemain, selfRecover = self:CheckUnitLife() + if unitsRemain > 0 then + local isWinchester, isFull = self:CheckAmmo() + if isWinchester and self.AllowRearming then + self:_CallForRearm() + if self.TroopsAttacking then self:_ReturnTroops() end + elseif not selfRecover then + if self.TroopsAttacking then + self:_ReturnTroops() + elseif self.Destination then + self:EngageToTravel() + self:_InitiateTravel() + else + self:EngageToIdle() + end + end + end + end + if self.DrawDataE.DrawText then + trigger.action.removeMark(self.DrawDataE.DrawText) + self.DrawDataE.DrawText = nil + end + if self.DrawDataE.DrawPoly then + trigger.action.removeMark(self.DrawDataE.DrawPoly) + self.DrawDataE.DrawPoly = nil + end + return self +end + +--- [Internal Hold timer +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_HoldTimer() + self:T("TACTICS: >> HOLD POSITION TIMER << - [GRP: "..self.Groupname.."]") + if not self.Group:GetCoordinate() then + if not self:Is("Dead") then + self:_DeadResponse() + end + else + if not self:Is("Engaging") then + self:I("TACTICS: "..self.Groupname.." is no longer holding after spotting a threat.") + if self.ManageAlarmState then + if self.DefaultAlarmState == "Auto" then + self.Group:OptionAlarmStateAuto() + elseif self.DefaultAlarmState == "Red" then + self.Group:OptionAlarmStateRed() + elseif self.DefaultAlarmState == "Green" then + self.Group:OptionAlarmStateGreen() + end + end + if self.TroopsAttacking then + self:_ReturnTroops() + elseif self.Destination then + self:HoldToTravel() + self:_InitiateTravel() + else + self:HoldToIdle() + end + end + end + return self +end + +--- [Internal Attacktimer +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_AttackTimer() + self:T("TACTICS: >> ATTACK TIMER << - [GRP: "..self.Groupname.."]") + self.attackLastCoord = nil + if not self.Group:GetCoordinate() then + if not self:Is("Dead") then + self:_DeadResponse() + end + else + if self:Is("Attacking") then + self:I("TACTICS: "..self.Groupname.." does not see their target anymore and will stop attacking.") + if self:Is("Attacking") then + if self.TroopsAttacking then + self:_ReturnTroops() + elseif self.Destination then + self:AttackToTravel() + self:_InitiateTravel() + else + self.Group:RouteStop() + self:AttackToIdle() + end + end + if self.ManageAlarmState then + if self.DefaultAlarmState == "Auto" then + self.Group:OptionAlarmStateAuto() + elseif self.DefaultAlarmState == "Red" then + self.Group:OptionAlarmStateRed() + elseif self.DefaultAlarmState == "Green" then + self.Group:OptionAlarmStateGreen() + end + end + end + end + return self +end + +--- [Internal EXTERNAL TIMER: Despawn Rearm Group +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_RearmingGroupDespawn() + self.RearmingGroupDespawnTimer = TIMER:New(function() + self:T({"TACTICS: >> REARM GROUP DESPAWN TIMER <<",GroupName = self.Groupname}) + if self.RearmGroup and self.RearmGroup:GetCoordinate() then + self:I("TACTICS: checking if "..self.RearmGroup:GetName().." can despawn.") + local speedGroup = self.RearmGroup:GetVelocityKMH() + if speedGroup < 1 then + self:I("TACTICS: removing "..self.RearmGroup:GetName().." from the simulation") + self.RearmGroup:Destroy() + self.RearmingGroupDespawnTimer:Stop() + self.RearmingGroupDespawnTimer = nil + end + else + self.RearmingGroupDespawnTimer:Stop() + self.RearmingGroupDespawnTimer = nil + end + end) + self.RearmingGroupDespawnTimer:Start(60,60) + return self +end + + + + +--------[INTERNAL METHODS]--------- + +--- [Internal Initiate Travel +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_InitiateTravel() + if self.Destination then + self:T({"TACTICS TRAVEL INITIATED:", Groupname = self.Groupname, UseRoads = self.travelToStored.UseRoads, ToCoordinate = self.travelToStored.ToCoordinate, Speed = self.travelToStored.Speed, Formation = self.travelToStored.Formation, DelaySeconds = self.travelToStored.DelaySeconds, WaypointFunction = self.travelToStored.WaypointFunction, WaypointFunctionArguments = self.travelToStored.WaypointFunctionArguments, CurrentState = self:GetState()}) + if self.travelToStored.UseRoads then + self.Group:RouteGroundOnRoad(self.travelToStored.ToCoordinate, self.travelToStored.Speed, self.travelToStored.DelaySeconds, self.travelToStored.Formation, self.travelToStored.WaypointFunction, self.travelToStored.WaypointFunctionArguments) + else + self.Group:RouteGroundTo(self.travelToStored.ToCoordinate, self.travelToStored.Speed, self.travelToStored.Formation, self.travelToStored.DelaySeconds, self.travelToStored.WaypointFunction, self.travelToStored.WaypointFunctionArguments) + end + if self:Is("Idle") then + self:StartTravel(self.Group:GetAverageCoordinate(), self.Destination) + elseif self:Is("Travelling") then + self:Redirect(self.Group:GetAverageCoordinate(), self.Destination) + end + self.TimeTravelActive = true + self.TimeTravelStamp = timer.getAbsTime() + else + self:E("TACTICS ERROR: "..self.Groupname.." attempted to travel somewhere but doesn't have a destination!") + end + return self +end + +--- [Internal Initiate Engagements +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_InitEngagement(WasHit,EventData) + self:T({"TACTICS: "..self.Groupname.." started engaging something.",WasHit,EventData}) + local UnitName = EventData.IniDCSUnitName + if not WasHit then UnitName = EventData.TgtDCSUnitName end + local WeaponName = EventData.WeaponName + local coordGroup = self.Group:GetAverageCoordinate() + if not coordGroup then coordGroup = self.Group:GetCoordinate() end + if coordGroup then + local unitEnemy = UNIT:FindByName(UnitName) + local coordEnemy = nil + if unitEnemy then coordEnemy = unitEnemy:GetCoordinate() end + local targetCoord = coordEnemy + if not targetCoord then targetCoord = coordGroup end + + --Deploy troops if able + if not self.TroopsAttacking and self.TroopTransport then + self:DropTroops(targetCoord,true) + end + + --Disperse if able + if self.DisperseOnShoot and not WasHit or self.DisperseOnHit and WasHit then + self:T("TACTICS: "..self.Groupname.." will attempt to disperse.") + local headingGroup = self.Group:GetHeading() + if headingGroup then + local roadside = math.random(2) + if roadside == 1 then headingGroup = headingGroup + 45 else headingGroup = headingGroup - 45 end + if headingGroup < 1 then headingGroup = headingGroup + 360 elseif headingGroup > 359 then headingGroup = headingGroup - 360 end + local coordEvade = coordGroup:Translate(headingGroup, self.EvadeDistance, false, false) + if not coordEvade:IsInFlatArea(25, self.InclineLimit) or coordEvade:IsSurfaceTypeWater() or coordEvade:IsSurfaceTypeShallowWater() then + if roadside == 2 then headingGroup = headingGroup + 45 else headingGroup = headingGroup - 45 end + if headingGroup < 1 then headingGroup = headingGroup + 360 elseif headingGroup > 359 then headingGroup = headingGroup - 360 end + coordEvade:Translate(headingGroup, self.EvadeDistance, false, true) + end + if coordEvade:IsInFlatArea(25, self.InclineLimit) and not coordEvade:IsSurfaceTypeWater() and not coordEvade:IsSurfaceTypeShallowWater() then + self:T({"TACTICS: "..self.Groupname.." is dispersing.",headingGroup,self.EvadeDistance}) + self.Group:RouteGroundTo(coordEvade, 20, 'Cone', 1) + TIMER:New(function() + if not self:Is("Retreating") then self.Group:RouteStop() end + end):Start(15) + else + self:T("TACTICS: "..self.Groupname.." could not disperse due to unsuitable terrain.") + self.Group:RouteStop() + end + end + elseif self.Is("Traveling") then + self.Group:RouteStop() + end + + --Interrupt rearming if required + if self.IsWinchester then + self.IsWinchester = false + if self.IsRearming then + self.IsRearming = false + self.TimeRearmActive = false + if self.RearmGroup and self.RearmGroup:GetCoordinate() then + self:_RTBRearmGroup() + end + if self.DrawDataR.DrawText then + trigger.action.removeMark(self.DrawDataR.DrawText) + self.DrawDataR.DrawText = nil + end + if self.DrawDataR.DrawPoly then + trigger.action.removeMark(self.DrawDataR.DrawPoly) + self.DrawDataR.DrawPoly = nil + end + end + end + + --Draw on map if able + if self.EngageDrawing then + if self.DrawDataE.DrawText then + trigger.action.removeMark(self.DrawDataE.DrawText) + end + if self.DrawDataE.DrawPoly then + trigger.action.removeMark(self.DrawDataE.DrawPoly) + end + + self.DrawDataE.DrawText = coordGroup:TextToAll(self.DrawDataE.Text,self.DrawDataE.Coalition,self.DrawDataE.Color1,self.DrawDataE.Alpha1,self.DrawDataE.Color2,self.DrawDataE.Alpha2,self.DrawDataE.Font,false) + self.DrawDataE.DrawPoly = coordGroup:CircleToAll(self.DrawDataE.Radius,self.DrawDataE.Coalition,self.DrawDataE.Color1,self.DrawDataE.Alpha1,self.DrawDataE.Color2,self.DrawDataE.Alpha2/2,self.DrawDataE.Linetype,false) + + if self.DrawDataE.MarkEnemy then + local tempType = 0 + if not coordEnemy then tempType = 1 end + self:_ContactMark(targetCoord,tempType) + end + end + + self:StartEngaging(WasHit, UnitName, WeaponName, EventData) + end + return self +end + +--- [Internal Dead unit response +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_DeadResponse(EventData) + if not self:Is("Dead") then + if EventData then + self:UnitLost(EventData) + self:T("TACTICS: "..self.Groupname.." lost a unit.") + if self.SupportLevel <= 4 and not self.TimeSupportActive and self.Group:GetCoordinate() and self.AllowSupportRequests then + self:RequestSupport() + end + end + if self.Group:GetUnits() == nil or #self.Group:GetUnits() <= 0 then + self:I("TACTICS: "..self.Groupname.." has died or was removed from the sim.") + if self.MasterTime and self.MasterTime:IsRunning() then self.MasterTime:Stop() end + + --Garbage Day + self.MasterTime = nil + self.UnitTable = nil + -- self.setEnemy:FilterStop() + -- self.setEnemy = nil + + if self.DrawDataR.DrawText then + trigger.action.removeMark(self.DrawDataR.DrawText) + self.DrawDataR.DrawText = nil + end + if self.DrawDataR.DrawPoly then + trigger.action.removeMark(self.DrawDataR.DrawPoly) + self.DrawDataR.DrawPoly = nil + end + if self.DrawDataE.DrawText then + trigger.action.removeMark(self.DrawDataE.DrawText) + self.DrawDataE.DrawText = nil + end + if self.DrawDataE.DrawPoly then + trigger.action.removeMark(self.DrawDataE.DrawPoly) + self.DrawDataE.DrawPoly = nil + end + + self:GroupDead(EventData) + elseif self.RetreatAfterLosses and #self.Group:GetUnits() <= (#self.UnitTable * self.RetreatAfterLosses) and not self:Is("Retreating") then + self:Retreat() + end + end + return self +end + +--- [Internal Ammo check +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:CheckAmmo() + self:T("TACTICS: Ammo check called for "..self.Groupname) + local checkUnits = self.Group:GetUnits() + local ammoLow = false + local ammoFull = true + for u = 1, #checkUnits do + for z = 1, #self.UnitTable do + if checkUnits[u]:GetName() == self.UnitTable[z].UnitName then + self:T("TACTICS: Checking "..self.UnitTable[z].UnitName) + local currentAmmo = checkUnits[u]:GetAmmo() + if self.groupAmmo[z] and #self.groupAmmo[z] > 0 then + for i = 1, #self.groupAmmo[z] do + local ammoCount = 0 + if currentAmmo and #currentAmmo > 0 then + for v = 1, #currentAmmo do + if currentAmmo[v]["desc"]["typeName"] == self.groupAmmo[z][i]["desc"]["typeName"] then + ammoCount = currentAmmo[v]["count"] + self:T( "TACTICS: "..self.groupAmmo[z][i]["desc"]["typeName"].." = "..currentAmmo[v]["count"].."/"..self.groupAmmo[z][i]["count"]) + end + end + else + ammoLow = true + end + if ammoCount < (self.groupAmmo[z][i]["count"] * self.LowAmmoPercent) then + ammoLow = true + end + if ammoCount < (self.groupAmmo[z][i]["count"] * self.FullAmmoPercent) or ammoLow then + ammoFull = false + end + end + else + self:T(self.UnitTable[z].UnitName.." does not use ammo.") + end + end + end + end + return ammoLow, ammoFull +end + +--- [Internal Call for rearm +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_CallForRearm(respawnCall) + if respawnCall then + self:I("TACTICS: "..self.Groupname.." requesting respawn of rearm group.") + else + self:I("TACTICS: "..self.Groupname.." is winchester.") + end + + self.IsWinchester = true + + --Manage rearming if able + if self.AllowRearming and not self:Is("Retreating") then + if self.RearmingGroupDespawnTimer and self.RearmingGroupDespawnTimer:IsRunning() then + self.RearmingGroupDespawnTimer:Stop() + self.RearmingGroupDespawnTimer = nil + end + --Dispatch ammo truck if available + local coordGroup = self.Group:GetAverageCoordinate() + if self.RearmGroup and self.RearmGroup:GetCoordinate() then + self:I("TACTICS: Dispatching "..self.RearmGroup:GetName().." to rearm "..self.Groupname) + if self.RearmGroupUseRoads then + self.RearmGroup:RouteGroundOnRoad(coordGroup, self.RearmGroupSpeed, 1, self.RearmGroupFormation) + else + self.RearmGroup:RouteGroundTo(coordGroup, self.RearmGroupSpeed, self.RearmGroupFormation, 1) + end + elseif self.RearmTemplate and self.RearmSpawnCoord then + --Spawn new if able and no truck is available + local spawnIndex = math.random(9999) + local rearmSpawn = SPAWN:NewWithAlias(self.RearmTemplate, self.RearmTemplate..spawnIndex) + rearmSpawn:OnSpawnGroup(function(_grp) + self.RearmGroup = _grp + if self.RearmGroupUseRoads then + _grp:RouteGroundOnRoad(coordGroup, self.RearmGroupSpeed, 1, self.RearmGroupFormation) + else + _grp:RouteGroundTo(coordGroup, self.RearmGroupSpeed, self.RearmGroupFormation, 1) + end + self:I("TACTICS: Spawned and dispatched ".._grp:GetName().." to rearm "..self.Groupname) + end) + rearmSpawn:SpawnFromCoordinate(self.RearmSpawnCoord) + else + self:I("TACTICS: "..self.Groupname.." does not have a rearming group assigned to it. They can mark but will remain stationary until rearmed.") + end + + --Draw on map if able + if self.RearmDrawing then + if self.DrawDataR.DrawText then + trigger.action.removeMark(self.DrawDataR.DrawText) + end + if self.DrawDataR.DrawPoly then + trigger.action.removeMark(self.DrawDataR.DrawPoly) + end + + self.DrawDataR.DrawText = coordGroup:TextToAll(self.DrawDataR.Text,self.DrawDataR.Coalition,self.DrawDataR.Color1,self.DrawDataR.Alpha1,self.DrawDataR.Color2,self.DrawDataR.Alpha2,self.DrawDataR.Font,false) + self.DrawDataR.DrawPoly = coordGroup:CircleToAll(self.DrawDataR.Radius,self.DrawDataR.Coalition,self.DrawDataR.Color1,self.DrawDataR.Alpha1,self.DrawDataR.Color2,self.DrawDataR.Alpha2/2,self.DrawDataR.Linetype,false) + end + + --Group units up if spread out + local unitList = self.Group:GetUnits() + for i = 1,#unitList do + local coordUnit = unitList[i]:GetCoordinate() + if coordUnit:Get3DDistance(coordGroup) >= 300 then + self.Group:RouteGroundTo(coordGroup) + break + end + end + + self.IsRearming = true + self:RequestRearming(self.RearmGroup) + self.TimeRearmActive = true + self.TimeRearmStamp = timer.getAbsTime() + end + self:Winchester() + return self +end + +--- [Internal RTB Rearm Group +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_RTBRearmGroup() + if self.RearmGroup and self.RearmGroup:GetCoordinate() and self.RearmGroupBase and self.RearmGroupRTB then + self:I("TACTICS: Sending "..self.RearmGroup:GetName().." back to base.") + if self.RearmGroupUseRoads then + self.RearmGroup:RouteGroundOnRoad(self.RearmGroupBase, self.RearmGroupSpeed, 1, self.RearmGroupFormation) + else + self.RearmGroup:RouteGroundTo(self.RearmGroupBase, self.RearmGroupSpeed, self.RearmGroupFormation, 1) + end + if self.DespawnRearmingGroup and not self.RearmingGroupDespawnTimer then + self:_RearmingGroupDespawn() + end + end + return self +end + +--- [Internal] Spot target +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_SpotTarget(_enemy,distance) + self:T("TACTICS: "..self.Groupname.." detected "..tostring(_enemy:GetName()).." at a range of "..distance.."m. Current Tactical ROE: "..self.TacticalROE) + if self.ManageAlarmState then self.Group:OptionAlarmStateRed() end + --Attack + if self.TacticalROE == "Attack" then + self:AttackTarget(_enemy,distance) + + --Hold + elseif self.TacticalROE == "Hold" then + if distance <= self.HoldDistance then + if self:Is("Travelling") or self:Is("Idle") then + self.Group:RouteStop() + self:StartHold() + end + --Deploy troops if able + if not self.TroopsAttacking and self.TroopTransport and _enemy:GetCoordinate() then + self:DropTroops(_enemy:GetCoordinate(),true) + end + self.TimeHoldActive = true + self.TimeHoldStamp = timer.getAbsTime() + end + + --Avoid + elseif self.TacticalROE == "Avoid" then + if distance <= self.AvoidDistance then + self:AvoidTarget(_enemy,distance) + end + + --Ignore + else + if self.ManageAlarmState then + TIMER:New(function() + if self.DefaultAlarmState == "Auto" then + self.Group:OptionAlarmStateAuto() + elseif self.DefaultAlarmState == "Red" then + self.Group:OptionAlarmStateRed() + elseif self.DefaultAlarmState == "Green" then + self.Group:OptionAlarmStateGreen() + end + end):Start(self.DetectionRate-1) + end + end + + if self.SupportLevel <= 1 and not self.TimeSupportActive and self.AllowSupportRequests then + self:RequestSupport(_enemy:GetCoordinate()) + end + return self +end + +--- [Internal Attack a target +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:AttackTarget(_enemy,distance) + local coordGroup = self.Group:GetAverageCoordinate() + local coordEnemy = nil + local hdgEnemy = 0 + local className = _enemy:GetClassName() + if className == "COORDINATE" then + self:T("TACTICS: "..self.Groupname.." is attacking a provided coordinate "..distance.."m away.") + coordEnemy = _enemy + elseif not className then + self:E("TACTICS ERROR: The provided enemy is not a valid Core.COORDINATE or Wrapper.POSITIONABLE object.") + else + self:T("TACTICS: "..self.Groupname.." is attacking "..tostring(_enemy:GetName()).." "..distance.."m away.") + coordEnemy = _enemy:GetCoordinate() + end + + if coordEnemy and coordGroup then + hdgEnemy = coordGroup:HeadingTo(coordEnemy) + local coordTargetDest = nil + local skipNewDest = false + if self.attackLastCoord and self.attackLastCoord:Get3DDistance(coordEnemy) <= self.LastTargetThreshold then + skipNewDest = true + local tempDist = self.attackLastCoord:Get3DDistance(coordEnemy) + self:T("TACTICS: "..self.Groupname.."'s target is only "..tempDist.."m away from where it was last. Threshold set to "..self.LastTargetThreshold) + end + + --Get attack move-to position + if distance > 25 then + local hdgTgt = coordGroup:HeadingTo(coordEnemy) + coordTargetDest = coordEnemy:Translate(-self.AttackPositionDist,hdgTgt,false,false) + if distance < self.AttackPositionDist+20 then + coordTargetDest = coordEnemy:Translate(-(distance/2),hdgTgt,false,false) + skipNewDest = false + end + local booIsFlat = false + local checkFlat = 0 + if not skipNewDest then + while not booIsFlat and checkFlat < 10 do + checkFlat = checkFlat + 1 + if coordTargetDest:IsInFlatArea(25, self.InclineLimit) and not coordTargetDest:IsSurfaceTypeWater() and not coordTargetDest:IsSurfaceTypeShallowWater() then + if distance < self.CombatDistance then + self.Group:RouteGroundTo(coordTargetDest, self.AttackSpeed, self.AttackFormation, 1) + else + if self.AttackFarUsesRoads then self.Group:RouteGroundOnRoad(coordTargetDest, self.AttackSpeedFar, 1, self.AttackFormationFar) + else self.Group:RouteGroundTo(coordTargetDest, self.AttackSpeedFar, self.AttackFormationFar, 1) end + end + booIsFlat = true + checkFlat = 10 + else + coordTargetDest:Translate(25,(hdgTgt+90) % 360 ,false,true) + end + end + if not booIsFlat then + coordTargetDest = coordEnemy:Translate(-self.AttackPositionDist,hdgTgt,false,false) + if distance < self.AttackPositionDist+20 then coordTargetDest = coordEnemy:Translate(-(distance/2),hdgTgt,false,false) end + if distance < self.CombatDistance then + self.Group:RouteGroundTo(coordTargetDest, self.AttackSpeed, self.AttackFormation, 1) + else + if self.AttackFarUsesRoads then self.Group:RouteGroundOnRoad(coordTargetDest, self.AttackSpeedFar, 1, self.AttackFormationFar) + else self.Group:RouteGroundTo(coordTargetDest, self.AttackSpeedFar, self.AttackFormationFar, 1) end + end + end + end + else + --Move both groups toward a convergence point + if className == "GROUP" or className == "UNIT" then + self:T("TACTICS: "..self.Groupname.." and "..tostring(_enemy:GetName()).." are too close without engaging (>25m). Attempting to move both groups.") + local coordTargetDest = coordEnemy:Translate(25,90,false,false) + if not coordTargetDest:IsInFlatArea(25, self.InclineLimit) or coordTargetDest:IsSurfaceTypeWater() or coordTargetDest:IsSurfaceTypeShallowWater() then + coordTargetDest = coordEnemy:Translate(25,-90 ,false,false) + end + if not coordTargetDest:IsInFlatArea(25, self.InclineLimit) or coordTargetDest:IsSurfaceTypeWater() or coordTargetDest:IsSurfaceTypeShallowWater() then + coordTargetDest = coordEnemy:Translate(25,0 ,false,false) + end + if not coordTargetDest:IsInFlatArea(25, self.InclineLimit) or coordTargetDest:IsSurfaceTypeWater() or coordTargetDest:IsSurfaceTypeShallowWater() then + coordTargetDest = coordEnemy:Translate(25,180 ,false,false) + end + self.Group:RouteGroundTo(coordTargetDest, self.AttackSpeed, self.AttackFormation, 1) + _enemy:RouteGroundTo(coordTargetDest, self.AttackSpeed, self.AttackFormation, 1) + end + end + + --Deploy Troops If Able + if not self.TroopsAttacking and self.TroopTransport and distance <= self.TroopAttackDist then + self:T("TACTICS: "..self.Groupname.." is deploying troops to attack "..tostring(_enemy:GetName())) + self.Group:RouteStop() + self:DropTroops(coordEnemy,true) + TIMER:New(function() + self.Group:RouteResume() + end):Start(5) + end + + self.attackLastCoord = coordEnemy + self.TimeAttackActive = true + self.TimeAttackStamp = timer.getAbsTime() + self:AttackManeuver(_enemy,distance,hdgEnemy) + if not self:Is("Attacking") then self:StartAttack(_enemy,distance,hdgEnemy) end + end + return self +end + +--- [Internal Avoid target +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:AvoidTarget(_enemy,distance) + local coordGroup = self.Group:GetAverageCoordinate() + local coordEnemy = nil + local className = _enemy:GetClassName() + if className == "COORDINATE" then + self:T("TACTICS: "..self.Groupname.." is avoiding a provided coordinate "..distance.."m away.") + coordEnemy = _enemy + elseif not className then + self:E("TACTICS ERROR: The provided enemy is not a valid Core.COORDINATE or Wrapper.POSITIONABLE object.") + else + self:T("TACTICS: "..self.Groupname.." is avoiding "..tostring(_enemy:GetName()).." "..distance.."m away.") + coordEnemy = _enemy:GetCoordinate() + end + local isAvoiding = false + + if coordGroup and coordEnemy then + self:T("TACTICS: "..self.Groupname.." will try to avoid "..tostring(_enemy:GetName())) + for i = 1,100 do + local hdgAvoid = coordEnemy:HeadingTo(coordGroup) + if self.AvoidAzimuthMax then + hdgAvoid = ((hdgAvoid - self.AvoidAzimuthMax) + (math.random(self.AvoidAzimuthMax*2))) % 360 + if hdgAvoid < 1 then hdgAvoid = hdgAvoid + 360 end + end + local avoidPoint = coordGroup:Translate(self.AvoidDistance, hdgAvoid, false, false) + if avoidPoint:IsInFlatArea(25, self.InclineLimit) and not avoidPoint:IsSurfaceTypeWater() and not avoidPoint:IsSurfaceTypeShallowWater() then + isAvoiding = true + if self.AvoidUseRoads then + self.Group:RouteGroundOnRoad(avoidPoint, self.AvoidSpeed, 1, self.AvoidFormation) + self.avoidDest = avoidPoint + self.TimeAvoidActive = true + self.TimeAvoidStamp = timer.getAbsTime() + else + self.Group:RouteGroundTo(avoidPoint, self.AvoidSpeed, self.AvoidFormation, 1) + self.avoidDest = avoidPoint + self.TimeAvoidActive = true + self.TimeAvoidStamp = timer.getAbsTime() + end + if self:Is("Travelling") or self:Is("Idle") then self:StartAvoid() end + break + end + end + end + if not isAvoiding then + self:E("TACTICS ERROR: "..self.Groupname.." tried to avoid ".._enemy:GetName().." but could not find a destination with suitable terrain after 100 attempts.") + end +end + +--- [Internal Drop troops +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:DropTroops(_targetCoord,_attacking,_limit) + self:T("TACTICS: "..self.Groupname.." called dropTroops. Attacking = "..tostring(_attacking)) + local coordGroup = self.Group:GetCoordinate() + local hdgGroup = self.Group:GetHeading() + + --Check if enemy is close enough to attack + local distTarget = _targetCoord:Get3DDistance(coordGroup) + local hdgTarget = nil + local coordAttack = nil + + if distTarget <= self.TroopAttackDist and _attacking then + self:T("TACTICS: "..self.Groupname.." dropped troops are in range and will attack the target.") + hdgTarget = coordGroup:HeadingTo(_targetCoord) + coordAttack = coordGroup:Translate(distTarget-20,hdgTarget,false,false) + elseif _attacking then + self:T("TACTICS: "..self.Groupname.." dropped troops are too far from the target and will disperse instead.") + else + coordAttack = _targetCoord + end + + --Get spawn parameters + local intSpawnIndex = math.random(1,9999) + local nameDroppedTroops = self.TroopTemplate.."_"..self.Groupname..intSpawnIndex + local troopsOffset = math.random(self.TroopDismountDistMin,self.TroopDismountDistMax) + local directionX = math.random(1,100) + if directionX > 50 then + troopsOffset = -troopsOffset + end + + --Set up troop spawn + local spawnDropTroops = SPAWN:NewWithAlias(self.TroopTemplate, nameDroppedTroops):InitGroupHeading(hdgGroup) + spawnDropTroops:OnSpawnGroup(function(_troops) + local coordMoveTo = nil + if coordAttack then coordMoveTo = coordAttack:GetCoordinate() end + if coordMoveTo and _attacking then + local headingFlank = 0 + local checkFlank = math.random(0,2) + if checkFlank == 0 then headingFlank = hdgTarget-90 elseif checkFlank == 2 then headingFlank = hdgTarget+90 end + if headingFlank < 0 then headingFlank = headingFlank+360 elseif headingFlank > 360 then headingFlank = headingFlank-360 end + if checkFlank ~= 1 then + coordMoveTo:Translate(100,headingFlank,false,true) + coordMoveTo:Translate(40,hdgTarget,false,true) + end + elseif _attacking then + coordMoveTo = _troops:GetUnit(1):GetOffsetCoordinate(0,0,troopsOffset) + elseif not _attacking then + coordMoveTo = _targetCoord + end + _troops:RouteGroundTo(coordMoveTo,self.TroopMoveSpeed,self.TroopFormation) + if _attacking then + table.insert(self.AttackingTroops,_troops) + self.TroopsAttacking = true + else + table.insert(self.DeployedTroops,_troops) + end + self:T("TACTICS: "..self.Groupname.." deployed ".._troops:GetName()) + end) + + --Timer to drop troops + local timerDropTroops = TIMER:New(function() + coordGroup = self.Group:GetCoordinate() + local unitTable = self.Group:GetUnits() + local dropAmount = #unitTable + if _limit then dropAmount = _limit end + for i = 1, dropAmount do + local coordDrop = unitTable[i]:GetOffsetCoordinate(-10,0,0) + spawnDropTroops:SpawnFromCoordinate(coordDrop) + end + self:TroopsDropped(_targetCoord,_attacking) + end) + timerDropTroops:Start(5) + return self +end + +--- [Internal Return troops +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_ReturnTroops() + self:I("TACTICS: "..self.Groupname.." attempting to recover deployed troops.") + self.Group:RouteStop() + + local homeCoord = self.Group:GetAverageCoordinate() + if not homeCoord then homeCoord = self.Group:GetCoordinate() end + local countTroops = 0 + + --Function to see if all troops have boarded + local function checkAllReturned() + if not self:Is("Dead") then + self:T("TACTICS: "..self.Groupname.."'s troops remaining to be extracted = "..tostring(countTroops)) + if countTroops == 0 then + self.AttackingTroops = {} + self.TroopsAttacking = false + self:I("TACTICS: "..self.Groupname.." picked up all deployed troops and will resume tasking.") + local isWinchester, isFull = self:CheckAmmo() + if isWinchester and self.AllowRearming and not self.IsRearming then + self:_CallForRearm() + end + if self:Is("Retreating") then + if self.RetreatOnRoads then + self.Group:RouteGroundOnRoad(self.retreatDestination, self.RetreatSpeed, 1, self.RetreatFormation) + self.TimeRetreatActive = true + self.TimeRetreatStamp = timer.getAbsTime() + else + self.Group:RouteGroundTo(self.retreatDestination, self.a, self.RetreatFormation, 1) + self.TimeRetreatActive = true + self.TimeRetreatStamp = timer.getAbsTime() + end + elseif not self.IsRearming then + if self.Destination then + if self:Is("Engaging") then self:EngageToTravel() + elseif self:Is("Attacking") then self:AttackToTravel() + elseif self:Is("Holding") then self:HoldToTravel() + elseif self:Is("Idle") then self:StartTravel() end + self:_InitiateTravel() + else + if self:Is("Engaging") then self:EngageToIdle() + elseif self:Is("Holding") then self:HoldToIdle() + elseif self:Is("Attacking") then self:AttackToIdle()end + end + end + self:TroopsReturned() + end + end + end + + --Send troops back to group position + if #self.AttackingTroops == 0 then + self:I("TACTICS: All of "..self.Groupname.."'s troops are KIA!") + checkAllReturned() + else + for i = #self.AttackingTroops,1,-1 do + local troopGroup = self.AttackingTroops[i] + if troopGroup:GetCoordinate() then + self:T("TACTICS: "..self.Groupname.." is recovering "..troopGroup:GetName()) + local despawn = {} + troopGroup:RouteGroundTo(homeCoord,self.TroopMoveSpeed,self.TroopFormation) + + --Check to despawn troops + local timerLimit = math.ceil(self.ExtractTimeLimit/20) + despawn.Timer = TIMER:New(function() + timerLimit = timerLimit - 1 + self:T("TACTICS: "..troopGroup:GetName().." recovery limit check = "..timerLimit.."/"..math.ceil(self.ExtractTimeLimit/20)) + local checkCoord = troopGroup:GetCoordinate() + local checkHome = self.Group:GetCoordinate() + if checkCoord and checkHome then + local distHome = checkCoord:Get3DDistance(homeCoord) + if distHome <= 25 then + despawn.Timer:Stop() + troopGroup:Destroy() + countTroops = countTroops - 1 + checkAllReturned() + end + else + despawn.Timer:Stop() + countTroops = countTroops - 1 + checkAllReturned() + end + if despawn.Timer:IsRunning() and timerLimit <= 0 then + despawn.Timer:Stop() + troopGroup:Destroy() + countTroops = countTroops - 1 + checkAllReturned() + end + end) + despawn.Timer:Start(20,20) + else + table.remove(self.AttackingTroops,i) + end + countTroops = #self.AttackingTroops + end + end + + --Backup in the event of a failure + TIMER:New(function() + if countTroops > 0 then + for i = 1, #self.AttackingTroops do + self.AttackingTroops[i]:Destroy() + end + countTroops = 0 + checkAllReturned() + end + end):Start(self.ExtractTimeLimit+5) + return self +end + +--- [Internal Exctract Troops +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:ExtractTroops() + if not self:Is("Retreating") then + self:I("TACTICS: "..self.Groupname.." attempting to extract troops from the field.") + self.Group:RouteStop() + + local homeCoord = self.Group:GetAverageCoordinate() + if not homeCoord then homeCoord = self.Group:GetCoordinate() end + local countTroops = 0 + + --Function to see if all troops have boarded + local function checkAllReturned() + self:T("TACTICS: "..self.Groupname.."'s troops remaining to be extracted = "..tostring(countTroops)) + if countTroops == 0 then + self.DeployedTroops = {} + self:I("TACTICS: "..self.Groupname.." picked up all deployed troops and will resume tasking.") + if self:Is("Retreating") then + if self.RetreatOnRoads then + self.Group:RouteGroundOnRoad(self.retreatDestination, self.RetreatSpeed, 1, self.RetreatFormation) + self.TimeRetreatActive = true + self.TimeRetreatStamp = timer.getAbsTime() + else + self.Group:RouteGroundTo(self.retreatDestination, self.a, self.RetreatFormation, 1) + self.TimeRetreatActive = true + self.TimeRetreatStamp = timer.getAbsTime() + end + elseif not self.IsRearming then + if self.Destination then + if self:Is("Engaging") then self:EngageToTravel() + elseif self:Is("Attacking") then self:AttackToTravel() + elseif self:Is("Idle") then self:StartTravel() end + self:_InitiateTravel() + else + if self:Is("Engaging") then self:EngageToIdle() + elseif self:Is("Attacking") then self:AttackToIdle()end + end + end + self:TroopsExtracted() + end + end + + --Send troops back to group position + if #self.DeployedTroops == 0 then + self:I("TACTICS: All of "..self.Groupname.."'s deployed troops are KIA!") + checkAllReturned() + else + for i = #self.DeployedTroops,1,-1 do + local troopGroup = self.DeployedTroops[i] + if troopGroup:GetCoordinate() then + local despawn = {} + troopGroup:RouteGroundTo(homeCoord,self.TroopMoveSpeed,self.TroopFormation) + + --Check to despawn troops + local timerLimit = math.ceil(self.ExtractTimeLimit/20) + despawn.Timer = TIMER:New(function() + timerLimit = timerLimit - 1 + local checkCoord = troopGroup:GetCoordinate() + local checkHome = self.Group:GetCoordinate() + if checkCoord and checkHome then + local distHome = checkCoord:Get3DDistance(homeCoord) + if distHome <= 25 then + despawn.Timer:Stop() + troopGroup:Destroy() + countTroops = countTroops - 1 + checkAllReturned() + end + else + despawn.Timer:Stop() + countTroops = countTroops - 1 + checkAllReturned() + end + if despawn.Timer:IsRunning() and timerLimit <= 0 then + despawn.Timer:Stop() + troopGroup:Destroy() + countTroops = countTroops - 1 + checkAllReturned() + end + end) + despawn.Timer:Start(20,20) + else + table.remove(self.DeployedTroops,i) + end + countTroops = #self.DeployedTroops + end + end + + --Backup in the event of a failure + TIMER:New(function() + if countTroops > 0 then + for i = 1, #self.DeployedTroops do + self.DeployedTroops[i]:Destroy() + end + countTroops = 0 + checkAllReturned() + end + end):Start(self.ExtractTimeLimit+5) + return self + else + self:E("TACTICS ERROR: "..self.Groupname.." cannot extract troops while retreating.") + end +end + +--- [Internal Contact marks +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:_ContactMark(_coord,_type) + + --Get coordinates for the correct shape + local coordQuad1 = _coord:Translate(200,359,false,false) + local coordQuad2 = _coord:Translate(200,1,false,false) + local coordQuad3 = _coord:Translate(200,120,false,false) + local coordQuad4 = _coord:Translate(200,240,false,false) + if _type == 1 then + coordQuad1 = _coord:Translate(200,179,false,false) + coordQuad2 = _coord:Translate(200,181,false,false) + coordQuad3 = _coord:Translate(200,300,false,false) + coordQuad4 = _coord:Translate(200,60,false,false) + end + if _type == 2 then + coordQuad1 = _coord:Translate(200,0,false,false) + coordQuad2 = _coord:Translate(200,90,false,false) + coordQuad3 = _coord:Translate(200,180,false,false) + coordQuad4 = _coord:Translate(200,270,false,false) + end + + --Get coalition params + local coalitionNum = 2 + local coalitionColorA = {1,0,0} + local coalitionColorB = {0.5,0,0} + if self.groupCoalition == coalition.side.RED then + coalitionColorA = {0,0,1} + coalitionColorB = {0,0,0.5} + coalitionNum = 1 + end + + --Get timeout values + local timeoutFresh = self.EngageDrawingFresh + local timeoutStale = self.EngageDrawingStale + if _type == 2 then + timeoutFresh = self.DetectionRate-1 + timeoutStale = self.EnemySpotStale + end + + --Draw marker + if self.markSpot then trigger.action.removeMark(self.markSpot) end + self.markSpot = coordQuad1:QuadToAll(coordQuad2, coordQuad3, coordQuad4, coalitionNum,coalitionColorA,1,coalitionColorA,0.15,1,true) + if self.markSpotTimer and self.markSpotTimer:IsRunning() then + self.markSpotTimer:Stop() + self.markSpotTimer = nil + end + self.markSpotTimer = TIMER:New(function() + trigger.action.removeMark(self.markSpot) + self.markSpot = coordQuad1:QuadToAll(coordQuad2, coordQuad3, coordQuad4, coalitionNum,coalitionColorB,1,coalitionColorB,0.15,3,true) + self.markSpotTimer = nil + self.markSpotTimer = TIMER:New(function() + trigger.action.removeMark(self.markSpot) + self.markSpot = nil + self.markSpotTimer = nil + end):Start(timeoutStale) + end):Start(timeoutFresh) + return self +end + +--- [Internal Request Support +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:RequestSupport(target) + local targetName = "nil target" + local coordGroup = self.Group:GetCoordinate() + local coordTarget = coordGroup + local unitTarget = nil + if target and target:GetClassName() == "COORDINATE" then + coordTarget = target + elseif target and target:GetClassName() == "UNIT" then + coordTarget = target:GetCoordinate() + unitTarget = target + targetName = target:GetName() + end + self:T({"TACTICS: "..self.Groupname.." requested support.","Target = "..tostring(targetName)}) + if coordGroup then + self:T2("TACTICS: "..self.Groupname.." will search for nearby friendlies.") + local countGroups = 0 + local friendlyTable = TACTICS_UTILS.GroupsRed + if self.groupCoalition == coalition.side.BLUE then friendlyTable = TACTICS_UTILS.GroupsBlue end + for i = 1,#friendlyTable do + self:T2("TACTICS: Group table index "..i.." for coalition "..tostring(self.groupCoalition)) + local coordFriendly = friendlyTable[i].Group:GetCoordinate() + if coordFriendly then + self:T({"TACTICS: "..self.Groupname.." is checking if "..friendlyTable[i].Groupname.." can provide support.",RespondToSupport = friendlyTable[i].RespondToSupport, State = friendlyTable[i]:GetState()}) + if friendlyTable[i].Groupname ~= self.Groupname and friendlyTable[i].RespondToSupport then + if friendlyTable[i]:Is("Idle") or friendlyTable[i]:Is("Travelling") then + local distFriendly = coordFriendly:Get3DDistance(coordGroup) + self:T("TACTICS: "..friendlyTable[i].Groupname.." is "..distFriendly.."m / "..self.SupportRadius.."m from "..self.Groupname) + if distFriendly <= self.SupportRadius then + local distTarget = coordFriendly:Get3DDistance(coordTarget) + friendlyTable[i]:AttackTarget(coordTarget,distTarget) + table.insert(self.supportTable,friendlyTable[i]) + countGroups = countGroups + 1 + self:I("TACTICS: "..friendlyTable[i].Groupname.." is moving to support "..self.Groupname) + friendlyTable[i]:Supporting(self.Group,coordTarget,self.MessageCallsign) + if self.SupportGroupLimit and countGroups >= self.SupportGroupLimit then + self:I("TACTICS: "..self.Groupname.." reached the support group limit: "..self.SupportGroupLimit) + break + end + end + end + end + end + end + self.TimeSupportActive = true + self.TimeSupportStamp = timer.getAbsTime() + self:SupportRequested(coordTarget,unitTarget) + end + return self +end + +--- [Internal Check Unit Health +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:CheckUnitLife() + local unitsRemain = 0 + local willRecover = false + local unitList = self.Group:GetUnits() + if unitList and #unitList > 0 then + self:T({"TACTICS: Checking health of all units in "..self.Groupname}) + for i = 1,#unitList do + unitsRemain = unitsRemain + 1 + local unitLife = unitList[i]:GetLifeRelative() + self:T({"TACTICS: "..unitList[i]:GetName(),"RelativeLife = "..unitLife.."/"..self.AbandonHealth,GroupUnitCount = unitsRemain}) + if unitLife < self.AbandonHealth then + unitsRemain = unitsRemain - 1 + if self.AllowSelfRecover then willRecover = true end + self:T({"TACTICS: "..unitList[i]:GetName().." is below the life threshold. Crew will abandon vehicle.",GroupUnitCount = unitsRemain,SelfRecover = willRecover}) + if self:Is("Retreating") then + self.Group:RouteStop() + TIMER:New(function() + self:AbandonVehicle(unitList[i]) + end):Start(5) + else + self:AbandonVehicle(unitList[i]) + end + end + end + end + return unitsRemain,willRecover +end + +--- [Internal Abandon vehicle +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:AbandonVehicle(Unit) + local coordUnit = Unit:GetCoordinate() + local headingUnit = Unit:GetHeading() + local healthUnit = Unit:GetLife() + local nameUnit = Unit:GetName() + local countryUnit = Unit:GetCountry() + local typeUnit = Unit:GetTypeName() + local crewPosition = coordUnit:Translate(5,(headingUnit + 90)%360,false,false) + Unit:Destroy() + self:T({"TACTICS: "..nameUnit.." is being abandoned."}) + self:_DeadResponse(false) + + --Crew spawning logic + local spawnCrew = SPAWN:NewWithAlias(self.CrewTemplate,"Crew_"..nameUnit) + spawnCrew:OnSpawnGroup(function(_grp) + self:T({"TACTICS: ".._grp:GetName().." spawned. Checking next step.", CanRecover = self.AllowSelfRecover}) + local canRecover = false + local crewName = _grp:GetName() + + --Check if group can self recover + if self.Group:GetCoordinate() and self.AllowSelfRecover then + local groupUnits = self.Group:GetUnits() + local closestDist = 99999999 + local closestCoord = nil + local closestUnit = nil + local closestUnitName = "invalid" + for i = 1,#groupUnits do + local coordNearby = groupUnits[i]:GetCoordinate() + local distNearby = coordNearby:Get3DDistance(crewPosition) + if distNearby < closestDist then + closestDist = distNearby + closestCoord = coordNearby + closestUnit = groupUnits[i] + canRecover = true + end + end + if canRecover then + self:T({"TACTICS: "..crewName.." will move to "..closestUnitName.." for recovery."}) + _grp:RouteGroundTo(closestCoord,20,"Diamond",1) + local tbl = {} + tbl.recoverTime = TIMER:New(function() + local coordGrp = _grp:GetCoordinate() + local coordRecoverUnit = closestUnit:GetCoordinate() + if coordGrp and coordRecoverUnit then + local distGrp = coordGrp:Get3DDistance(closestCoord) + self:T2({"TACTICS: "..crewName.." is "..distGrp.."/25m from "..closestUnitName}) + if distGrp <= 25 then + self:T({"TACTICS: "..crewName.." is within 25m of "..closestUnitName.." and will board the vehicle.",Distance = distGrp}) + _grp:Destroy() + self:CrewRecovered(closestUnit,nameUnit) + tbl.recoverTime:Stop() + if self.TroopsAttacking then + self:_ReturnTroops() + elseif self.Destination and not self:Is("Retreating") then + self:EngageToTravel() + self:_InitiateTravel() + elseif self:Is("Retreating") then + self.Group:RouteResume() + else + self:EngageToIdle() + end + end + else + self:T({"TACTICS: "..crewName.." was destroyed."}) + tbl.recoverTime:Stop() + if self.TroopsAttacking then + self:_ReturnTroops() + elseif self.Destination and not self:Is("Retreating") then + self:EngageToTravel() + self:_InitiateTravel() + elseif self:Is("Retreating") then + self.Group:RouteResume() + else + self:EngageToIdle() + end + end + end) + tbl.recoverTime:Start(20,20) + end + return self + end + + --Run away and dispatch recovery vehicle if avail + if not canRecover then + local hdgEscape = math.random(90,270) + if self:Is("Retreating") then hdgEscape = math.random(270,450) end + hdgEscape = (hdgEscape + headingUnit)%360 + self:T({"TACTICS: "..crewName.." will run "..self.AbandonDistance.."m away at a heading of "..hdgEscape}) + local coordEscape = coordUnit:Translate(hdgEscape,self.AbandonDistance) + _grp:RouteGroundTo(coordEscape,20,"Diamond",1) + if self.RecoveryVehicle and self.RecoverySpawnZone then + self:T({"TACTICS: "..crewName.." will request a recovery vehicle pickup from their destination."}) + local spawnRecovery = SPAWN:NewWithAlias(self.RecoveryVehicle,"Recovery_"..nameUnit) + spawnRecovery:OnSpawnGroup(function(_veh) + local nameRecovery = _veh:GetName() + local isRTB = false + if self.RecoveryUseRoads then + _veh:RouteGroundOnRoad(coordEscape, self.RecoverySpeed, 1, self.RecoveryFormation) + else + _veh:RouteGroundTo(coordEscape,self.RecoverySpeed,self.RecoveryFormation,1) + end + local tbl = {} + tbl.recoverTimer = TIMER:New(function() + local coordVeh = _veh:GetCoordinate() + local coordGrp = _grp:GetCoordinate() + if coordVeh then + if not isRTB then + if coordGrp then + local distRecover = coordVeh:Get3DDistance(coordGrp) + self:T2({"TACTICS: "..nameRecovery.." is "..distRecover.."/25m from "..crewName}) + if distRecover < 25 then + self:T({"TACTICS: "..crewName.." is within 25m of "..nameRecovery.." and will board the vehicle.",Distance = distRecover}) + _grp:Destroy() + self:CrewRecovered(_veh,nameUnit) + isRTB = true + if self.RecoveryUseRoads then + _veh:RouteGroundOnRoad(self.RecoverySpawnZone:GetCoordinate(), self.RecoverySpeed, 1, self.RecoveryFormation) + else + _veh:RouteGroundTo(self.RecoverySpawnZone:GetCoordinate(),self.RecoverySpeed,self.RecoveryFormation,1) + end + end + else + self:T({"TACTICS: "..crewName.." was destroyed. "..nameRecovery.." is RTB."}) + isRTB = true + if self.RecoveryUseRoads then + _veh:RouteGroundOnRoad(self.RecoverySpawnZone:GetCoordinate(), self.RecoverySpeed, 1, self.RecoveryFormation) + else + _veh:RouteGroundTo(self.RecoverySpawnZone:GetCoordinate(),self.RecoverySpeed,self.RecoveryFormation,1) + end + end + else + if _veh:IsAnyInZone(self.RecoverySpawnZone) then + self:T({"TACTICS: "..nameRecovery.." returned to base and will despawn."}) + _veh:Destroy() + tbl.recoverTimer:Stop() + end + end + else + tbl.recoverTimer:Stop() + end + end) + tbl.recoverTimer:Start(10,10) + self:T({"TACTICS: "..nameRecovery.." spawned and is moving to recover "..crewName,UseRoads = self.RecoveryUseRoads,Speed = self.RecoverySpeed,Formation = self.RecoveryFormation}) + end) + spawnRecovery:SpawnInZone(self.RecoverySpawnZone,true) + end + end + end) + spawnCrew:SpawnFromCoordinate(crewPosition) + + --New static spawn + self:T({"TACTICS: Replacing "..nameUnit.." with a static object."}) + local vec2Unit = coordUnit:GetVec2() + local staticInfo = { + ["heading"] = headingUnit * (math.pi*2) / 360, + ["Country"] = countryUnit, + ["y"] = vec2Unit.y, + ["x"] = vec2Unit.x, + ["name"] = nameUnit.."_Abandoned", + ["category"] = "Ground Identifiable", + ["type"] = typeUnit, + ["dead"] = false, + --["livery_id"] = SaveStatics[k]["livery_id"] + } + coalition.addStaticObject(countryUnit, staticInfo) + if self.SmokeAbandoned then + coordUnit:BigSmokeSmall(0.01) + TIMER:New(function() + coordUnit:StopBigSmokeAndFire() + end):Start(self.StaticSmokeTimeout) + end + self:Abandoned(coordUnit,nameUnit,typeUnit) +end + + +--------[USER METHODS]--------- + +--- [User] Find a group by name +-- @param #TACTICS self +-- @param #string Groupname Name to find +-- @return Wrapper.Group#GROUP Group found +function TACTICS:FindByName(Groupname) + for i = 1,#TACTICS_UTILS.GroupsRed do + if TACTICS_UTILS.GroupsRed[i].Groupname == Groupname then + return TACTICS_UTILS.GroupsRed[i] + end + end + for i = 1,#TACTICS_UTILS.GroupsBlue do + if TACTICS_UTILS.GroupsBlue[i].Groupname == Groupname then + return TACTICS_UTILS.GroupsBlue[i] + end + end + BASE:E("TACTICS ERROR: Could not find a group named "..Groupname.." with Tactics capabilities enabled.") + return nil +end + +--- [User] Get Group Name +-- @param #TACTICS self +-- @return #string Name Name of the group +function TACTICS:GetName() + return self.Groupname +end + + +--- [User] Travel To: Assigns a destination for the group to make its way to +-- @param #TACTICS self +-- @param #boolean UseRoads Set to true to use road connections +-- @param Core.Point#COORDINATE ToCoordinate Coordinate to go to +-- @param #number Speed Speed to set on the group +-- @param #string Formation Formation to be used +-- @param #number DelaySeconds Delay in seconds +-- @param #function WaypointFunction Function to execute at a waypoint +-- @param #table WaypointFunctionArguments Arguments to be passed to the prior function +-- @return #TACTICS self +function TACTICS:TravelTo(UseRoads, ToCoordinate, Speed, Formation, DelaySeconds, WaypointFunction, WaypointFunctionArguments) + if not self:Is("Dead") then + self.Destination = ToCoordinate + self.travelToStored = {UseRoads = UseRoads, ToCoordinate = ToCoordinate, Speed = Speed, Formation = Formation, DelaySeconds = DelaySeconds, WaypointFunction = WaypointFunction, WaypointFunctionArguments = WaypointFunctionArguments} + if self:Is("Idle") or self:Is("Travelling") then + self:_InitiateTravel() + end + end + return self +end + + +--- [User] Stop Travel +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:StopTravel() + if not self:Is("Dead") then + if self:Is("Travelling") then + self.Group:RouteStop() + self:T("TACTICS: "..self.Groupname.." was forced to stop travelling.") + self:EndTravel(self.Group:GetAverageCoordinate(), self.Destination) + self.TimeTravelActive = false + self.Destination = nil + self.travelToStored = nil + else + self.Destination = nil + self.travelToStored = nil + self:T("TACTICS: Removing travel to from "..self.Groupname.."'s queue.") + end + end + return self +end + +--- [User] Set tactical ROE +-- @param #TACTICS self +-- @param #string rtype Type or ROE, can be "Ignore" or "Attack" or "Hold" or "Avoid" +-- @return #TACTICS self +function TACTICS:SetTacticalROE(rtype) + if rtype == "Ignore" or rtype == "Attack" or rtype == "Hold" or rtype == "Avoid" then + self.TacticalROE = rtype + else + self:E("TACTICS ERROR: "..tostring(rtype).." is not a valid TacticalROE! Please enter one of the following: 'Attack' | 'Hold' | 'Avoid' | 'Ignore'") + end + return self +end + +--- [User] Enable/Disable Detection +-- @param #TACTICS self +-- @param #boolean OnOff +-- @param Core.Zone#ZONE FilterZone +-- @param #string FilterType +-- @return #TACTICS self +function TACTICS:DetectionEnabled(OnOff,FilterZone,FilterType) + if OnOff == true then + self.UseDetection = true + self.TimeDetectionActive = true + else + self.UseDetection = false + self.TimeDetectionActive = false + end + if FilterZone then self.FilterDetectionZones = FilterZone end + if FilterType then self.FilterEnemyType = FilterType end + return self +end + +--- [User] Enable/Disable Rearming +-- @param #TACTICS self +-- @param #boolean OnOff +-- @param #number LowPercent +-- @param #string FullPercent +-- @param #boolean RearmGroup +-- @param Core.Point#COORDINATE BaseCoord +-- @param #boolean AllowRTB +-- @param #string SpawnTemplate +-- @param Core.Point#COORDINATE SpawnCoord +-- @param #boolean UseRoads +-- @param #number Speed +-- @param #string Formation +-- @return #TACTICS self +function TACTICS:EnableRearming(OnOff,LowPercent,FullPercent,RearmGroup,BaseCoord,AllowRTB,SpawnTemplate,SpawnCoord,UseRoads,Speed,Formation) + self.AllowRearming = OnOff + self.LowAmmoPercent = LowPercent or 0.25 + self.FullAmmoPercent = FullPercent or 0.95 + self.RearmGroup = GroupAlive + self.RearmTemplate = SpawnTemplate + self.RearmSpawnCoord = SpawnCoord or BaseCoord + self.DespawnRearmingGroup = false + self.RearmGroupBase = BaseCoord + self.RearmGroupRTB = AllowRTB or false + self.RearmGroupUseRoads = UseRoads or false + self.RearmGroupSpeed = Speed or 60 + self.RearmGroupFormation = Formation or "Diamond" + return self +end + +--- [User] Enable Troop Transport +-- @param #TACTICS self +-- @param #string template +-- @param #number attackdist +-- @param #number attackreturn +-- @param string unitprefix +-- @return #TACTICS self +function TACTICS:EnableTroopTransport(template,attackdist,attackreturn,unitprefix) + self.TroopTransport = true + self.TroopTemplate = template + if attackdist then self.TroopAttackDist = attackdist end + if attackreturn then self.TroopReturnTime = attackreturn end + if unitprefix then self.TransportUnitPrefix = unitprefix end + return self +end + +--- [User] Enable drawing rearming requests +-- @param #TACTICS self +-- @param #string Text +-- @param #number FontSize +-- @param #number Radius +-- @param #table BorderTextColor +-- @param #number BorderTextAlpha +-- @param #table FillColor +-- @param #number FillAlpha +-- @param #number LineType +-- @return #TACTICS self +function TACTICS:EnableRearmDrawings(Text,FontSize,Radius,BorderTextColor,BorderTextAlpha,FillColor,FillAlpha,LineType) + self.RearmDrawing = true + self.DrawDataR.DrawText = nil + self.DrawDataR.DrawPoly = nil + self.DrawDataR.Text = Text + self.DrawDataR.Font = FontSize + self.DrawDataR.Radius = Radius + self.DrawDataR.Color1 = BorderTextColor + self.DrawDataR.Color2 = FillColor + self.DrawDataR.Alpha1 = BorderTextAlpha + self.DrawDataR.Alpha2 = FillAlpha + self.DrawDataR.Linetype = LineType + self.DrawDataR.Coalition = 2 + if self.groupCoalition == coalition.side.RED then self.DrawDataR.Coalition = 1 end + return self +end + +--- [User] Enable drawing rgroups that are engaged with enemy forces +-- @param #TACTICS self +-- @param #string Text +-- @param #number FontSize +-- @param #number Radius +-- @param #table BorderTextColor +-- @param #number BorderTextAlpha +-- @param #table FillColor +-- @param #number FillAlpha +-- @param #number LineType +-- @param #boolean MarkEnemy +-- @param #number MarkFreshTime +-- @param #number MarkStaleTime +-- @return #TACTICS self +function TACTICS:EnableEngageDrawings(Text,FontSize,Radius,BorderTextColor,BorderTextAlpha,FillColor,FillAlpha,LineType,MarkEnemy,MarkFreshTime,MarkStaleTime) + self.EngageDrawing = true + self.DrawDataE.DrawText = nil + self.DrawDataE.DrawPoly = nil + self.DrawDataE.Text = Text + self.DrawDataE.Font = FontSize + self.DrawDataE.Radius = Radius + self.DrawDataE.Color1 = BorderTextColor + self.DrawDataE.Color2 = FillColor + self.DrawDataE.Alpha1 = BorderTextAlpha + self.DrawDataE.Alpha2 = FillAlpha + self.DrawDataE.Linetype = LineType + if MarkEnemy then self.DrawDataE.MarkEnemy = MarkEnemy end + if MarkFreshTime then self.EngageDrawingFresh = MarkFreshTime end + if MarkStaleTime then self.EngageDrawingStale = MarkStaleTime end + self.DrawDataE.Coalition = 2 + if self.groupCoalition == coalition.side.RED then self.DrawDataE.Coalition = 1 end + return self +end + +--- [User] Enable drawing rgroups that are engaged with enemy forces +-- @param #TACTICS self +-- @param #number StaleTimeout +-- @return #TACTICS self +function TACTICS:EnableSpotDrawings(StaleTimeout) + self.DrawEnemySpots = true + if StaleTimeout then self.EnemySpotStale = StaleTimeout end + return self +end + +--- [User] Retreat to the retreat zone +-- @param #TACTICS self +-- @param Core.Point#COORDINATE Destination +-- @param #boolean Despawn +-- @param #number Speed +-- @param #string Formation +-- @param #boolean UseRoads +-- @return #TACTICS self +function TACTICS:Retreat(Destination,Despawn,Speed,Formation,UseRoads) + if not self:Is("Retreating") then + if not Destination and self.RetreatZone then + self:I("RETREAT: "..self.Groupname.." will attempt to retreat to the defined retreat zone.") + Destination = self.RetreatZone:GetRandomCoordinate() + if not Speed then Speed = self.RetreatSpeed else self.RetreatSpeed = Speed end + if not Formation then Formation = self.RetreatFormation else self.RetreatFormation = Formation end + if not UseRoads then UseRoads = false end + if not Despawn then Despawn = false end + self.retreatDestination = Destination + self.TimeTravelActive = false + self.TimeEngageActive = false + self.TimeHoldActive = false + self.TimeAvoidActive = false + self.TimeAttackActive = false + self.TimeDetectionActive = false + + if self.DrawDataR.DrawText then + trigger.action.removeMark(self.DrawDataR.DrawText) + self.DrawDataR.DrawText = nil + end + if self.DrawDataR.DrawPoly then + trigger.action.removeMark(self.DrawDataR.DrawPoly) + self.DrawDataR.DrawPoly = nil + end + if self.DrawDataE.DrawText then + trigger.action.removeMark(self.DrawDataE.DrawText) + self.DrawDataE.DrawText = nil + end + if self.DrawDataE.DrawPoly then + trigger.action.removeMark(self.DrawDataE.DrawPoly) + self.DrawDataE.DrawPoly = nil + end + + if not self.TroopsAttacking then + if self.RetreatOnRoads then + self.Group:RouteGroundOnRoad(self.retreatDestination, self.RetreatSpeed, 1, self.RetreatFormation) + self.TimeRetreatActive = true + self.TimeRetreatStamp = timer.getAbsTime() + else + self.Group:RouteGroundTo(self.retreatDestination, self.a, self.RetreatFormation, 1) + self.TimeRetreatActive = true + self.TimeRetreatStamp = timer.getAbsTime() + end + else + self:_ReturnTroops() + end + self:StartRetreat(self.retreatDestination) + else + self:E("TACICS ERROR: "..self.Groupname.." attempted to retreat but no retreat zone was declared and no destination was provided.") + end + end + return self +end + +--- [User] Enable retreating +-- @param #TACTICS self +-- @param Core.Zone#ZONE RetreatZone +-- @param #number LossPercentage +-- @param #boolean DespawnAfterRetreat +-- @param #number RetreatSpeed +-- @param #string RetreatFormation +-- @param #boolean UseRoads +-- @return #TACTICS self +function TACTICS:EnableRetreating(RetreatZone,LossPercentage,DespawnAfterRetreat,RetreatSpeed,RetreatFormation,UseRoads) + if RetreatZone then + self.RetreatZone = RetreatZone + else + self:E("TACTICS WARNING: No retreat zone declared when retreating was enabled for "..self.Groupname..". Retreating can only be conducted if executed manually.") + end + if LossPercentage then self.RetreatAfterLosses = LossPercentage end + if RetreatSpeed then self.RetreatSpeed = RetreatSpeed end + if RetreatFormation then self.RetreatFormation = RetreatFormation end + if UseRoads or UseRoads == false then self.RetreatOnRoads = UseRoads end + if DespawnAfterRetreat or DespawnAfterRetreat == false then self.DespawnAfterRetreat = DespawnAfterRetreat end + self:T({"TACTCS: Retreating enabled for "..self.Groupname,RetreatZone = RetreatZone,LossPercentage = LossPercentage,RetreatSpeed = RetreatSpeed,RetreatFormation = RetreatFormation,UseRoads = UseRoads,DespawnAfterRetreat = DespawnAfterRetreat}) + return self +end + +--- [User] Enable Abandoning +-- @param #TACTICS self +-- @param #string CrewTemplate +-- @param #boolean AllowSelfRecover +-- @param #string RecoveryVehicle +-- @param Core.Zone#ZONE RecoverySpawnZone +-- @param #boolean SmokeAbandoned +-- @param #number AbandonHealth +-- @param #number AbandonDistance +-- @param #boolean RecoveryUseRoads +-- @param #number RecoverySpeed +-- @param #string RecoveryFormation +-- @return #TACTICS self +function TACTICS:EnableAbandon(CrewTemplate,AllowSelfRecover,RecoveryVehicle,RecoverySpawnZone,SmokeAbandoned,AbandonHealth,AbandonDistance,RecoveryUseRoads,RecoverySpeed,RecoveryFormation) + if CrewTemplate then + self.AbandonEnabled = true + self.CrewTemplate = CrewTemplate + if AllowSelfRecover then self.AllowSelfRecover = AllowSelfRecover end + if RecoveryVehicle then self.RecoveryVehicle = RecoveryVehicle end + if RecoverySpawnZone then self.RecoverySpawnZone = RecoverySpawnZone end + if SmokeAbandoned then self.SmokeAbandoned = true else self.SmokeAbandoned = false end + if AbandonHealth then self.AbandonHealth = AbandonHealth end + if AbandonDistance then self.AbandonDistance = AbandonDistance end + if RecoveryUseRoads then self.RecoveryUseRoads = true else self.RecoveryUseRoads = false end + if RecoverySpeed then self.RecoverySpeed = RecoverySpeed end + if RecoveryFormation then self.RecoveryFormation = RecoveryFormation end + else + self:E("TACTICS ERROR: A crew template is required for vehicle abandoning!") + end + return self +end + +--- [User] Set Detection Range +-- @param #TACTICS self +-- @param #number val +-- @return #TACTICS self +function TACTICS:SetDetectionMax(val) + self.zoneDetection:SetRadius(val) + self.MaxDetection = val + return self +end + +--- [User] Enable Message Output +-- @param #TACTICS self +-- @param #boolean OnOff +-- @param #string Callsign +-- @param #string sound +-- @param #number duration +-- @return #TACTICS self +function TACTICS:EnableMessageOutput(OnOff,Callsign,Sound,Duration) + self.MessageOutput = OnOff or true + if Callsign then self.MessageCallsign = Callsign end + if Sound then self.MessageSound = Sound end + if Duration then self.MessageDuration = Duration end + return self +end + +--- [User] Enable Support Requests +-- @param #TACTICS self +-- @param #boolean OnOff +-- @param #number Radius +-- @param #number GroupLimit +-- @return #TACTICS self +function TACTICS:EnableSupportRequest(OnOff,Radius,GroupLimit) + self.AllowSupportRequests = OnOff or true + if Radius then self.SupportRadius = Radius end + if GroupLimit then self.SupportGroupLimit = GroupLimit end + return self +end + +--- [User] Enable Support Response +-- @param #TACTICS self +-- @param #boolean OnOff +-- @return #TACTICS self +function TACTICS:EnableSupportRespond(OnOff) + self.RespondToSupport = OnOff or true + return self +end + +--- [User] Pause timers, halt group, and disable AI +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:Sleep() + if not self.Sleeping then + if self.MasterTime then + self.MasterTime:Stop() + self.Group:RouteStop() + self.Group:SetAIOff() + self.Sleeping = true + else + self.E("Failed attempt to sleep "..self.Groupname.." as it is no longer active!") + end + end + return self +end + +--- [User] Resume timers after 5 seconds and enable AI immediately +-- @param #TACTICS self +-- @return #TACTICS self +function TACTICS:Wake() + if self.Sleeping then + if self.MasterTime then + self.MasterTime:Start(5,self.TickRate) + self.Group:SetAIOn() + self.Sleeping = false + else + self.E("Failed attempt to wake "..self.Groupname.." as it is no longer active!") + end + end + return self +end diff --git a/Moose Development/Moose/Functional/Tiresias.lua b/Moose Development/Moose/Functional/Tiresias.lua index 078bfda56..c7853c593 100644 --- a/Moose Development/Moose/Functional/Tiresias.lua +++ b/Moose Development/Moose/Functional/Tiresias.lua @@ -187,7 +187,7 @@ function TIRESIAS:SetAAARanges(FiringRange,SwitchAAA) return self end ---- [USER] Add a SET_GROUP of GROUP objects as exceptions. Can be done multiple times. +--- [USER] Add a SET_GROUP of GROUP objects as exceptions. Can be done multiple times. Does **not** work work for GROUP objects spawned into the SET after start, i.e. the groups need to exist in the game already. -- @param #TIRESIAS self -- @param Core.Set#SET_GROUP Set to add to the exception list. -- @return #TIRESIAS self diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 1fd51026b..4ddabce63 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -508,7 +508,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.63", -- #string + version = "0.2.64", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -4639,7 +4639,7 @@ function AWACS:_CheckTaskQueue() -- Check ranges for TAC and MELD -- postions relative to CAP position - + --[[ local targetgrp = entry.Contact.group local position = entry.Contact.position or entry.Cluster.coordinate if targetgrp and targetgrp:IsAlive() and managedgroup then @@ -4664,6 +4664,7 @@ function AWACS:_CheckTaskQueue() end end end + --]] local auftrag = entry.Auftrag -- Ops.Auftrag#AUFTRAG local auftragstatus = "Not Known" @@ -4862,6 +4863,7 @@ function AWACS:_CheckTaskQueue() elseif entry.Status == AWACS.TaskStatus.ASSIGNED then self:T("Open Tasks VID ASSIGNED for GroupID "..entry.AssignedGroupID) -- check TAC/MELD ranges + --[[ local targetgrp = entry.Contact.group local position = entry.Contact.position or entry.Cluster.coordinate if targetgrp and targetgrp:IsAlive() and managedgroup then @@ -4886,6 +4888,7 @@ function AWACS:_CheckTaskQueue() end end end + --]] elseif entry.Status == AWACS.TaskStatus.SUCCESS then self:T("Open Tasks VID success for GroupID "..entry.AssignedGroupID) -- outcomes - player ID'd @@ -5486,6 +5489,7 @@ function AWACS:_TACRangeCall(GID,Contact) local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup local contact = Contact.Contact -- Ops.Intel#INTEL.Contact local contacttag = Contact.TargetGroupNaming + local name = managedgroup.GroupName if contact then --and not Contact.TACCallDone then local position = contact.position -- Core.Point#COORDINATE if position then @@ -5494,12 +5498,13 @@ function AWACS:_TACRangeCall(GID,Contact) local grptxt = self.gettext:GetEntry("GROUP",self.locale) local miles = self.gettext:GetEntry("MILES",self.locale) local text = string.format("%s. %s. %s %s, %d %s.",self.callsigntxt,pilotcallsign,contacttag,grptxt,distance,miles) - self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + if not self.TacticalSubscribers[name] then + self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + end self:_UpdateContactEngagementTag(Contact.CID,Contact.EngagementTag,true,false,AWACS.TaskStatus.EXECUTING) if GID and GID ~= 0 then --local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then - local name = managedgroup.GroupName if self.TacticalSubscribers[name] then self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true,true) end @@ -5524,6 +5529,7 @@ function AWACS:_MeldRangeCall(GID,Contact) local flightpos = managedgroup.Group:GetCoordinate() local contact = Contact.Contact -- Ops.Intel#INTEL.Contact local contacttag = Contact.TargetGroupNaming or "Bogey" + local name = managedgroup.GroupName if contact then --and not Contact.MeldCallDone then local position = contact.position -- Core.Point#COORDINATE if position then @@ -5535,7 +5541,9 @@ function AWACS:_MeldRangeCall(GID,Contact) end local grptxt = self.gettext:GetEntry("GROUP",self.locale) local text = string.format("%s. %s. %s %s, %s",self.callsigntxt,pilotcallsign,contacttag,grptxt,BRATExt) - self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + if not self.TacticalSubscribers[name] then + self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + end self:_UpdateContactEngagementTag(Contact.CID,Contact.EngagementTag,true,true,AWACS.TaskStatus.EXECUTING) if GID and GID ~= 0 then --local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup @@ -5563,6 +5571,8 @@ function AWACS:_ThreatRangeCall(GID,Contact) local flightpos = managedgroup.Group:GetCoordinate() or managedgroup.LastKnownPosition local contact = Contact.Contact -- Ops.Intel#INTEL.Contact local contacttag = Contact.TargetGroupNaming or "Bogey" + local name = managedgroup.GroupName + local IsSub = self.TacticalSubscribers[name] and true or false if contact then local position = contact.position or contact.group:GetCoordinate() -- Core.Point#COORDINATE if position then @@ -5575,7 +5585,9 @@ function AWACS:_ThreatRangeCall(GID,Contact) local grptxt = self.gettext:GetEntry("GROUP",self.locale) local thrt = self.gettext:GetEntry("THREAT",self.locale) local text = string.format("%s. %s. %s %s, %s. %s",self.callsigntxt,pilotcallsign,contacttag,grptxt, thrt, BRATExt) - self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + if IsSub == false then + self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + end if GID and GID ~= 0 then --local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then @@ -5600,11 +5612,17 @@ function AWACS:_MergedCall(GID) local pilotcallsign = self:_GetCallSign(nil,GID) local merge = self.gettext:GetEntry("MERGED",self.locale) local text = string.format("%s. %s. %s.",self.callsigntxt,pilotcallsign,merge) - self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup + local name + if managedgroup then + name = managedgroup.GroupName or "none" + end + if not self.TacticalSubscribers[name] then + self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + end if GID and GID ~= 0 then local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup - if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then - local name = managedgroup.GroupName + if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then if self.TacticalSubscribers[name] then self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true,true) end From d7963bb161643341d94829b1d104900244348d72 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 22 Mar 2024 16:04:58 +0100 Subject: [PATCH 530/603] xx --- Moose Development/Moose/Wrapper/Unit.lua | 66 ++++++++++++------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 781c71d48..3053194cc 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -1192,17 +1192,17 @@ function UNIT:GetThreatLevel() if self:IsGround() then local ThreatLevels = { - "Unarmed", - "Infantry", - "Old Tanks & APCs", - "Tanks & IFVs without ATGM", - "Tanks & IFV with ATGM", - "Modern Tanks", - "AAA", - "IR Guided SAMs", - "SR SAMs", - "MR SAMs", - "LR SAMs" + [1] = "Unarmed", + [2] = "Infantry", + [3] = "Old Tanks & APCs", + [4] = "Tanks & IFVs without ATGM", + [5] = "Tanks & IFV with ATGM", + [6] = "Modern Tanks", + [7] = "AAA", + [8] = "IR Guided SAMs", + [9] = "SR SAMs", + [10] = "MR SAMs", + [11] = "LR SAMs" } @@ -1228,17 +1228,17 @@ function UNIT:GetThreatLevel() if self:IsAir() then local ThreatLevels = { - "Unarmed", - "Tanker", - "AWACS", - "Transport Helicopter", - "UAV", - "Bomber", - "Strategic Bomber", - "Attack Helicopter", - "Battleplane", - "Multirole Fighter", - "Fighter" + [1] = "Unarmed", + [2] = "Tanker", + [3] = "AWACS", + [4] = "Transport Helicopter", + [5] = "UAV", + [6] = "Bomber", + [7] = "Strategic Bomber", + [8] = "Attack Helicopter", + [9] = "Battleplane", + [10] = "Multirole Fighter", + [11] = "Fighter" } @@ -1272,17 +1272,17 @@ function UNIT:GetThreatLevel() --["Unarmed ships"] = {"Ships","HeavyArmoredUnits",}, local ThreatLevels = { - "Unarmed ship", - "Light armed ships", - "Corvettes", - "", - "Frigates", - "", - "Cruiser", - "", - "Destroyer", - "", - "Aircraft Carrier" + [1] = "Unarmed ship", + [2] = "Light armed ships", + [3] = "Corvettes", + [4] = "", + [5] = "Frigates", + [6] = "", + [7] = "Cruiser", + [8] = "", + [9] = "Destroyer", + [10] = "", + [11] = "Aircraft Carrier" } From caedc314b7eda888b37f0fdf6897f5e5c799b311 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 23 Mar 2024 15:12:16 +0100 Subject: [PATCH 531/603] xx --- Moose Development/Moose/Functional/Scoring.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 42dc8005a..c4a0c8e20 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -229,7 +229,7 @@ SCORING = { ClassID = 0, Players = {}, AutoSave = true, - version = "1.18.3" + version = "1.18.4" } local _SCORINGCoalition = { @@ -248,13 +248,15 @@ local _SCORINGCategory = { --- Creates a new SCORING object to administer the scoring achieved by players. -- @param #SCORING self -- @param #string GameName The name of the game. This name is also logged in the CSV score file. +-- @param #string SavePath (Optional) Path where to save the CSV file, defaults to your **\\Saved Games\\DCS\\Logs** folder. +-- @param #boolean AutoSave (Optional) If passed as `false`, then swith autosave off. -- @return #SCORING self -- @usage -- -- -- Define a new scoring object for the mission Gori Valley. -- ScoringObject = SCORING:New( "Gori Valley" ) -- -function SCORING:New( GameName ) +function SCORING:New( GameName, SavePath, AutoSave ) -- Inherits from BASE local self = BASE:Inherit( self, BASE:New() ) -- #SCORING @@ -317,7 +319,8 @@ function SCORING:New( GameName ) end ) -- Create the CSV file. - self.AutoSave = true + self.AutoSavePath = SavePath + self.AutoSave = AutoSave or true self:OpenCSV( GameName ) return self @@ -1839,10 +1842,11 @@ end function SCORING:OpenCSV( ScoringCSV ) self:F( ScoringCSV ) - if lfs and io and os and self.AutoSave then + if lfs and io and os and self.AutoSave == true then if ScoringCSV then self.ScoringCSV = ScoringCSV - local fdir = lfs.writedir() .. [[Logs\]] .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv" + local path = self.AutoSavePath or lfs.writedir() .. [[Logs\]] + local fdir = path .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv" self.CSVFile, self.err = io.open( fdir, "w+" ) if not self.CSVFile then From 13cdb779576288bac2cb81c9a4118f73577971e5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 24 Mar 2024 13:22:54 +0100 Subject: [PATCH 532/603] xxx --- Moose Development/Moose/Sound/SRS.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 0d3315dcc..eeb16452a 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -824,7 +824,7 @@ function MSRS:SetVoiceProvider(Voice, Provider) self:F( {Voice=Voice, Provider=Provider} ) self.poptions=self.poptions or {} - self.poptions[Provider or self:GetProvider()]=Voice + self.poptions[Provider or self:GetProvider()].voice =Voice return self end @@ -1238,7 +1238,7 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture self:T({Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate} ) if Delay and Delay>0 then - self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) + self:ScheduleOnce(Delay, self.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) else Frequencies = Frequencies or self:GetFrequencies() @@ -1560,8 +1560,8 @@ end function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) -- Debug info. - self:F("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") - self:F({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) + self:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") + self:T({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) local options = {} -- #MSRS.GRPCOptions @@ -1587,7 +1587,6 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab -- Provider (win, gcloud, ...) local provider = self.provider or MSRS.Provider.WINDOWS - self:F({provider=provider}) -- Provider options: voice, credentials options.provider = {} @@ -1595,7 +1594,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab -- Voice Voice=Voice or self:GetVoice(self.provider) or self.voice - + if Voice then -- We use a specific voice options.provider[provider].voice = Voice From 371b10067219f44b6d99345c2fa41aa6148ea4fa Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 27 Mar 2024 14:40:47 +0100 Subject: [PATCH 533/603] xxx --- Moose Development/Moose/Core/Point.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b69e7d294..bfb623bca 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -702,8 +702,9 @@ do -- COORDINATE -- @param #COORDINATE PointVec2Reference The reference @{#COORDINATE}. -- @return DCS#Distance The distance from the reference @{#COORDINATE} in meters. function COORDINATE:DistanceFromPointVec2( PointVec2Reference ) - self:F2( PointVec2Reference ) - + self:F2( PointVec2Reference ) + if not PointVec2Reference then return math.huge end + local Distance = ( ( PointVec2Reference.x - self.x ) ^ 2 + ( PointVec2Reference.z - self.z ) ^2 ) ^ 0.5 self:T2( Distance ) From 1b072e11ad673a09d7f3e9cc3373bd0d531f631e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 27 Mar 2024 18:00:50 +0100 Subject: [PATCH 534/603] xxx --- Moose Development/Moose/Ops/CTLD.lua | 54 ++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index e571e5cd0..0c9bb8f14 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -24,7 +24,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update February 2024 +-- Last Update March 2024 do @@ -45,6 +45,7 @@ do -- @field #number Stock Number of builds available, -1 for unlimited. -- @field #string Subcategory Sub-category name. -- @field #boolean DontShowInMenu Show this item in menu or not. +-- @field Core.Zone#ZONE Location Location (if set) where to get this cargo item. -- @extends Core.Base#BASE --- @@ -64,6 +65,7 @@ CTLD_CARGO = { Stock = nil, Mark = nil, DontShowInMenu = false, + Location = nil, } --- Define cargo types. @@ -100,8 +102,9 @@ CTLD_CARGO = { -- @param #number Stock Number of builds available, nil for unlimited -- @param #string Subcategory Name of subcategory, handy if using > 10 types to load. -- @param #boolean DontShowInMenu Show this item in menu or not (default: false == show it). + -- @param Core.Zone#ZONE Location (optional) Where the cargo is available (one location only). -- @return #CTLD_CARGO self - function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory,DontShowInMenu) + function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory, DontShowInMenu, Location) -- Inherit everything from BASE class. local self=BASE:Inherit(self, BASE:New()) -- #CTLD_CARGO self:T({ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped}) @@ -119,8 +122,19 @@ CTLD_CARGO = { self.Mark = nil self.Subcategory = Subcategory or "Other" self.DontShowInMenu = DontShowInMenu or false + if type(Location) == "string" then + Location = ZONE:New("Location") + end + self.Location = Location return self end + + --- Query Location. + -- @param #CTLD_CARGO self + -- @return Core.Zone#ZONE location or `nil` if not set + function CTLD_CARGO:GetLocation() + return self.Location + end --- Query ID. -- @param #CTLD_CARGO self @@ -1233,7 +1247,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.48" +CTLD.version="1.0.49" --- Instantiate a new CTLD. -- @param #CTLD self @@ -2355,7 +2369,21 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack) self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group) if not self.debug then return self end end - + + -- Check cargo location if available + local location = Cargo:GetLocation() + + if location then + local unitcoord = Unit:GetCoordinate() or Group:GetCoordinate() + if unitcoord then + if not location:IsCoordinateInZone(unitcoord) then + -- no we're not at the right spot + self:_SendMessage("The requested cargo is not available in this zone!", 10, false, Group) + if not self.debug then return self end + end + end + end + -- avoid crate spam local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities local canloadcratesno = capabilities.cratelimit @@ -3853,7 +3881,9 @@ end -- @param #number PerCrateMass Mass in kg of each crate -- @param #number Stock Number of buildable groups in stock. Nil for unlimited. -- @param #string SubCategory Name of sub-category (optional). -function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory) +-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu. +-- @param Core.Zone#ZONE Location (optional) If set, the cargo item is **only** available here. Can be a #ZONE object or the name of a zone as #string. +function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location) self:T(self.lid .. " AddCratesCargo") if not self:_CheckTemplates(Templates) then self:E(self.lid .. "Crates Cargo for " .. Name .. " has missing template(s)!" ) @@ -3861,7 +3891,7 @@ function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,Sub end self.CargoCounter = self.CargoCounter + 1 -- Crates are not directly loadable - local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory) + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location) table.insert(self.Cargo_Crates,cargo) return self end @@ -3872,13 +3902,15 @@ end -- @param #number Mass Mass in kg of each static in kg, e.g. 100. -- @param #number Stock Number of groups in stock. Nil for unlimited. -- @param #string SubCategory Name of sub-category (optional). -function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory) +-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu. +-- @param Core.Zone#ZONE Location (optional) If set, the cargo item is **only** available here. Can be a #ZONE object or the name of a zone as #string. +function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory,DontShowInMenu,Location) self:T(self.lid .. " AddStaticsCargo") self.CargoCounter = self.CargoCounter + 1 local type = CTLD_CARGO.Enum.STATIC local template = STATIC:FindByName(Name,true):GetTypeName() -- Crates are not directly loadable - local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock,SubCategory) + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock,SubCategory,DontShowInMenu,Location) table.insert(self.Cargo_Statics,cargo) return self end @@ -3908,7 +3940,9 @@ end -- @param #number PerCrateMass Mass in kg of each crate -- @param #number Stock Number of groups in stock. Nil for unlimited. -- @param #string SubCategory Name of the sub-category (optional). -function CTLD:AddCratesRepair(Name,Template,Type,NoCrates, PerCrateMass,Stock,SubCategory) +-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu. +-- @param Core.Zone#ZONE Location (optional) If set, the cargo item is **only** available here. Can be a #ZONE object or the name of a zone as #string. +function CTLD:AddCratesRepair(Name,Template,Type,NoCrates, PerCrateMass,Stock,SubCategory,DontShowInMenu,Location) self:T(self.lid .. " AddCratesRepair") if not self:_CheckTemplates(Template) then self:E(self.lid .. "Repair Cargo for " .. Name .. " has a missing template!" ) @@ -3916,7 +3950,7 @@ function CTLD:AddCratesRepair(Name,Template,Type,NoCrates, PerCrateMass,Stock,Su end self.CargoCounter = self.CargoCounter + 1 -- Crates are not directly loadable - local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Template,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory) + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Template,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location) table.insert(self.Cargo_Crates,cargo) return self end From a52df9ae9aaa44aef9a5d67ce3ad668ad5255bba Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 28 Mar 2024 08:44:12 +0100 Subject: [PATCH 535/603] xxx --- Moose Development/Moose/Ops/CTLD.lua | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 0c9bb8f14..af6a94cf7 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -123,7 +123,7 @@ CTLD_CARGO = { self.Subcategory = Subcategory or "Other" self.DontShowInMenu = DontShowInMenu or false if type(Location) == "string" then - Location = ZONE:New("Location") + Location = ZONE:New(Location) end self.Location = Location return self @@ -674,6 +674,8 @@ do -- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775) -- -- if you want to limit your stock, add a number (here: 10) as parameter after weight. No parameter / nil means unlimited stock. -- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10) +-- -- additionally, you can limit **where** the stock is available (one location only!) - this one is available in a zone called "Vehicle Store". +-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10,nil,nil,"Vehicle Store") -- -- -- add infantry unit called "Forward Ops Base" using template "FOB", of type FOB, size 4, i.e. needs four crates to be build: -- my_ctld:AddCratesCargo("Forward Ops Base",{"FOB"},CTLD_CARGO.Enum.FOB,4) @@ -775,6 +777,9 @@ do -- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- ["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- ["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, +-- ["MH-60R"] = {type="MH-60R", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats +-- ["SH-60B"] = {type="SH-60B", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats +-- ["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450}, -- -- ### 2.1.2 Activate and deactivate zones -- @@ -1241,6 +1246,7 @@ CTLD.UnitTypeCapabilities = { --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 ["MH-60R"] = {type="MH-60R", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats + ["SH-60B"] = {type="SH-60B", 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 ["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450}, } @@ -3769,9 +3775,13 @@ function CTLD:_RefreshF10Menus() local entry = _entry -- #CTLD_CARGO local subcat = entry.Subcategory local noshow = entry.DontShowInMenu + local zone = entry.Location if not noshow then menucount = menucount + 1 local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + if zone then + menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0) + end menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry) end end @@ -3779,9 +3789,13 @@ function CTLD:_RefreshF10Menus() local entry = _entry -- #CTLD_CARGO local subcat = entry.Subcategory local noshow = entry.DontShowInMenu + local zone = entry.Location if not noshow then menucount = menucount + 1 local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + if zone then + menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0) + end menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry) end end @@ -3789,18 +3803,26 @@ function CTLD:_RefreshF10Menus() for _,_entry in pairs(self.Cargo_Crates) do local entry = _entry -- #CTLD_CARGO local noshow = entry.DontShowInMenu + local zone = entry.Location if not noshow then menucount = menucount + 1 local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + if zone then + menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0) + end menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) end end for _,_entry in pairs(self.Cargo_Statics) do local entry = _entry -- #CTLD_CARGO local noshow = entry.DontShowInMenu + local zone = entry.Location if not noshow then menucount = menucount + 1 local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + if zone then + menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0) + end menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) end end From 7aa252e42f8c4b83e7de506da824e880cdfaee06 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 28 Mar 2024 11:13:13 +0100 Subject: [PATCH 536/603] xxx --- Moose Development/Moose/Ops/CTLD.lua | 51 +++++++++++++++++----------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index af6a94cf7..3c4cbb563 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1253,7 +1253,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.49" +CTLD.version="1.0.50" --- Instantiate a new CTLD. -- @param #CTLD self @@ -2260,7 +2260,7 @@ end local secondarygroups = {} for i=1,#distancekeys do - local nearestGroup = nearestList[distancekeys[i]] + local nearestGroup = nearestList[distancekeys[i]] -- Wrapper.Group#GROUP -- find matching cargo type local groupType = string.match(nearestGroup:GetName(), "(.+)-(.+)$") local Cargotype = nil @@ -2296,20 +2296,31 @@ end self.CargoCounter = self.CargoCounter + 1 local loadcargotype = CTLD_CARGO:New(self.CargoCounter, Cargotype.Name, Cargotype.Templates, Cargotype.CargoType, true, true, Cargotype.CratesNeeded,nil,nil,Cargotype.PerCrateMass) self:T({cargotype=loadcargotype}) + local running = math.floor(nearestDistance / 4)+10 -- time run to helo plus boarding loaded.Troopsloaded = loaded.Troopsloaded + troopsize table.insert(loaded.Cargo,loadcargotype) self.Loaded_Cargo[unitname] = loaded - self:_SendMessage("Troops boarded!", 10, false, Group) + self:ScheduleOnce(running,self._SendMessage,self,"Troops boarded!", 10, false, Group) + self:_SendMessage("Troops boarding!", 10, false, Group) self:_UpdateUnitCargoMass(Unit) - self:__TroopsExtracted(1,Group, Unit, nearestGroup) - + self:__TroopsExtracted(running,Group, Unit, nearestGroup) + local coord = Unit:GetCoordinate() or Group:GetCoordinate() -- Core.Point#COORDINATE + local Point + if coord then + local heading = unit:GetHeading() or 0 + local Angle = math.floor((heading+160)%360) + Point = coord:Translate(8,Angle):GetVec2() + if Point then + nearestGroup:RouteToVec2(Point,4) + end + end -- clean up: if type(Cargotype.Templates) == "table" and Cargotype.Templates[2] then for _,_key in pairs (Cargotype.Templates) do table.insert(secondarygroups,_key) end end - nearestGroup:Destroy(false) + nearestGroup:Destroy(false,running) end end end @@ -2319,7 +2330,7 @@ end if _group and _group:IsAlive() then local groupname = string.match(_group:GetName(), "(.+)-(.+)$") if _name == groupname then - _group:Destroy(false) + _group:Destroy(false,15) end end end @@ -5437,19 +5448,19 @@ end return self end - --- (Internal) FSM Function onbeforeTroopsExtracted. - -- @param #CTLD self - -- @param #string From State. - -- @param #string Event Trigger. - -- @param #string To State. - -- @param Wrapper.Group#GROUP Group Group Object. - -- @param Wrapper.Unit#UNIT Unit Unit Object. - -- @param Wrapper.Group#GROUP Troops Troops #GROUP Object. - -- @return #CTLD self - function CTLD:onbeforeTroopsExtracted(From, Event, To, Group, Unit, Troops) - self:T({From, Event, To}) - return self - end + --- (Internal) FSM Function onbeforeTroopsExtracted. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Troops Troops #GROUP Object. + -- @return #CTLD self + function CTLD:onbeforeTroopsExtracted(From, Event, To, Group, Unit, Troops) + self:T({From, Event, To}) + return self + end --- (Internal) FSM Function onbeforeTroopsDeployed. From 1ea2f091e0ee0cd609aea27b67c44d6443584d53 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 1 Apr 2024 12:58:20 +0200 Subject: [PATCH 537/603] Fix --- Moose Development/Moose/Core/Point.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index bfb623bca..6db800ac8 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -3148,17 +3148,18 @@ do -- COORDINATE -- @param #string Northing Meters northing - string in order to allow for leading zeros, e.g. "12340". Should be 5 digits. -- @return #COORDINATE self function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing ) - if string.len(Easting) < 5 then Easting = Easting..string.rep("0",5-string.len(Easting) )end - if string.len(Northing) < 5 then Northing = Northing..string.rep("0",5-string.len(Northing) )end + if string.len(Easting) < 5 then Easting = tostring(Easting..string.rep("0",5-string.len(Easting) )) end + if string.len(Northing) < 5 then Northing = tostring(Northing..string.rep("0",5-string.len(Northing) )) end local MGRS = { UTMZone = UTMZone, MGRSDigraph = MGRSDigraph, - Easting = Easting, - Northing = Northing, + Easting = tostring(Easting), + Northing = tostring(Northing), } local lat, lon = coord.MGRStoLL(MGRS) local point = coord.LLtoLO(lat, lon, 0) local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) + return coord end --- Provides a coordinate string of the point, based on a coordinate format system: From 355230a97f940969e46cf48ab374c1f47a522400 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 1 Apr 2024 19:11:33 +0200 Subject: [PATCH 538/603] xxx --- Moose Development/Moose/Ops/OpsGroup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 2298aba18..f5a76c9cd 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -597,7 +597,7 @@ function OPSGROUP:New(group) if units then local masterunit=units[1] --Wrapper.Unit#UNIT - if unit then + if masterunit then -- Get Descriptors. self.descriptors=masterunit:GetDesc() From 483cf09676c2d2d8c639d781310bc266b4728848 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 2 Apr 2024 13:23:36 +0200 Subject: [PATCH 539/603] xxx --- Moose Development/Moose/Tasking/Task_A2G.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 9553000d1..84bdcf360 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -280,7 +280,7 @@ do -- TASK_A2G function TASK_A2G:SetGoalTotal() - self.GoalTotal = self.TargetSetUnit:Count() + self.GoalTotal = self.TargetSetUnit:CountAlive() end function TASK_A2G:GetGoalTotal() @@ -304,7 +304,7 @@ do -- TASK_A2G function TASK_A2G:onafterGoal( TaskUnit, From, Event, To ) local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT - if TargetSetUnit:Count() == 0 then + if TargetSetUnit:CountAlive() == 0 then self:Success() end @@ -328,7 +328,7 @@ do -- TASK_A2G self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 10, "MOD", true ) if self.Detection then - local DetectedItemsCount = self.TargetSetUnit:Count() + local DetectedItemsCount = self.TargetSetUnit:CountAlive() local ReportTypes = REPORT:New() local TargetTypes = {} for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do @@ -341,7 +341,7 @@ do -- TASK_A2G self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true ) else - local DetectedItemsCount = self.TargetSetUnit:Count() + local DetectedItemsCount = self.TargetSetUnit:CountAlive() local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true ) From b9cb6dbffb8c6ac484a71d3712641fea29caa4ec Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 15 Apr 2024 12:50:15 +0200 Subject: [PATCH 540/603] xxx --- Moose Development/Moose/Core/ClientMenu.lua | 72 ++++++++++++++------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index a87583be9..6071c7e4f 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -20,7 +20,7 @@ -- -- @module Core.ClientMenu -- @image Core_Menu.JPG --- last change: Oct 2023 +-- last change: Apr 2024 -- TODO ---------------------------------------------------------------------------------------------------------------- @@ -51,6 +51,7 @@ -- @field #boolean Generic -- @field #boolean debug -- @field #CLIENTMENUMANAGER Controller +-- @field #active boolean -- @extends Core.Base#BASE --- @@ -58,7 +59,7 @@ CLIENTMENU = { ClassName = "CLIENTMENUE", lid = "", - version = "0.1.1", + version = "0.1.2", name = nil, path = nil, group = nil, @@ -70,6 +71,7 @@ CLIENTMENU = { debug = false, Controller = nil, groupname = nil, + active = false, } --- @@ -114,7 +116,7 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) if self.Functionargs and self.debug then self:T({"Functionargs",self.Functionargs}) end - if not self.Generic then + if not self.Generic and self.active == false then if Function ~= nil then local ErrorHandler = function( errmsg ) env.info( "MOOSE Error in CLIENTMENU COMMAND function: " .. errmsg ) @@ -133,8 +135,10 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) end end self.path = missionCommands.addCommandForGroup(self.GroupID,Text,self.parentpath, self.CallHandler) + self.active = true else self.path = missionCommands.addSubMenuForGroup(self.GroupID,Text,self.parentpath) + self.active = true end else if self.parentpath then @@ -200,6 +204,7 @@ function CLIENTMENU:RemoveF10() if not status then self:I(string.format("**** Error Removing Menu Entry %s for %s!",tostring(self.name),self.groupname)) end + self.active = false end return self end @@ -302,6 +307,7 @@ end -- @field #table flattree -- @field #table rootentries -- @field #table menutree +-- @field #table SecondSeat -- @field #number entrycount -- @field #boolean debug -- @field #table PlayerMenu @@ -412,7 +418,7 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.1.4", + version = "0.1.5", name = nil, clientset = nil, menutree = {}, @@ -423,6 +429,7 @@ CLIENTMENUMANAGER = { debug = true, PlayerMenu = {}, Coalition = nil, + SecondSeat = {}, } --- Create a new ClientManager instance. @@ -676,6 +683,7 @@ end function CLIENTMENUMANAGER:Propagate(Client) self:T(self.lid.."Propagate") --self:I(UTILS.PrintTableToLog(Client,1)) + local knownunits = {} -- track so we can ID multi seated local Set = self.clientset.Set if Client then Set = {Client} @@ -684,28 +692,36 @@ function CLIENTMENUMANAGER:Propagate(Client) for _,_client in pairs(Set) do local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then + local playerunit = client:GetName() + local playergroup = client:GetGroup() local playername = client:GetPlayerName() or "none" - if not self.playertree[playername] then - self.playertree[playername] = {} - end - for level,branch in pairs (self.menutree) do - self:T("Building branch:" .. level) - for _,leaf in pairs(branch) do - self:T("Building leaf:" .. leaf) - local entry = self:FindEntryByUUID(leaf) - if entry then - self:T("Found generic entry:" .. entry.UUID) - local parent = nil - if entry.Parent and entry.Parent.UUID then - parent = self.playertree[playername][entry.Parent.UUID] or self:FindEntryByUUID(entry.Parent.UUID) - end - self.playertree[playername][entry.UUID] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) - self.playertree[playername][entry.UUID].Once = entry.Once - else - self:T("NO generic entry for:" .. leaf) - end - end + if not knownunits[playerunit] then + knownunits[playerunit] = true + else + self:I("Player in multi seat unit: "..playername) + break -- multi seat already build + end + if not self.playertree[playername] then + self.playertree[playername] = {} + end + for level,branch in pairs (self.menutree) do + self:T("Building branch:" .. level) + for _,leaf in pairs(branch) do + self:T("Building leaf:" .. leaf) + local entry = self:FindEntryByUUID(leaf) + if entry then + self:T("Found generic entry:" .. entry.UUID) + local parent = nil + if entry.Parent and entry.Parent.UUID then + parent = self.playertree[playername][entry.Parent.UUID] or self:FindEntryByUUID(entry.Parent.UUID) + end + self.playertree[playername][entry.UUID] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) + self.playertree[playername][entry.UUID].Once = entry.Once + else + self:T("NO generic entry for:" .. leaf) + end end + end end end return self @@ -719,6 +735,7 @@ end function CLIENTMENUMANAGER:AddEntry(Entry,Client) self:T(self.lid.."AddEntry") local Set = self.clientset.Set + local knownunits = {} if Client then Set = {Client} end @@ -726,6 +743,13 @@ function CLIENTMENUMANAGER:AddEntry(Entry,Client) local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then local playername = client:GetPlayerName() + local unitname = client:GetName() + if not knownunits[unitname] then + knownunits[unitname] = true + else + self:I("Player in multi seat unit: "..playername) + break + end if Entry then self:T("Adding generic entry:" .. Entry.UUID) local parent = nil From 620df5ad3ef67c5f2d5c2513b66859b7542e4b57 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 15 Apr 2024 18:54:59 +0200 Subject: [PATCH 541/603] xx --- Moose Development/Moose/Core/Base.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index d4851370c..3cb847f54 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -1153,7 +1153,7 @@ function BASE:_Serialize(Arguments) text = string.gsub(text,"(\n+)","") text = string.gsub(text,"%(%(","%(") text = string.gsub(text,"%)%)","%)") - text = string.gsub(text,"(%s+)","") + text = string.gsub(text,"(%s+)"," ") return text end From c9fd56116eb8b1fe2741b1395ba48daaf2e06529 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 16 Apr 2024 08:45:53 +0200 Subject: [PATCH 542/603] Housekeeping --- Moose Development/Moose/Core/ClientMenu.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 6071c7e4f..bcc348814 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -307,7 +307,6 @@ end -- @field #table flattree -- @field #table rootentries -- @field #table menutree --- @field #table SecondSeat -- @field #number entrycount -- @field #boolean debug -- @field #table PlayerMenu @@ -418,7 +417,7 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.1.5", + version = "0.1.5a", name = nil, clientset = nil, menutree = {}, @@ -429,7 +428,6 @@ CLIENTMENUMANAGER = { debug = true, PlayerMenu = {}, Coalition = nil, - SecondSeat = {}, } --- Create a new ClientManager instance. From 186b64ea8185db019508d8f6c4cef9be7524def8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 18 Apr 2024 09:32:27 +0200 Subject: [PATCH 543/603] #CTLD --- Moose Development/Moose/Ops/CTLD.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 3c4cbb563..d55132fee 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -24,7 +24,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update March 2024 +-- Last Update April 2024 do @@ -1253,7 +1253,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.50" +CTLD.version="1.0.51" --- Instantiate a new CTLD. -- @param #CTLD self @@ -2240,7 +2240,9 @@ end local extractdistance = self.CrateDistance * self.ExtractFactor for k,v in pairs(self.DroppedTroops) do local distance = self:_GetDistance(v:GetCoordinate(),unitcoord) - if distance <= extractdistance and distance ~= -1 then + local TNow = timer.getTime() + local vtime = v.ExtractTime or TNow-310 + if distance <= extractdistance and distance ~= -1 and (TNow - vtime > 300) then nearestGroup = v nearestGroupIndex = k nearestDistance = distance @@ -2291,9 +2293,11 @@ end end if troopsize + numberonboard > trooplimit then self:_SendMessage("Sorry, we\'re crammed already!", 10, false, Group) + nearestGroup.ExtractTime = 0 --return self else self.CargoCounter = self.CargoCounter + 1 + nearestGroup.ExtractTime = timer.GetTime() local loadcargotype = CTLD_CARGO:New(self.CargoCounter, Cargotype.Name, Cargotype.Templates, Cargotype.CargoType, true, true, Cargotype.CratesNeeded,nil,nil,Cargotype.PerCrateMass) self:T({cargotype=loadcargotype}) local running = math.floor(nearestDistance / 4)+10 -- time run to helo plus boarding From 5adb9439399b496e2f53aaeb591b8cce662d89cd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 18 Apr 2024 13:52:10 +0200 Subject: [PATCH 544/603] SPAWN --- Moose Development/Moose/Core/Spawn.lua | 40 +++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 24c808cae..e9747fc9b 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1467,6 +1467,30 @@ do -- Delay methods end -- Delay methods +--- Hide the group on the map view (visible to game master slots!). +-- @param #SPAWN self +-- @return #SPAWN The SPAWN object +function SPAWN:InitHiddenOnMap() + self.SpawnHiddenOnMap = true + return self +end + +--- Hide the group on MFDs (visible to game master slots!). +-- @param #SPAWN self +-- @return #SPAWN The SPAWN object +function SPAWN:InitHiddenOnMFD() + self.SpawnHiddenOnMFD = true + return self +end + +--- Hide the group on planner (visible to game master slots!). +-- @param #SPAWN self +-- @return #SPAWN The SPAWN object +function SPAWN:InitHiddenOnPlanner() + self.SpawnHiddenOnPlanner = true + return self +end + --- Will spawn a group based on the internal index. -- Note: This method uses the global _DATABASE object (an instance of @{Core.Database#DATABASE}), which contains ALL initial and new spawned objects in MOOSE. -- @param #SPAWN self @@ -1740,7 +1764,22 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth ) if self.SpawnInitModu then SpawnTemplate.modulation = self.SpawnInitModu end + + -- hiding options + if self.SpawnHiddenOnPlanner then + SpawnTemplate.hiddenOnPlanner=true + end + if self.SpawnHiddenOnMFD then + SpawnTemplate.hiddenOnMFD=true + end + + if self.SpawnHiddenOnMap then + SpawnTemplate.hidden=true + end + + self:I(SpawnTemplate) + -- Set country, coalition and category. SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID @@ -3808,7 +3847,6 @@ end -- @param #number SpawnIndex Spawn index. -- @return #number self.SpawnIndex function SPAWN:_GetSpawnIndex( SpawnIndex ) - self:T("_GetSpawnIndex") self:F2( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnMaxGroups, self.SpawnMaxUnitsAlive, self.AliveUnits, #self.SpawnTemplate.units } ) if (self.SpawnMaxGroups == 0) or (SpawnIndex <= self.SpawnMaxGroups) then From 5d00a2d80692680e5e9d2078d6cf95fb76a34b2f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 18 Apr 2024 14:41:44 +0200 Subject: [PATCH 545/603] xxx --- Moose Development/Moose/Core/Database.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index cbfa62555..e17b01757 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1009,7 +1009,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category self.Templates.Groups[GroupTemplateName].CategoryID = CategoryID self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionSide self.Templates.Groups[GroupTemplateName].CountryID = CountryID - + local UnitNames = {} for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do @@ -2081,7 +2081,7 @@ function DATABASE:_RegisterTemplates() for group_num, Template in pairs(obj_type_data.group) do if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group - + self:_RegisterGroupTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID) else From 1beb5934c2ecaa6a1b446cd7bd594f3765f1730b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 18 Apr 2024 14:51:53 +0200 Subject: [PATCH 546/603] xxx --- Moose Development/Moose/Core/Database.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index e17b01757..bdd3fcaae 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1074,7 +1074,7 @@ end -- @param #string unitname Name of the associated unit. -- @return #number Octal function DATABASE:GetNextSTN(octal,unitname) - local first = UTILS.OctalToDecimal(octal) + local first = UTILS.OctalToDecimal(octal) or 0 if self.STNS[first] == unitname then return octal end local nextoctal = 77777 local found = false @@ -1111,7 +1111,7 @@ end -- @param #string unitname Name of the associated unit. -- @return #number Octal function DATABASE:GetNextSADL(octal,unitname) - local first = UTILS.OctalToDecimal(octal) + local first = UTILS.OctalToDecimal(octal) or 0 if self.SADL[first] == unitname then return octal end local nextoctal = 7777 local found = false From 235cbbe23a8e0b1d4a568e622cd66d5f08787dd2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 19 Apr 2024 11:32:35 +0200 Subject: [PATCH 547/603] #CSAR - add'l logging --- Moose Development/Moose/Ops/CSAR.lua | 68 +++++++++++++++++++--------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index f1d366ca4..905798115 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -30,8 +30,8 @@ -- @module Ops.CSAR -- @image OPS_CSAR.jpg --- Date: May 2023 --- Last: Update Dec 2024 +--- +-- Last Update April 2024 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM @@ -294,7 +294,7 @@ CSAR.AircraftType["MH-60R"] = 10 --- CSAR class version. -- @field #string version -CSAR.version="1.0.20" +CSAR.version="1.0.21" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -463,7 +463,7 @@ function CSAR:New(Coalition, Template, Alias) self.SRSModulation = radio.modulation.AM -- modulation self.SRSport = 5002 -- port self.SRSCulture = "en-GB" - self.SRSVoice = nil + self.SRSVoice = MSRS.Voices.Google.Standard.en_GB_Standard_B self.SRSGPathToCredentials = nil self.SRSVolume = 1.0 -- volume 0.0 to 1.0 self.SRSGender = "male" -- male or female @@ -1190,7 +1190,7 @@ function CSAR:_EventHandler(EventData) if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then self:__Landed(2,_event.IniUnitName, _place) - self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true) + self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true,true) else self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition())) end @@ -1529,7 +1529,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG local _reset = true if (_distance < 500) then - + self:T(self.lid .. "[Pickup Debug] Helo closer than 500m: ".._lookupKeyHeli) if self.heliCloseMessage[_lookupKeyHeli] == nil then if self.autosmoke == true then self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land or hover at the smoke.", self:_GetCustomCallSign(_heliName), _pilotName), self.messageTime,false,true) @@ -1538,14 +1538,16 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG end self.heliCloseMessage[_lookupKeyHeli] = true end - + self:T(self.lid .. "[Pickup Debug] Checking landed vs Hover for ".._lookupKeyHeli) -- have we landed close enough? if not _heliUnit:InAir() then - + self:T(self.lid .. "[Pickup Debug] Helo landed: ".._lookupKeyHeli) if self.pilotRuntoExtractPoint == true then if (_distance < self.extractDistance) then local _time = self.landedStatus[_lookupKeyHeli] + self:T(self.lid .. "[Pickup Debug] Check pilot running or arrived ".._lookupKeyHeli) if _time == nil then + self:T(self.lid .. "[Pickup Debug] Pilot running not arrived yet ".._lookupKeyHeli) self.landedStatus[_lookupKeyHeli] = math.floor( (_distance - self.loadDistance) / 3.6 ) _time = self.landedStatus[_lookupKeyHeli] _woundedGroup:OptionAlarmStateGreen() @@ -1556,11 +1558,15 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG self.landedStatus[_lookupKeyHeli] = _time end --if _time <= 0 or _distance < self.loadDistance then + self:T(self.lid .. "[Pickup Debug] Pilot close enough? ".._lookupKeyHeli) if _distance < self.loadDistance + 5 or _distance <= 13 then + self:T(self.lid .. "[Pickup Debug] Pilot close enough - YES ".._lookupKeyHeli) if self.pilotmustopendoors and (self:_IsLoadingDoorOpen(_heliName) == false) then self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true, true) + self:T(self.lid .. "[Pickup Debug] Door closed, try again next loop ".._lookupKeyHeli) return false else + self:T(self.lid .. "[Pickup Debug] Pick up Pilot ".._lookupKeyHeli) self.landedStatus[_lookupKeyHeli] = nil self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) return true @@ -1568,28 +1574,32 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG end end else + self:T(self.lid .. "[Pickup Debug] Helo landed, pilot NOT set to run to helo ".._lookupKeyHeli) if (_distance < self.loadDistance) then + self:T(self.lid .. "[Pickup Debug] Helo close enough, door check ".._lookupKeyHeli) if self.pilotmustopendoors and (self:_IsLoadingDoorOpen(_heliName) == false) then + self:T(self.lid .. "[Pickup Debug] Door closed, try again next loop ".._lookupKeyHeli) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true, true) return false else + self:T(self.lid .. "[Pickup Debug] Pick up Pilot ".._lookupKeyHeli) self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) return true end end end else - + self:T(self.lid .. "[Pickup Debug] Helo hovering".._lookupKeyHeli) local _unitsInHelicopter = self:_PilotsOnboard(_heliName) local _maxUnits = self.AircraftType[_heliUnit:GetTypeName()] if _maxUnits == nil then _maxUnits = self.max_units end - + self:T(self.lid .. "[Pickup Debug] Check capacity and close enough for winching ".._lookupKeyHeli) if _heliUnit:InAir() and _unitsInHelicopter + 1 <= _maxUnits then -- DONE - make variable if _distance < self.rescuehoverdistance then - + self:T(self.lid .. "[Pickup Debug] Helo hovering close enough ".._lookupKeyHeli) --check height! local leaderheight = _woundedLeader:GetHeight() if leaderheight < 0 then leaderheight = 0 end @@ -1597,7 +1607,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG -- DONE - make variable if _height <= self.rescuehoverheight then - + self:T(self.lid .. "[Pickup Debug] Helo hovering low enough ".._lookupKeyHeli) local _time = self.hoverStatus[_lookupKeyHeli] if _time == nil then @@ -1607,22 +1617,28 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG _time = self.hoverStatus[_lookupKeyHeli] - 10 self.hoverStatus[_lookupKeyHeli] = _time end - + self:T(self.lid .. "[Pickup Debug] Check hover timer ".._lookupKeyHeli) if _time > 0 then + self:T(self.lid .. "[Pickup Debug] Helo hovering not long enough ".._lookupKeyHeli) self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you\'re too far away!", self.messageTime, true) else + self:T(self.lid .. "[Pickup Debug] Helo hovering long enough - door check ".._lookupKeyHeli) if self.pilotmustopendoors and (self:_IsLoadingDoorOpen(_heliName) == false) then self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true, true) + self:T(self.lid .. "[Pickup Debug] Door closed, try again next loop ".._lookupKeyHeli) return false else self.hoverStatus[_lookupKeyHeli] = nil self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) + self:T(self.lid .. "[Pickup Debug] Pilot picked up ".._lookupKeyHeli) return true end end _reset = false else + self:T(self.lid .. "[Pickup Debug] Helo hovering too high ".._lookupKeyHeli) self:_DisplayMessageToSAR(_heliUnit, "Too high to winch " .. _pilotName .. " \nReduce height and hover for 10 seconds!", self.messageTime, true,true) + self:T(self.lid .. "[Pickup Debug] Hovering too high, try again next loop ".._lookupKeyHeli) return false end end @@ -1647,7 +1663,8 @@ end -- @param #string heliname Heli name -- @param #string groupname Group name -- @param #boolean isairport If true, EVENT.Landing took place at an airport or FARP -function CSAR:_ScheduledSARFlight(heliname,groupname, isairport) +-- @param #boolean noreschedule If true, do not try to reschedule this is distances are not ok (coming from landing event) +function CSAR:_ScheduledSARFlight(heliname,groupname, isairport, noreschedule) self:T(self.lid .. " _ScheduledSARFlight") self:T({heliname,groupname}) local _heliUnit = self:_GetSARHeli(heliname) @@ -1667,20 +1684,29 @@ function CSAR:_ScheduledSARFlight(heliname,groupname, isairport) local _dist = self:_GetClosestMASH(_heliUnit) if _dist == -1 then - return + self:T(self.lid.."[Drop off debug] Check distance to MASH for "..heliname.." Distance can not be determined!") + return end - + + self:T(self.lid.."[Drop off debug] Check distance to MASH for "..heliname.." Distance km: "..math.floor(_dist/1000)) + if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then + self:T(self.lid.."[Drop off debug] Distance ok, door check") if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true, true) + self:T(self.lid.."[Drop off debug] Door closed, try again next loop") else + self:T(self.lid.."[Drop off debug] Rescued!") self:_RescuePilots(_heliUnit) return end end --queue up - self:__Returning(-5,heliname,_woundedGroupName, isairport) + if not noreschedule then + self:__Returning(5,heliname,_woundedGroupName, isairport) + self:ScheduleOnce(5,self._ScheduledSARFlight,self,heliname,groupname, isairport, noreschedule) + end return self end @@ -1752,7 +1778,7 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak, _overrid _text = string.gsub(_text,"nm"," nautical miles") --self.msrs:SetVoice(self.SRSVoice) --self.SRSQueue:NewTransmission(_text,nil,self.msrs,nil,1) - self:I("Voice = "..self.SRSVoice) + --self:I("Voice = "..self.SRSVoice) self.SRSQueue:NewTransmission(_text,duration,self.msrs,tstart,2,subgroups,subtitle,subduration,self.SRSchannel,self.SRSModulation,gender,culture,self.SRSVoice,volume,label,coord) end return self @@ -1981,7 +2007,7 @@ end --- (Internal) Determine distance to closest MASH. -- @param #CSAR self -- @param Wrapper.Unit#UNIT _heli Helicopter #UNIT --- @retunr +-- @return #CSAR self function CSAR:_GetClosestMASH(_heli) self:T(self.lid .. " _GetClosestMASH") local _mashset = self.mash -- Core.Set#SET_GROUP @@ -2219,7 +2245,7 @@ function CSAR:_RefreshRadioBeacons() if self:_CountActiveDownedPilots() > 0 then local PilotTable = self.downedPilots for _,_pilot in pairs (PilotTable) do - self:T({_pilot}) + self:T({_pilot.name}) local pilot = _pilot -- #CSAR.DownedPilot local group = pilot.group local frequency = pilot.frequency or 0 -- thanks to @Thrud @@ -2501,7 +2527,7 @@ end -- @param #boolean IsAirport True if heli has landed on an AFB (from event land). function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname, IsAirPort) self:T({From, Event, To, Heliname, Woundedgroupname}) - self:_ScheduledSARFlight(Heliname,Woundedgroupname, IsAirPort) + --self:_ScheduledSARFlight(Heliname,Woundedgroupname, IsAirPort) return self end From 89d945037d6333a8fb1718ae42ecc17c6b510716 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 19 Apr 2024 15:54:30 +0200 Subject: [PATCH 548/603] xxx --- .../Moose/AI/AI_A2A_Dispatcher.lua | 38 +++++++++---------- .../Moose/AI/AI_A2G_Dispatcher.lua | 20 +++++----- Moose Development/Moose/AI/AI_Air.lua | 29 ++++++++------ .../Moose/AI/AI_Air_Dispatcher.lua | 6 +-- Moose Development/Moose/AI/AI_Air_Engage.lua | 28 ++++++++------ .../Moose/AI/AI_Air_Squadron.lua | 2 +- Moose Development/Moose/AI/AI_Cargo.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- Moose Development/Moose/AI/AI_Escort.lua | 2 +- .../Moose/AI/AI_Escort_Dispatcher.lua | 22 +++++------ Moose Development/Moose/AI/AI_Patrol.lua | 4 +- 11 files changed, 83 insertions(+), 72 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index e2ced1002..bbfaa868b 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1151,14 +1151,14 @@ do -- AI_A2A_DISPATCHER local AirbaseName = EventData.PlaceName -- The name of the airbase that was captured. - self:I( "Captured " .. AirbaseName ) + self:T( "Captured " .. AirbaseName ) -- Now search for all squadrons located at the airbase, and sanitize them. for SquadronName, Squadron in pairs( self.DefenderSquadrons ) do if Squadron.AirbaseName == AirbaseName then Squadron.ResourceCount = -999 -- The base has been captured, and the resources are eliminated. No more spawning. Squadron.Captured = true - self:I( "Squadron " .. SquadronName .. " captured." ) + self:T( "Squadron " .. SquadronName .. " captured." ) end end end @@ -1828,7 +1828,7 @@ do -- AI_A2A_DISPATCHER self:SetSquadronCapInterval( SquadronName, self.DefenderDefault.CapLimit, self.DefenderDefault.CapMinSeconds, self.DefenderDefault.CapMaxSeconds, 1 ) - self:I( { CAP = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, Zone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, EngageAltType } } ) + self:T( { CAP = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, Zone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, EngageAltType } } ) -- Add the CAP to the EWR network. @@ -2085,7 +2085,7 @@ do -- AI_A2A_DISPATCHER Intercept.EngageCeilingAltitude = EngageCeilingAltitude Intercept.EngageAltType = EngageAltType - self:I( { GCI = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) + self:T( { GCI = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) end --- Set squadron GCI. @@ -3000,17 +3000,17 @@ do -- AI_A2A_DISPATCHER for FriendlyDistance, AIFriendly in UTILS.spairs( DefenderFriendlies or {} ) do -- We only allow to ENGAGE targets as long as the Units on both sides are balanced. if AttackerCount > DefenderCount then - --self:I("***** AI_A2A_DISPATCHER:CountDefendersToBeEngaged() *****\nThis is supposed to be a UNIT:") + --self:T("***** AI_A2A_DISPATCHER:CountDefendersToBeEngaged() *****\nThis is supposed to be a UNIT:") if AIFriendly then local classname = AIFriendly.ClassName or "No Class Name" local unitname = AIFriendly.IdentifiableName or "No Unit Name" - --self:I("Class Name: " .. classname) - --self:I("Unit Name: " .. unitname) - --self:I({AIFriendly}) + --self:T("Class Name: " .. classname) + --self:T("Unit Name: " .. unitname) + --self:T({AIFriendly}) end local Friendly = nil if AIFriendly and AIFriendly:IsAlive() then - --self:I("AIFriendly alive, getting GROUP") + --self:T("AIFriendly alive, getting GROUP") Friendly = AIFriendly:GetGroup() -- Wrapper.Group#GROUP end @@ -4322,23 +4322,23 @@ do -- Setup squadrons - self:I( { Airbases = AirbaseNames } ) + self:T( { Airbases = AirbaseNames } ) - self:I( "Defining Templates for Airbases ..." ) + self:T( "Defining Templates for Airbases ..." ) for AirbaseID, AirbaseName in pairs( AirbaseNames ) do local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE local AirbaseName = Airbase:GetName() local AirbaseCoord = Airbase:GetCoordinate() local AirbaseZone = ZONE_RADIUS:New( "Airbase", AirbaseCoord:GetVec2(), 3000 ) local Templates = nil - self:I( { Airbase = AirbaseName } ) + self:T( { Airbase = AirbaseName } ) for TemplateID, Template in pairs( self.Templates:GetSet() ) do local Template = Template -- Wrapper.Group#GROUP local TemplateCoord = Template:GetCoordinate() if AirbaseZone:IsVec2InZone( TemplateCoord:GetVec2() ) then Templates = Templates or {} table.insert( Templates, Template:GetName() ) - self:I( { Template = Template:GetName() } ) + self:T( { Template = Template:GetName() } ) end end if Templates then @@ -4354,13 +4354,13 @@ do self.CAPTemplates:FilterPrefixes( CapPrefixes ) self.CAPTemplates:FilterOnce() - self:I( "Setting up CAP ..." ) + self:T( "Setting up CAP ..." ) for CAPID, CAPTemplate in pairs( self.CAPTemplates:GetSet() ) do local CAPZone = ZONE_POLYGON:New( CAPTemplate:GetName(), CAPTemplate ) -- Now find the closest airbase from the ZONE (start or center) local AirbaseDistance = 99999999 local AirbaseClosest = nil -- Wrapper.Airbase#AIRBASE - self:I( { CAPZoneGroup = CAPID } ) + self:T( { CAPZoneGroup = CAPID } ) for AirbaseID, AirbaseName in pairs( AirbaseNames ) do local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE local AirbaseName = Airbase:GetName() @@ -4368,7 +4368,7 @@ do local Squadron = self.DefenderSquadrons[AirbaseName] if Squadron then local Distance = AirbaseCoord:Get2DDistance( CAPZone:GetCoordinate() ) - self:I( { AirbaseDistance = Distance } ) + self:T( { AirbaseDistance = Distance } ) if Distance < AirbaseDistance then AirbaseDistance = Distance AirbaseClosest = Airbase @@ -4376,7 +4376,7 @@ do end end if AirbaseClosest then - self:I( { CAPAirbase = AirbaseClosest:GetName() } ) + self:T( { CAPAirbase = AirbaseClosest:GetName() } ) self:SetSquadronCap( AirbaseClosest:GetName(), CAPZone, 6000, 10000, 500, 800, 800, 1200, "RADIO" ) self:SetSquadronCapInterval( AirbaseClosest:GetName(), CapLimit, 300, 600, 1 ) end @@ -4384,14 +4384,14 @@ do -- Setup GCI. -- GCI is setup for all Squadrons. - self:I( "Setting up GCI ..." ) + self:T( "Setting up GCI ..." ) for AirbaseID, AirbaseName in pairs( AirbaseNames ) do local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE local AirbaseName = Airbase:GetName() local Squadron = self.DefenderSquadrons[AirbaseName] self:F( { Airbase = AirbaseName } ) if Squadron then - self:I( { GCIAirbase = AirbaseName } ) + self:T( { GCIAirbase = AirbaseName } ) self:SetSquadronGci( AirbaseName, 800, 1200 ) end end diff --git a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua index c2bd2de0e..451414f7f 100644 --- a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua @@ -1147,7 +1147,7 @@ do -- AI_A2G_DISPATCHER for Resource = 1, DefenderSquadron.ResourceCount or 0 do self:ResourcePark( DefenderSquadron ) end - self:I( "Parked resources for squadron " .. DefenderSquadron.Name ) + self:T( "Parked resources for squadron " .. DefenderSquadron.Name ) end end @@ -1224,14 +1224,14 @@ do -- AI_A2G_DISPATCHER local AirbaseName = EventData.PlaceName -- The name of the airbase that was captured. - self:I( "Captured " .. AirbaseName ) + self:T( "Captured " .. AirbaseName ) -- Now search for all squadrons located at the airbase, and sanitize them. for SquadronName, Squadron in pairs( self.DefenderSquadrons ) do if Squadron.AirbaseName == AirbaseName then Squadron.ResourceCount = -999 -- The base has been captured, and the resources are eliminated. No more spawning. Squadron.Captured = true - self:I( "Squadron " .. SquadronName .. " captured." ) + self:T( "Squadron " .. SquadronName .. " captured." ) end end end @@ -2144,7 +2144,7 @@ do -- AI_A2G_DISPATCHER Sead.EngageAltType = EngageAltType Sead.Defend = true - self:I( { SEAD = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) + self:T( { SEAD = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) return self end @@ -2234,7 +2234,7 @@ do -- AI_A2G_DISPATCHER self:SetSquadronPatrolInterval( SquadronName, self.DefenderDefault.PatrolLimit, self.DefenderDefault.PatrolMinSeconds, self.DefenderDefault.PatrolMaxSeconds, 1, "SEAD" ) - self:I( { SEAD = { Zone:GetName(), PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) + self:T( { SEAD = { Zone:GetName(), PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) end @@ -2295,7 +2295,7 @@ do -- AI_A2G_DISPATCHER Cas.EngageAltType = EngageAltType Cas.Defend = true - self:I( { CAS = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) + self:T( { CAS = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) return self end @@ -2385,7 +2385,7 @@ do -- AI_A2G_DISPATCHER self:SetSquadronPatrolInterval( SquadronName, self.DefenderDefault.PatrolLimit, self.DefenderDefault.PatrolMinSeconds, self.DefenderDefault.PatrolMaxSeconds, 1, "CAS" ) - self:I( { CAS = { Zone:GetName(), PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) + self:T( { CAS = { Zone:GetName(), PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) end @@ -2446,7 +2446,7 @@ do -- AI_A2G_DISPATCHER Bai.EngageAltType = EngageAltType Bai.Defend = true - self:I( { BAI = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) + self:T( { BAI = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) return self end @@ -2536,7 +2536,7 @@ do -- AI_A2G_DISPATCHER self:SetSquadronPatrolInterval( SquadronName, self.DefenderDefault.PatrolLimit, self.DefenderDefault.PatrolMinSeconds, self.DefenderDefault.PatrolMaxSeconds, 1, "BAI" ) - self:I( { BAI = { Zone:GetName(), PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) + self:T( { BAI = { Zone:GetName(), PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) end @@ -3894,7 +3894,7 @@ do -- AI_A2G_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) if Squadron then - local FirstUnit = AttackSetUnit:GetFirst() + local FirstUnit = AttackSetUnit:GetRandomSurely() local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE if self.SetSendPlayerMessages then Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", on route to ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup ) diff --git a/Moose Development/Moose/AI/AI_Air.lua b/Moose Development/Moose/AI/AI_Air.lua index 5e598197c..08c85e751 100644 --- a/Moose Development/Moose/AI/AI_Air.lua +++ b/Moose Development/Moose/AI/AI_Air.lua @@ -465,7 +465,7 @@ function AI_AIR:onafterStatus() local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() ) if DistanceFromHomeBase > self.DisengageRadius then - self:I( self.Controllable:GetName() .. " is too far from home base, RTB!" ) + self:T( self.Controllable:GetName() .. " is too far from home base, RTB!" ) self:Hold( 300 ) RTB = false end @@ -489,10 +489,10 @@ function AI_AIR:onafterStatus() if Fuel < self.FuelThresholdPercentage then if self.TankerName then - self:I( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" ) + self:T( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" ) self:Refuel() else - self:I( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" ) + self:T( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" ) local OldAIControllable = self.Controllable local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed ) @@ -518,7 +518,7 @@ function AI_AIR:onafterStatus() -- Note that a group can consist of more units, so if one unit is damaged of a group, the mission may continue. -- The damaged unit will RTB due to DCS logic, and the others will continue to engage. if ( Damage / InitialLife ) < self.PatrolDamageThreshold then - self:I( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" ) + self:T( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" ) self:Damaged() RTB = true self:SetStatusOff() @@ -536,7 +536,7 @@ function AI_AIR:onafterStatus() if Damage ~= InitialLife then self:Damaged() else - self:I( self.Controllable:GetName() .. " control lost! " ) + self:T( self.Controllable:GetName() .. " control lost! " ) self:LostControl() end @@ -617,7 +617,10 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To ) --- Calculate the target route point. local FromCoord = AIGroup:GetCoordinate() + if not FromCoord then return end + local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!) + local ToTargetVec3 = ToTargetCoord:GetVec3() ToTargetVec3.y = ToTargetCoord:GetLandHeight()+3000 -- let's set this 1000m/3000 feet above ground local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 ) @@ -638,13 +641,13 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To ) local ToAirbaseCoord = ToTargetCoord2 if Distance < 5000 then - self:I( "RTB and near the airbase!" ) + self:T( "RTB and near the airbase!" ) self:Home() return end if not AIGroup:InAir() == true then - self:I( "Not anymore in the air, considered Home." ) + self:T( "Not anymore in the air, considered Home." ) self:Home() return end @@ -691,7 +694,7 @@ end function AI_AIR:onafterHome( AIGroup, From, Event, To ) self:F( { AIGroup, From, Event, To } ) - self:I( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" ) + self:T( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" ) if AIGroup and AIGroup:IsAlive() then end @@ -705,10 +708,12 @@ end function AI_AIR:onafterHold( AIGroup, From, Event, To, HoldTime ) self:F( { AIGroup, From, Event, To } ) - self:I( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" ) + self:T( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" ) if AIGroup and AIGroup:IsAlive() then - local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed ) + local Coordinate = AIGroup:GetCoordinate() + if Coordinate == nil then return end + local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed, Coordinate ) local TimedOrbitTask = AIGroup:TaskControlled( OrbitTask, AIGroup:TaskCondition( nil, nil, nil, nil, HoldTime , nil ) ) local RTBTask = AIGroup:TaskFunction( "AI_AIR.RTBHold", self ) @@ -725,7 +730,7 @@ end -- @param Wrapper.Group#GROUP AIGroup function AI_AIR.Resume( AIGroup, Fsm ) - AIGroup:I( { "AI_AIR.Resume:", AIGroup:GetName() } ) + AIGroup:T( { "AI_AIR.Resume:", AIGroup:GetName() } ) if AIGroup:IsAlive() then Fsm:__RTB( Fsm.TaskDelay ) end @@ -744,7 +749,7 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To ) if Tanker and Tanker:IsAlive() and Tanker:IsAirPlane() then - self:I( "Group " .. self.Controllable:GetName() .. " ... Refuelling! State=" .. self:GetState() .. ", Refuelling tanker " .. self.TankerName ) + self:T( "Group " .. self.Controllable:GetName() .. " ... Refuelling! State=" .. self:GetState() .. ", Refuelling tanker " .. self.TankerName ) local RefuelRoute = {} diff --git a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua index b10f2c0e0..9e5939aa0 100644 --- a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua @@ -1141,7 +1141,7 @@ do -- AI_AIR_DISPATCHER for Resource = 1, DefenderSquadron.ResourceCount or 0 do self:ResourcePark( DefenderSquadron ) end - self:I( "Parked resources for squadron " .. DefenderSquadron.Name ) + self:T( "Parked resources for squadron " .. DefenderSquadron.Name ) end end @@ -1217,14 +1217,14 @@ do -- AI_AIR_DISPATCHER local AirbaseName = EventData.PlaceName -- The name of the airbase that was captured. - self:I( "Captured " .. AirbaseName ) + self:T( "Captured " .. AirbaseName ) -- Now search for all squadrons located at the airbase, and sanitize them. for SquadronName, Squadron in pairs( self.DefenderSquadrons ) do if Squadron.AirbaseName == AirbaseName then Squadron.ResourceCount = -999 -- The base has been captured, and the resources are eliminated. No more spawning. Squadron.Captured = true - self:I( "Squadron " .. SquadronName .. " captured." ) + self:T( "Squadron " .. SquadronName .. " captured." ) end end end diff --git a/Moose Development/Moose/AI/AI_Air_Engage.lua b/Moose Development/Moose/AI/AI_Air_Engage.lua index c55c9e8c7..70898d2ba 100644 --- a/Moose Development/Moose/AI/AI_Air_Engage.lua +++ b/Moose Development/Moose/AI/AI_Air_Engage.lua @@ -389,7 +389,7 @@ end -- @param Wrapper.Group#GROUP AIControllable function AI_AIR_ENGAGE.___EngageRoute( AIGroup, Fsm, AttackSetUnit ) - Fsm:I(string.format("AI_AIR_ENGAGE.___EngageRoute: %s", tostring(AIGroup:GetName()))) + Fsm:T(string.format("AI_AIR_ENGAGE.___EngageRoute: %s", tostring(AIGroup:GetName()))) if AIGroup and AIGroup:IsAlive() then Fsm:__EngageRoute( Fsm.TaskDelay or 0.1, AttackSetUnit ) @@ -404,7 +404,7 @@ end -- @param #string To The To State string. -- @param Core.Set#SET_UNIT AttackSetUnit Unit set to be attacked. function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit ) - self:I( { DefenderGroup, From, Event, To, AttackSetUnit } ) + self:T( { DefenderGroup, From, Event, To, AttackSetUnit } ) local DefenderGroupName = DefenderGroup:GetName() @@ -426,7 +426,13 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac local DefenderCoord = DefenderGroup:GetPointVec3() DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude. - local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3() + local TargetCoord = AttackSetUnit:GetRandomSurely():GetPointVec3() + + if TargetCoord == nil then + self:Return() + return + end + TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude. local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord ) @@ -435,12 +441,12 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac -- TODO: A factor of * 3 is way too close. This causes the AI not to engange until merged sometimes! if TargetDistance <= EngageDistance * 9 then - --self:I(string.format("AI_AIR_ENGAGE onafterEngageRoute ==> __Engage - target distance = %.1f km", TargetDistance/1000)) + --self:T(string.format("AI_AIR_ENGAGE onafterEngageRoute ==> __Engage - target distance = %.1f km", TargetDistance/1000)) self:__Engage( 0.1, AttackSetUnit ) else - --self:I(string.format("FF AI_AIR_ENGAGE onafterEngageRoute ==> Routing - target distance = %.1f km", TargetDistance/1000)) + --self:T(string.format("FF AI_AIR_ENGAGE onafterEngageRoute ==> Routing - target distance = %.1f km", TargetDistance/1000)) local EngageRoute = {} local AttackTasks = {} @@ -472,7 +478,7 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac end else -- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling! - self:I( DefenderGroupName .. ": No targets found -> Going RTB") + self:T( DefenderGroupName .. ": No targets found -> Going RTB") self:Return() end end @@ -481,7 +487,7 @@ end -- @param Wrapper.Group#GROUP AIControllable function AI_AIR_ENGAGE.___Engage( AIGroup, Fsm, AttackSetUnit ) - Fsm:I(string.format("AI_AIR_ENGAGE.___Engage: %s", tostring(AIGroup:GetName()))) + Fsm:T(string.format("AI_AIR_ENGAGE.___Engage: %s", tostring(AIGroup:GetName()))) if AIGroup and AIGroup:IsAlive() then local delay=Fsm.TaskDelay or 0.1 @@ -516,7 +522,7 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU local DefenderCoord = DefenderGroup:GetPointVec3() DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude. - local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3() + local TargetCoord = AttackSetUnit:GetRandomSurely():GetPointVec3() if not TargetCoord then self:Return() return @@ -547,12 +553,12 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU local AttackUnitTasks = self:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) -- Polymorphic if #AttackUnitTasks == 0 then - self:I( DefenderGroupName .. ": No valid targets found -> Going RTB") + self:T( DefenderGroupName .. ": No valid targets found -> Going RTB") self:Return() return else local text=string.format("%s: Engaging targets at distance %.2f NM", DefenderGroupName, UTILS.MetersToNM(TargetDistance)) - self:I(text) + self:T(text) DefenderGroup:OptionROEOpenFire() DefenderGroup:OptionROTEvadeFire() DefenderGroup:OptionKeepWeaponsOnThreat() @@ -569,7 +575,7 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU end else -- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling! - self:I( DefenderGroupName .. ": No targets found -> returning.") + self:T( DefenderGroupName .. ": No targets found -> returning.") self:Return() return end diff --git a/Moose Development/Moose/AI/AI_Air_Squadron.lua b/Moose Development/Moose/AI/AI_Air_Squadron.lua index bc19c90cb..6651a92a5 100644 --- a/Moose Development/Moose/AI/AI_Air_Squadron.lua +++ b/Moose Development/Moose/AI/AI_Air_Squadron.lua @@ -38,7 +38,7 @@ AI_AIR_SQUADRON = { -- @return #AI_AIR_SQUADRON function AI_AIR_SQUADRON:New( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount ) - self:I( { Air_Squadron = { SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } ) + self:T( { Air_Squadron = { SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } ) local AI_Air_Squadron = BASE:New() -- #AI_AIR_SQUADRON diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 8133bfe43..0bd6ab9ea 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -547,7 +547,7 @@ function AI_CARGO:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit, for _, CarrierUnit in pairs( Carrier:GetUnits() ) do local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT local IsEmpty = CarrierUnit:IsCargoEmpty() - self:I({ IsEmpty = IsEmpty }) + self:T({ IsEmpty = IsEmpty }) if not IsEmpty then AllUnloaded = false break diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index d0f567731..71b7f9f43 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -1161,7 +1161,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() else local text=string.format("WARNING: Cargo %s is too heavy to be loaded into transport. Cargo weight %.1f > %.1f load capacity of carrier %s.", tostring(Cargo:GetName()), Cargo:GetWeight(), LargestLoadCapacity, tostring(Carrier:GetName())) - self:I(text) + self:T(text) end end end diff --git a/Moose Development/Moose/AI/AI_Escort.lua b/Moose Development/Moose/AI/AI_Escort.lua index a063cc31d..ad325ed94 100644 --- a/Moose Development/Moose/AI/AI_Escort.lua +++ b/Moose Development/Moose/AI/AI_Escort.lua @@ -556,7 +556,7 @@ function AI_ESCORT:SetFlightMenuFormation( Formation ) if MenuFormation then local Arguments = MenuFormation.Arguments - --self:I({Arguments=unpack(Arguments)}) + --self:T({Arguments=unpack(Arguments)}) local FlightMenuFormation = MENU_GROUP:New( self.PlayerGroup, "Formation", self.MainMenu ) local MenuFlightFormationID = MENU_GROUP_COMMAND:New( self.PlayerGroup, Formation, FlightMenuFormation, function ( self, Formation, ... ) diff --git a/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua b/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua index ea1598fd1..ff4c0ddfe 100644 --- a/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua @@ -110,11 +110,11 @@ function AI_ESCORT_DISPATCHER:OnEventExit( EventData ) local PlayerGroup = EventData.IniGroup local PlayerUnit = EventData.IniUnit - self:I({EscortAirbase= self.EscortAirbase } ) - self:I({PlayerGroupName = PlayerGroupName } ) - self:I({PlayerGroup = PlayerGroup}) - self:I({FirstGroup = self.CarrierSet:GetFirst()}) - self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )}) + self:T({EscortAirbase= self.EscortAirbase } ) + self:T({PlayerGroupName = PlayerGroupName } ) + self:T({PlayerGroup = PlayerGroup}) + self:T({FirstGroup = self.CarrierSet:GetFirst()}) + self:T({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )}) if self.CarrierSet:FindGroup( PlayerGroupName ) then if self.AI_Escorts[PlayerGroupName] then @@ -133,17 +133,17 @@ function AI_ESCORT_DISPATCHER:OnEventBirth( EventData ) local PlayerGroup = EventData.IniGroup local PlayerUnit = EventData.IniUnit - self:I({EscortAirbase= self.EscortAirbase } ) - self:I({PlayerGroupName = PlayerGroupName } ) - self:I({PlayerGroup = PlayerGroup}) - self:I({FirstGroup = self.CarrierSet:GetFirst()}) - self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )}) + self:T({EscortAirbase= self.EscortAirbase } ) + self:T({PlayerGroupName = PlayerGroupName } ) + self:T({PlayerGroup = PlayerGroup}) + self:T({FirstGroup = self.CarrierSet:GetFirst()}) + self:T({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )}) if self.CarrierSet:FindGroup( PlayerGroupName ) then if not self.AI_Escorts[PlayerGroupName] then local LeaderUnit = PlayerUnit local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot ) - self:I({EscortGroup = EscortGroup}) + self:T({EscortGroup = EscortGroup}) self:ScheduleOnce( 1, function( EscortGroup ) diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 8e86b3802..d5ce61d72 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -838,7 +838,7 @@ function AI_PATROL_ZONE:onafterStatus() local Fuel = self.Controllable:GetFuelMin() if Fuel < self.PatrolFuelThresholdPercentage then - self:I( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" ) + self:T( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" ) local OldAIControllable = self.Controllable local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed ) @@ -852,7 +852,7 @@ function AI_PATROL_ZONE:onafterStatus() -- TODO: Check GROUP damage function. local Damage = self.Controllable:GetLife() if Damage <= self.PatrolDamageThreshold then - self:I( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" ) + self:T( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" ) RTB = true end From 0b607eb75170874ec62fcec2d6898f9dd9218ec7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 19 Apr 2024 15:55:08 +0200 Subject: [PATCH 549/603] xx --- Moose Development/Moose/Core/Spawn.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index e9747fc9b..0b14b73a2 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1608,8 +1608,8 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth ) RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnOuterRadius, self.SpawnInnerRadius ) numTries = numTries + 1 inZone = SpawnZone:IsVec2InZone(RandomVec2) - self:I("Retrying " .. numTries .. "spawn " .. SpawnTemplate.name .. " in Zone " .. SpawnZone:GetName() .. "!") - self:I(SpawnZone) + --self:I("Retrying " .. numTries .. "spawn " .. SpawnTemplate.name .. " in Zone " .. SpawnZone:GetName() .. "!") + --self:I(SpawnZone) end end if (not inZone) then @@ -1778,7 +1778,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth ) SpawnTemplate.hidden=true end - self:I(SpawnTemplate) + --self:I(SpawnTemplate) -- Set country, coalition and category. SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID From 672f1ae2d64dbddb55837b59ac255e3a0286b3fb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 19 Apr 2024 15:57:02 +0200 Subject: [PATCH 550/603] xxx --- Moose Development/Moose/Wrapper/Group.lua | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index a15005b3c..96ac5d87f 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1187,13 +1187,12 @@ end -- @return Core.Point#COORDINATE The COORDINATE of the GROUP. function GROUP:GetCoordinate() - local Units = self:GetUnits() or {} for _,_unit in pairs(Units) do local FirstUnit = _unit -- Wrapper.Unit#UNIT - if FirstUnit then + if FirstUnit and FirstUnit:IsAlive() then local FirstUnitCoordinate = FirstUnit:GetCoordinate() @@ -1205,6 +1204,22 @@ function GROUP:GetCoordinate() end end + -- no luck, try the API way + + local DCSGroup = Group.getByName(self.GroupName) + local DCSUnits = DCSGroup:getUnits() or {} + for _,_unit in pairs(DCSUnits) do + if Object.isExist(_unit) then + local position = _unit:getPosition() + local point = position.p ~= nil and position.p or _unit:GetPoint() + if point then + --self:I(point) + local coord = COORDINATE:NewFromVec3(point) + return coord + end + end + end + BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } ) end From 9b26ed70422351e8a7bfaca0f2cabd1339ec593c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 20 Apr 2024 16:21:19 +0200 Subject: [PATCH 551/603] xxx --- .../Moose/Functional/Stratego.lua | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 2655500f8..66cc97903 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -759,9 +759,39 @@ function STRATEGO:GetNextHighestWeightNodes(Weight, Coalition) return airbases[weight],weight end +--- [USER] Set the aggregated weight of a single node found by its name manually. +-- @param #STRATEGO self +-- @param #string Name The name to look for. +-- @param #number Weight The weight to be set. +-- @return #boolean success +function STRATEGO:SetNodeWeight(Name,Weight) + self:T(self.lid.."SetNodeWeight") + if Name and Weight and self.airbasetable[Name] then + self.airbasetable[Name].weight = Weight or 0 + return true + else + return false + end +end + +--- [USER] Set the base weight of a single node found by its name manually. +-- @param #STRATEGO self +-- @param #string Name The name to look for. +-- @param #number Weight The weight to be set. +-- @return #boolean success +function STRATEGO:SetNodeBaseWeight(Name,Weight) + self:T(self.lid.."SetNodeBaseWeight") + if Name and Weight and self.airbasetable[Name] then + self.airbasetable[Name].baseweight = Weight or 0 + return true + else + return false + end +end + --- [USER] Get the aggregated weight of a node by its name. -- @param #STRATEGO self --- @param #string Name. +-- @param #string Name The name to look for. -- @return #number Weight The weight or 0 if not found. function STRATEGO:GetNodeWeight(Name) self:T(self.lid.."GetNodeWeight") @@ -774,7 +804,7 @@ end --- [USER] Get the base weight of a node by its name. -- @param #STRATEGO self --- @param #string Name. +-- @param #string Name The name to look for. -- @return #number Weight The base weight or 0 if not found. function STRATEGO:GetNodeBaseWeight(Name) self:T(self.lid.."GetNodeBaseWeight") @@ -787,7 +817,7 @@ end --- [USER] Get the COALITION of a node by its name. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return #number Coalition The coalition. function STRATEGO:GetNodeCoalition(Name) self:T(self.lid.."GetNodeCoalition") @@ -800,7 +830,7 @@ end --- [USER] Get the TYPE of a node by its name. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return #string Type Type of the node, e.g. STRATEGO.Type.AIRBASE or nil if not found. function STRATEGO:GetNodeType(Name) self:T(self.lid.."GetNodeType") @@ -813,7 +843,7 @@ end --- [USER] Get the ZONE of a node by its name. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return Core.Zone#ZONE Zone The Zone of the node or nil if not found. function STRATEGO:GetNodeZone(Name) self:T(self.lid.."GetNodeZone") @@ -826,7 +856,7 @@ end --- [USER] Get the OPSZONE of a node by its name. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return Ops.OpsZone#OPSZONE OpsZone The OpsZone of the node or nil if not found. function STRATEGO:GetNodeOpsZone(Name) self:T(self.lid.."GetNodeOpsZone") @@ -839,7 +869,7 @@ end --- [USER] Get the COORDINATE of a node by its name. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return Core.Point#COORDINATE Coordinate The Coordinate of the node or nil if not found. function STRATEGO:GetNodeCoordinate(Name) self:T(self.lid.."GetNodeCoordinate") @@ -852,7 +882,7 @@ end --- [USER] Check if the TYPE of a node is AIRBASE. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return #boolean Outcome function STRATEGO:IsAirbase(Name) self:T(self.lid.."IsAirbase") @@ -865,7 +895,7 @@ end --- [USER] Check if the TYPE of a node is PORT. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return #boolean Outcome function STRATEGO:IsPort(Name) self:T(self.lid.."IsPort") @@ -878,7 +908,7 @@ end --- [USER] Check if the TYPE of a node is POI. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return #boolean Outcome function STRATEGO:IsPOI(Name) self:T(self.lid.."IsPOI") @@ -891,7 +921,7 @@ end --- [USER] Check if the TYPE of a node is FARP. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return #boolean Outcome function STRATEGO:IsFARP(Name) self:T(self.lid.."IsFARP") @@ -904,7 +934,7 @@ end --- [USER] Check if the TYPE of a node is SHIP. -- @param #STRATEGO self --- @param #string Name. +-- @param #string The name to look for. -- @return #boolean Outcome function STRATEGO:IsShip(Name) self:T(self.lid.."IsShip") From 2f957899e10524ee6e70a57c5671e891e5f65656 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 23 Apr 2024 09:17:31 +0200 Subject: [PATCH 552/603] xxx --- Moose Development/Moose/Core/Spawn.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 4dc93b17d..2f856eff4 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -202,19 +202,19 @@ -- -- ### Link-16 Datalink STN and SADL IDs (limited at the moment to F15/16/18/AWACS/Tanker/B1B, but not the F15E for clients, SADL A10CII only) -- --- *{#SPAWN.InitSTN}(): Set the STN of the first unit in the group. All other units will have consecutive STNs, provided they have not been used yet. --- *{#SPAWN.InitSADL}(): Set the SADL of the first unit in the group. All other units will have consecutive SADLs, provided they have not been used yet. +-- * @{#SPAWN.InitSTN}(): Set the STN of the first unit in the group. All other units will have consecutive STNs, provided they have not been used yet. +-- * @{#SPAWN.InitSADL}(): Set the SADL of the first unit in the group. All other units will have consecutive SADLs, provided they have not been used yet. -- -- ### Callsigns -- --- *{#SPAWN.InitRandomizeCallsign}(): Set a random callsign name per spawn. --- *{#SPAWN.SpawnInitCallSign}(): Set a specific callsign for a spawned group. +-- * @{#SPAWN.InitRandomizeCallsign}(): Set a random callsign name per spawn. +-- * @{#SPAWN.SpawnInitCallSign}(): Set a specific callsign for a spawned group. -- -- ### Speed -- --- *{#SPAWN.InitSpeedMps}(): Set the initial speed on spawning in meters per second. --- *{#SPAWN.InitSpeedKph}(): Set the initial speed on spawning in kilometers per hour. --- *{#SPAWN.InitSpeedKnots}(): Set the initial speed on spawning in knots. +-- * @{#SPAWN.InitSpeedMps}(): Set the initial speed on spawning in meters per second. +-- * @{#SPAWN.InitSpeedKph}(): Set the initial speed on spawning in kilometers per hour. +-- * @{#SPAWN.InitSpeedKnots}(): Set the initial speed on spawning in knots. -- -- ## SPAWN **Spawn** methods -- From 65aeb56c2f3d909fd31280393b46ef489bee19da Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 23 Apr 2024 10:12:11 +0200 Subject: [PATCH 553/603] #STRATEGO -- add SetStrategoZone --- .../Moose/Functional/Stratego.lua | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 97bf316f5..07bac3837 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -15,6 +15,7 @@ -- -- @module Functional.Stratego -- @image Functional.Stratego.png +-- Last Update April 2024 --- @@ -42,6 +43,7 @@ -- @field #number CaptureUnits -- @field #number CaptureThreatlevel -- @field #boolean ExcludeShips +-- @field Core.Zone#ZONE StrategoZone -- @extends Core.Base#BASE -- @extends Core.Fsm#FSM @@ -154,6 +156,7 @@ -- @{#STRATEGO.FindRoute}(): Find a route between two nodes. -- @{#STRATEGO.SetCaptureOptions}(): Set how many units of which minimum threat level are needed to capture one node (i.e. the underlying OpsZone). -- @{#STRATEGO.SetDebug}(): Set debug and draw options. +-- @{#STRATEGO.SetStrategoZone}(): Set a zone to restrict STRATEGO analytics to, can be any kind of ZONE Object. -- -- -- ## Visualisation example code for the Syria map: @@ -177,7 +180,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.6", + version = "0.2.7", portweight = 3, POIweight = 1, maxrunways = 3, @@ -377,6 +380,15 @@ function STRATEGO:SetDebug(Debug,DrawZones,MarkZones) return self end +--- [USER] Restrict Stratego to analyse this zone only. +-- @param #STRATEGO self +-- @param Core.Zone#ZONE Zone The Zone to restrict Stratego to, can be any kind of ZONE Object. +-- @return #STRATEGO self +function STRATEGO:SetStrategoZone(Zone) + self.StrategoZone = Zone + return self +end + --- [USER] Set weights for nodes and routes to determine their importance. -- @param #STRATEGO self -- @param #number MaxRunways Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase node hence equals the number of runways. @@ -425,12 +437,19 @@ function STRATEGO:AnalyseBases() local airbasetable = self.airbasetable local nonconnectedab = self.nonconnectedab local easynames = self.easynames + local zone = self.StrategoZone -- Core.Zone#ZONE_POLYGON -- find bases with >= 1 runways self.bases:ForEach( function(afb) local ab = afb -- Wrapper.Airbase#AIRBASE + local abvec2 = ab:GetVec2() if self.ExcludeShips and ab:IsShip() then return end + if zone ~= nil then + if not zone:IsVec2InZone(abvec2) then + return + end + end local abname = ab:GetName() local runways = ab:GetRunways() local numrwys = #runways From 8e99f3730623cf04d5c29c9e0ee71236cc5b0de6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 27 Apr 2024 17:28:50 +0200 Subject: [PATCH 554/603] xxx --- Moose Development/Moose/Wrapper/Weapon.lua | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Weapon.lua b/Moose Development/Moose/Wrapper/Weapon.lua index 5c9ebc53d..3e75d8672 100644 --- a/Moose Development/Moose/Wrapper/Weapon.lua +++ b/Moose Development/Moose/Wrapper/Weapon.lua @@ -40,6 +40,7 @@ -- @field #number coalition Coalition ID. -- @field #number country Country ID. -- @field DCS#Desc desc Descriptor table. +-- @field DCS#Desc guidance Missile guidance descriptor. -- @field DCS#Unit launcher Launcher DCS unit. -- @field Wrapper.Unit#UNIT launcherUnit Launcher Unit. -- @field #string launcherName Name of launcher unit. @@ -196,6 +197,9 @@ function WEAPON:New(WeaponObject) if self:IsMissile() and self.desc.missileCategory then self.categoryMissile=self.desc.missileCategory + if self.desc.guidance then + self.guidance = self.desc.guidance + end end -- Get type name. @@ -667,6 +671,26 @@ function WEAPON:IsTorpedo() return self.category==Weapon.Category.TORPEDO end +--- Check if weapon is a Fox One missile (Radar Semi-Active). +-- @param #WEAPON self +-- @return #boolean If `true`, is a Fox One. +function WEAPON:IsFoxOne() + return self.guidance==Weapon.GuidanceType.RADAR_SEMI_ACTIVE +end + +--- Check if weapon is a Fox Two missile (IR guided). +-- @param #WEAPON self +-- @return #boolean If `true`, is a Fox Two. +function WEAPON:IsFoxTwo() + return self.guidance==Weapon.GuidanceType.IR +end + +--- Check if weapon is a Fox Three missile (Radar Active). +-- @param #WEAPON self +-- @return #boolean If `true`, is a Fox Three. +function WEAPON:IsFoxThree() + return self.guidance==Weapon.GuidanceType.RADAR_ACTIVE +end --- Destroy the weapon object. -- @param #WEAPON self From 04b4f7cc0fe0ce1adc10f605518ddb5671ea65c4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 6 May 2024 19:01:42 +0200 Subject: [PATCH 555/603] XXX --- Moose Development/Moose/Ops/CSAR.lua | 43 +++++++++++++++------ Moose Development/Moose/Utilities/Utils.lua | 43 +++++++++++++++++++++ 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index f3484453d..e21824e7e 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -295,7 +295,7 @@ CSAR.AircraftType["OH-6A"] = 2 --- CSAR class version. -- @field #string version -CSAR.version="1.0.22" +CSAR.version="1.0.23" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -735,7 +735,7 @@ function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet) :NewWithAlias(template,alias) :InitCoalition(coalition) :InitCountry(country) - :InitAIOnOff(pilotcacontrol) + --:InitAIOnOff(pilotcacontrol) :InitDelayOff() :SpawnFromCoordinate(point) @@ -1239,10 +1239,24 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage, _pla if not _nomessage then if _freq ~= 0 then --shagrat local _text = string.format("%s requests SAR at %s, beacon at %.2f KHz", _groupName, _coordinatesText, _freqk)--shagrat _groupName to prevent 'f15_Pilot_Parachute' - self:_DisplayToAllSAR(_text,self.coalition,self.messageTime) + if self.coordtype ~= 2 then --not MGRS + self:_DisplayToAllSAR(_text,self.coalition,self.messageTime) + else + self:_DisplayToAllSAR(_text,self.coalition,self.messageTime,false,true) + local coordtext = UTILS.MGRSStringToSRSFriendly(_coordinatesText,true) + local _text = string.format("%s requests SAR at %s, beacon at %.2f kilo hertz", _groupName, coordtext, _freqk) + self:_DisplayToAllSAR(_text,self.coalition,self.messageTime,true,false) + end else --shagrat CASEVAC msg local _text = string.format("Pickup Zone at %s.", _coordinatesText ) - self:_DisplayToAllSAR(_text,self.coalition,self.messageTime) + if self.coordtype ~= 2 then --not MGRS + self:_DisplayToAllSAR(_text,self.coalition,self.messageTime) + else + self:_DisplayToAllSAR(_text,self.coalition,self.messageTime,false,true) + local coordtext = UTILS.MGRSStringToSRSFriendly(_coordinatesText,true) + local _text = string.format("Pickup Zone at %s.", coordtext ) + self:_DisplayToAllSAR(_text,self.coalition,self.messageTime,true,false) + end end end @@ -1946,23 +1960,28 @@ end --- (Internal) Display info to all SAR groups. -- @param #CSAR self -- @param #string _message Message to display. --- @param #number _side Coalition of message. +-- @param #number _side Coalition of message. -- @param #number _messagetime How long to show. -function CSAR:_DisplayToAllSAR(_message, _side, _messagetime) +-- @param #boolean ToSRS If true or nil, send to SRS TTS +-- @param #boolean ToScreen If true or nil, send to Screen +function CSAR:_DisplayToAllSAR(_message, _side, _messagetime,ToSRS,ToScreen) self:T(self.lid .. " _DisplayToAllSAR") local messagetime = _messagetime or self.messageTime - if self.msrs then + self:T({_message,ToSRS=ToSRS,ToScreen=ToScreen}) + if self.msrs and (ToSRS == true or ToSRS == nil) then local voice = self.CSARVoice or MSRS.Voices.Google.Standard.en_GB_Standard_F if self.msrs:GetProvider() == MSRS.Provider.WINDOWS then voice = self.CSARVoiceMS or MSRS.Voices.Microsoft.Hedda end - self:I("Voice = "..voice) + self:F("Voice = "..voice) self.SRSQueue:NewTransmission(_message,duration,self.msrs,tstart,2,subgroups,subtitle,subduration,self.SRSchannel,self.SRSModulation,gender,culture,voice,volume,label,self.coordinate) end - for _, _unitName in pairs(self.csarUnits) do - local _unit = self:_GetSARHeli(_unitName) - if _unit and not self.suppressmessages then - self:_DisplayMessageToSAR(_unit, _message, _messagetime) + if ToScreen == true or ToScreen == nil then + for _, _unitName in pairs(self.csarUnits) do + local _unit = self:_GetSARHeli(_unitName) + if _unit and not self.suppressmessages then + self:_DisplayMessageToSAR(_unit, _message, _messagetime) + end end end return self diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 151e65dc2..9d5f40b94 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -3884,3 +3884,46 @@ function UTILS.ClockHeadingString(refHdg,tgtHdg) local clockPos = math.ceil((relativeAngle % 360) / 30) return clockPos.." o'clock" end + +--- Get a NATO abbreviated MGRS text for SRS use, optionally with prosody slow tag +-- @param #string Text The input string, e.g. "MGRS 4Q FJ 12345 67890" +-- @param #boolean Slow Optional - add slow tags +-- @return #string Output for (Slow) spelling in SRS TTS e.g. "MGRS;4;Quebec;Foxtrot;Juliett;1;2;3;4;5;6;7;8;niner;zero;" +function UTILS.MGRSStringToSRSFriendly(Text,Slow) + local Text = string.gsub(Text,"MGRS ","") + Text = string.gsub(Text,"%s+","") + Text = string.gsub(Text,"([%a%d])","%1;") -- "0;5;1;" + Text = string.gsub(Text,"A","Alpha") + Text = string.gsub(Text,"B","Bravo") + Text = string.gsub(Text,"C","Charlie") + Text = string.gsub(Text,"D","Delta") + Text = string.gsub(Text,"E","Echo") + Text = string.gsub(Text,"F","Foxtrot") + Text = string.gsub(Text,"G","Golf") + Text = string.gsub(Text,"H","Hotel") + Text = string.gsub(Text,"I","India") + Text = string.gsub(Text,"J","Juliett") + Text = string.gsub(Text,"K","Kilo") + Text = string.gsub(Text,"L","Lima") + Text = string.gsub(Text,"M","Mike") + Text = string.gsub(Text,"N","November") + Text = string.gsub(Text,"O","Oscar") + Text = string.gsub(Text,"P","Papa") + Text = string.gsub(Text,"Q","Quebec") + Text = string.gsub(Text,"R","Romeo") + Text = string.gsub(Text,"S","Sierra") + Text = string.gsub(Text,"T","Tango") + Text = string.gsub(Text,"U","Uniform") + Text = string.gsub(Text,"V","Victor") + Text = string.gsub(Text,"W","Whiskey") + Text = string.gsub(Text,"X","Xray") + Text = string.gsub(Text,"Y","Yankee") + Text = string.gsub(Text,"Z","Zulu") + Text = string.gsub(Text,"0","zero") + Text = string.gsub(Text,"9","niner") + if Slow then + Text = ''..Text..'' + end + Text = "MGRS;"..Text + return Text +end From b1f3cda7f74c29569375f7e65576344ff10c8936 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 7 May 2024 14:33:49 +0200 Subject: [PATCH 556/603] #STRATEGO - Smarter route finding --- .../Moose/Functional/Stratego.lua | 174 +++++++++++++++--- 1 file changed, 150 insertions(+), 24 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 3eff164af..74caf5cc7 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -15,7 +15,7 @@ -- -- @module Functional.Stratego -- @image Functional.Stratego.png --- Last Update April 2024 +-- Last Update May 2024 --- @@ -181,7 +181,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.8", + version = "0.2.9", portweight = 3, POIweight = 1, maxrunways = 3, @@ -212,9 +212,10 @@ STRATEGO = { -- @field #number coalition -- @field #boolean port -- @field Core.Zone#ZONE_RADIUS zone, --- @field Core.Point#COORDINATRE coord +-- @field Core.Point#COORDINATE coord -- @field #string type -- @field Ops.OpsZone#OPSZONE opszone +-- @field #number connections --- -- @type STRATEGO.DistData @@ -490,6 +491,7 @@ function STRATEGO:AnalyseBases() coord = coord, type = abtype, opszone = opszone, + connections = 0, } airbasetable[abname] = tbl nonconnectedab[abname] = true @@ -576,10 +578,12 @@ function STRATEGO:AnalysePOIs(Set,Weight,Key) coord = coord, type = Key, opszone = opszone, + connections = 0, } - airbasetable[zone:GetName()] = tbl - nonconnectedab[zone:GetName()] = true + airbasetable[zname] = tbl + nonconnectedab[zname] = true local name = string.gsub(zname,"[%p%s]",".") + --self:I({name=name,zone=zname}) easynames[name]=zname end ) @@ -598,6 +602,30 @@ function STRATEGO:GetToFrom(StartPoint,EndPoint) return fromto, tofrom end +--- [USER] Get available connecting nodes from one start node +-- @param #STRATEGO self +-- @param #string StartPoint The starting name +-- @return #boolean found +-- @return #table Nodes +function STRATEGO:GetRoutesFromNode(StartPoint) + self:T(self.lid.."GetRoutesFromNode") + local pstart = string.gsub(StartPoint,"[%p%s]",".") + local found = false + pstart=pstart..";" + local routes = {} + local listed = {} + for _,_data in pairs(self.routexists) do + if string.find(_data,pstart,1,true) and not listed[_data] then + local target = string.gsub(_data,pstart,"") + local fname = self.easynames[target] + table.insert(routes,fname) + found = true + listed[_data] = true + end + end + return found,routes +end + --- [USER] Manually add a route, for e.g. Island hopping or to connect isolated networks. Use **after** STRATEGO has been started! -- @param #STRATEGO self -- @param #string Startpoint Starting Point, e.g. AIRBASE.Syria.Hatay @@ -630,6 +658,8 @@ function STRATEGO:AddRoutesManually(Startpoint,Endpoint,Color,Linetype,Draw) local factor = self.airbasetable[Startpoint].baseweight*self.routefactor self.airbasetable[Startpoint].weight = self.airbasetable[Startpoint].weight+factor self.airbasetable[Endpoint].weight = self.airbasetable[Endpoint].weight+factor + self.airbasetable[Endpoint].connections = self.airbasetable[Endpoint].connections + 2 + self.airbasetable[Startpoint].connections = self.airbasetable[Startpoint].connections+2 if self.debug or Draw then startcoordinate:LineToAll(targetcoordinate,-1,color,1,linetype,nil,string.format("%dkm",dist)) end @@ -648,7 +678,7 @@ function STRATEGO:AnalyseRoutes(tgtrwys,factor,color,linetype) for _,_data in pairs(self.airbasetable) do local fromto,tofrom = self:GetToFrom(startpoint,_data.name) if _data.name == startpoint then - -- sam as we + -- same as we elseif _data.baseweight == tgtrwys and not (self.routexists[fromto] or self.routexists[tofrom]) then local tgtc = _data.coord local dist = UTILS.Round(tgtc:Get2DDistance(startcoord),-2)/1000 @@ -670,6 +700,8 @@ function STRATEGO:AnalyseRoutes(tgtrwys,factor,color,linetype) self.nonconnectedab[startpoint] = false self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+factor self.airbasetable[_data.name].weight = self.airbasetable[_data.name].weight+factor + self.airbasetable[startpoint].connections = self.airbasetable[startpoint].connections + 1 + self.airbasetable[_data.name].connections = self.airbasetable[_data.name].connections + 1 if self.debug then startcoord:LineToAll(tgtc,-1,color,1,linetype,nil,string.format("%dkm",dist)) end @@ -716,6 +748,8 @@ function STRATEGO:AnalyseUnconnected(Color) end self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+1 self.airbasetable[closest].weight = self.airbasetable[closest].weight+1 + self.airbasetable[startpoint].connections = self.airbasetable[startpoint].connections+2 + self.airbasetable[closest].connections = self.airbasetable[closest].connections+2 local data = { start = startpoint, target = closest, @@ -732,6 +766,38 @@ function STRATEGO:AnalyseUnconnected(Color) return self end +--[[ +function STRATEGO:PruneDeadEnds(abtable) + local found = false + local newtable = {} + for name, _data in pairs(abtable) do + local data = _data -- #STRATEGO.Data + if data.connections > 2 then + newtable[name] = data + else + -- dead end + found = true + local neighbors, nearest, distance = self:FindNeighborNodes(name) + --self:I("Pruning "..name) + if nearest then + for _name,_ in pairs(neighbors) do + local abname = self.easynames[_name] or _name + --self:I({easyname=_name,airbasename=abname}) + if abtable[abname] then + abtable[abname].connections = abtable[abname].connections -1 + end + end + end + if self.debug then + data.coord:CircleToAll(5000,-1,{1,1,1},1,{1,1,1},1,3,true,"Dead End") + end + end + end + abtable = nil + return found,newtable +end +--]] + --- [USER] Get a list of the nodes with the highest weight. -- @param #STRATEGO self -- @param #number Coalition (Optional) Find for this coalition only. E.g. coalition.side.BLUE. @@ -1141,29 +1207,34 @@ function STRATEGO:FindNeighborNodes(Name,Enemies,Friends) self:T(self.lid.."FindNeighborNodes") local neighbors = {} local name = string.gsub(Name,"[%p%s]",".") + --self:I({Name=Name,name=name}) local shortestdist = 1000*1000 local nearest = nil for _route,_data in pairs(self.disttable) do if string.find(_route,name,1,true) then local dist = self.disttable[_route] -- #STRATEGO.DistData + --self:I({route=_route,name=name}) local tname = string.gsub(_route,name,"") local tname = string.gsub(tname,";","") + --self:I({tname=tname,cname=self.easynames[tname]}) local cname = self.easynames[tname] -- name of target - local encoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE - if Enemies == true then - if self.airbasetable[cname].coalition == encoa then - neighbors[cname] = dist + if cname then + local encoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE + if Enemies == true then + if self.airbasetable[cname].coalition == encoa then + neighbors[cname] = dist + end + elseif Friends == true then + if self.airbasetable[cname].coalition ~= encoa then + neighbors[cname] = dist + end + else + neighbors[cname] = dist end - elseif Friends == true then - if self.airbasetable[cname].coalition ~= encoa then - neighbors[cname] = dist + if neighbors[cname] and dist.dist < shortestdist then + shortestdist = dist.dist + nearest = cname end - else - neighbors[cname] = dist - end - if neighbors[cname] and dist.dist < shortestdist then - shortestdist = dist.dist - nearest = cname end end end @@ -1180,13 +1251,16 @@ end -- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 6. -- @return #table Route Table of #string name entries of the route -- @return #boolean Complete If true, the route was found end-to-end. +-- @return #boolean Reverse If true, the route was found with a reverse search, the route table will be from sorted from end point to start point. function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) self:T(self.lid.."FindRoute") --self:I({Start,End,Hops}) --local bases = UTILS.DeepCopy(self.airbasetable) - local Route = {} + local Route = {} + local InRoute = {} local hops = Hops or 4 local routecomplete = false + local reverse = false local function Checker(neighbors) for _name,_data in pairs(neighbors) do @@ -1200,13 +1274,16 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) local function NextClosest(Start,End) local ecoord = self.airbasetable[End].coord - local nodes = self:FindNeighborNodes(Start) + local nodes,nearest = self:FindNeighborNodes(Start) + --self:I(tostring(nearest)) local closest = nil local closedist = 1000*1000 for _name,_dist in pairs(nodes) do local kcoord = self.airbasetable[_name].coord + local nnodes = self.airbasetable[_name].connections > 2 and true or false + if _name == End then nnodes = true end local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5) - if dist < closedist then + if (dist < closedist and nnodes and InRoute[_name] ~= true) then closedist = dist closest = _name end @@ -1231,6 +1308,7 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) -- One hop Route[#Route+1] = Start + InRoute[Start] = true local nodes = self:FindNeighborNodes(Start) local endpoint = Checker(nodes) @@ -1240,9 +1318,11 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) else local spoint = Start for i=1,hops do + --self:I("Start="..tostring(spoint)) local Next = NextClosest(spoint,End) if Next then Route[#Route+1] = Next + InRoute[Next] = true local nodes = self:FindNeighborNodes(Next) local endpoint = Checker(nodes) if endpoint then @@ -1255,8 +1335,54 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) end end end - if (self.debug or Draw) then DrawRoute(Route) end - return Route, routecomplete + + -- optimize route + local function OptimizeRoute(Route) + local foundcut = false + local largestcut = 0 + local cut = {} + for i=1,#Route do + --self:I({Start=Route[i]}) + local found,nodes = self:GetRoutesFromNode(Route[i]) + for _,_name in pairs(nodes or {}) do + for j=i+2,#Route do + if _name == Route[j] then + --self:I({"Shortcut",Route[i],Route[j]}) + if j-i > largestcut then + largestcut = j-i + cut = {i=i,j=j} + foundcut = true + end + end + end + end + end + if foundcut then + local newroute = {} + for i=1,#Route do + if i<= cut.i or i>=cut.j then + table.insert(newroute,Route[i]) + end + end + return newroute + end + return Route, foundcut + end + + if routecomplete == true then + local foundcut = true + while foundcut ~= false do + Route, foundcut = OptimizeRoute(Route) + end + else + -- reverse search + Route, routecomplete = self:FindRoute(End,Start,Hops,Draw,Color,LineType) + reverse = true + end + + if (self.debug or Draw) then DrawRoute(Route) end + + return Route, routecomplete, reverse end --- [USER] Add budget points. From dfca4d8beca39fc4abbd984786a183cda1544c31 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 11 May 2024 09:36:36 +0200 Subject: [PATCH 557/603] xxx --- Moose Development/Moose/Ops/CTLD.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index a0ce48aba..507fe0675 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1254,7 +1254,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.52" +CTLD.version="1.0.53" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3608,7 +3608,7 @@ function CTLD:_MoveGroupToZone(Group) local groupcoord = Group:GetCoordinate() -- Get closest zone of type local outcome, name, zone, distance = self:IsUnitInZone(Group,CTLD.CargoZoneType.MOVE) - if (distance <= self.movetroopsdistance) and zone then + if (distance <= self.movetroopsdistance) and outcome == true and zone~= nil then -- yes, we can ;) local groupname = Group:GetName() local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE @@ -4465,10 +4465,9 @@ function CTLD:IsUnitInZone(Unit,Zonetype) zonewidth = zoneradius end local distance = self:_GetDistance(zonecoord,unitcoord) - if zone:IsVec2InZone(unitVec2) and active then + self:T("Distance Zone: "..distance) + if (zone:IsVec2InZone(unitVec2) or Zonetype == CTLD.CargoZoneType.MOVE) and active == true and maxdist > distance then outcome = true - end - if maxdist > distance then maxdist = distance zoneret = zone zonenameret = zonename From 662885aaa651d1437b257d2f2900b278e0e6ed26 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 13 May 2024 09:56:48 +0200 Subject: [PATCH 558/603] xxx --- Moose Development/Moose/Functional/Tiresias.lua | 7 ++++--- Moose Development/Moose/Ops/ArmyGroup.lua | 2 +- Moose Development/Moose/Wrapper/Group.lua | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Tiresias.lua b/Moose Development/Moose/Functional/Tiresias.lua index c7853c593..83cc02bba 100644 --- a/Moose Development/Moose/Functional/Tiresias.lua +++ b/Moose Development/Moose/Functional/Tiresias.lua @@ -97,7 +97,7 @@ TIRESIAS = { ClassName = "TIRESIAS", debug = false, - version = "0.0.4", + version = "0.0.5", Interval = 20, GroundSet = nil, VehicleSet = nil, @@ -203,7 +203,7 @@ function TIRESIAS:AddExceptionSet(Set) } exceptions:AddGroup(grp,true) end - BASE:I("TIRESIAS: Added exception group: "..grp:GetName()) + BASE:T("TIRESIAS: Added exception group: "..grp:GetName()) end ) return self @@ -391,6 +391,7 @@ function TIRESIAS:_SwitchOnGroups(group,radius) if ground:CountAlive() > 0 then ground:ForEachGroupAlive( function(grp) + local name = grp:GetName() if grp.Tiresias and grp.Tiresias.type and (not grp.Tiresias.exception == true ) then if grp.Tiresias.invisible == true then grp:SetCommandInvisible(false) @@ -407,7 +408,7 @@ function TIRESIAS:_SwitchOnGroups(group,radius) end --BASE:I(string.format("TIRESIAS - Switch on %s %s (Exception %s)",tostring(grp.Tiresias.type),grp:GetName(),tostring(grp.Tiresias.exception))) else - BASE:E("TIRESIAS - This group has not been initialized or is an exception!") + BASE:T("TIRESIAS - This group "..tostring(name).. " has not been initialized or is an exception!") end end ) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 4dff8027c..62f3dfec4 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -2109,7 +2109,7 @@ function ARMYGROUP:_InitGroup(Template, Delay) return end - self:I(self.lid.."FF Initializing Group") + self:T(self.lid.."FF Initializing Group") -- Get template of group. local template=Template or self:_GetTemplate() diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 4747091b5..ab2334d69 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -367,7 +367,7 @@ function GROUP:GetDCSObject() return DCSGroup end - self:E(string.format("ERROR: Could not get DCS group object of group %s because DCS object could not be found!", tostring(self.GroupName))) + self:T2(string.format("ERROR: Could not get DCS group object of group %s because DCS object could not be found!", tostring(self.GroupName))) return nil end From 0e40dc70b2e5bcf3910def281c7c0f7342944c58 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 May 2024 09:34:03 +0200 Subject: [PATCH 559/603] xxx --- Moose Development/Moose/Ops/PlayerTask.lua | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index e0c7f8c60..0964d7f9a 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1213,6 +1213,9 @@ do -- AIRDEFENSE = "Airdefense", -- SAM = "SAM", -- GROUP = "Group", +-- ELEVATION = "\nTarget Elevation: %s %s", +-- METER = "meter", +-- FEET = "feet", -- }, -- -- e.g. @@ -1419,6 +1422,9 @@ PLAYERTASKCONTROLLER.Messages = { THREATMEDIUM = "medium", THREATLOW = "low", THREATTEXT = "%s\nThreat: %s\nTargets left: %d\nCoord: %s", + ELEVATION = "\nTarget Elevation: %s %s", + METER = "meter", + FEET = "feet", THREATTEXTTTS = "%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.", MARKTASK = "%s, %s, copy, task %03d location marked on map!", SMOKETASK = "%s, %s, copy, task %03d location smoked!", @@ -1499,6 +1505,9 @@ PLAYERTASKCONTROLLER.Messages = { THREATMEDIUM = "mittel", THREATLOW = "niedrig", THREATTEXT = "%s\nGefahrstufe: %s\nZiele: %d\nKoord: %s", + ELEVATION = "\nZiel Höhe: %s %s", + METER = "Meter", + FEET = "Fuss", THREATTEXTTTS = "%s, %s. Zielinformation zu %s. Gefahrstufe %s. Ziele %d. Zielposition %s.", MARKTASK = "%s, %s, verstanden, Zielposition %03d auf der Karte markiert!", SMOKETASK = "%s, %s, verstanden, Zielposition %03d mit Rauch markiert!", @@ -1561,7 +1570,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.65" +PLAYERTASKCONTROLLER.version="0.1.66" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -3182,7 +3191,8 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale) local taskname = string.format(tname,task.Type,task.PlayerTaskNr) local ttstaskname = string.format(ttsname,task.TTSType,task.PlayerTaskNr) - local Coordinate = task.Target:GetCoordinate() or COORDINATE:New(0,0,0) + local Coordinate = task.Target:GetCoordinate() or COORDINATE:New(0,0,0) -- Core.Point#COORDINATE + local Elevation = Coordinate:GetLandHeight() or 0 -- meters local CoordText = "" local CoordTextLLDM = nil if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then @@ -3207,6 +3217,15 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel local ThreatLocaleText = self.gettext:GetEntry("THREATTEXT",self.locale) text = string.format(ThreatLocaleText, taskname, ThreatGraph, targets, CoordText) + local settings = _DATABASE:GetPlayerSettings(playername) or _SETTINGS -- Core.Settings#SETTINGS + local elevationmeasure = self.gettext:GetEntry("METER",self.locale) + if settings:IsMetric()() then + elevationmeasure = self.gettext:GetEntry("METER",self.locale) + Elevation = math.floor(UTILS.MetersToFeet(Elevation)) + end + -- ELEVATION = "\nTarget Elevation: %s %s", + local elev = self.gettext:GetEntry("ELEVATION",self.locale) + text = text .. string.format(elev,tostring(math.floor(Elevation)),elevationmeasure) -- Prec bombing if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then if self.LasingDrone and self.LasingDrone.playertask then From efca40d4810a0cf4783fd2e0cb3388e09df29df8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 May 2024 09:54:00 +0200 Subject: [PATCH 560/603] SPAWN - Fix for KeepUnitNames --- Moose Development/Moose/Core/Spawn.lua | 36 +++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 0c2f62a47..75664cee9 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -620,12 +620,14 @@ end -- and any spaces before and after the resulting name are removed. -- IMPORTANT! This method MUST be the first used after :New !!! -- @param #SPAWN self --- @param #boolean KeepUnitNames (optional) If true, the unit names are kept, false or not provided to make new unit names. +-- @param #boolean KeepUnitNames (optional) If true, the unit names are kept, false or not provided create new unit names. -- @return #SPAWN self function SPAWN:InitKeepUnitNames( KeepUnitNames ) self:F() - self.SpawnInitKeepUnitNames = KeepUnitNames or true + self.SpawnInitKeepUnitNames = false + + if KeepUnitNames == true then self.SpawnInitKeepUnitNames = true end return self end @@ -3437,24 +3439,28 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end if self.SpawnInitKeepUnitNames == false then - for UnitID = 1, #SpawnTemplate.units do - SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID ) + for UnitID = 1, #SpawnTemplate.units do + if not string.find(SpawnTemplate.units[UnitID].name,"#IFF_",1,true) then --Razbam IFF hack for F15E etc + SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID ) + end SpawnTemplate.units[UnitID].unitId = nil end else for UnitID = 1, #SpawnTemplate.units do - local SpawnInitKeepUnitIFF = false - if string.find(SpawnTemplate.units[UnitID].name,"#IFF_",1,true) then --Razbam IFF hack for F15E etc - SpawnInitKeepUnitIFF = true - end - local UnitPrefix, Rest - if SpawnInitKeepUnitIFF == false then - UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" ) - self:T( { UnitPrefix, Rest } ) - else - UnitPrefix=SpawnTemplate.units[UnitID].name + local SpawnInitKeepUnitIFF = false + if string.find(SpawnTemplate.units[UnitID].name,"#IFF_",1,true) then --Razbam IFF hack for F15E etc + SpawnInitKeepUnitIFF = true end - SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID ) + local UnitPrefix, Rest + if SpawnInitKeepUnitIFF == false then + UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" ) + SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID ) + self:T( { UnitPrefix, Rest } ) + --else + --UnitPrefix=SpawnTemplate.units[UnitID].name + end + --SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID ) + SpawnTemplate.units[UnitID].unitId = nil end end From 5d8260b28e850daf65f7d736341671d80bd45c78 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 May 2024 11:50:35 +0200 Subject: [PATCH 561/603] fix --- Moose Development/Moose/Ops/PlayerTask.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 0964d7f9a..8140be16d 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update Feb 2024 +-- @date Last Update May 2024 do @@ -1370,7 +1370,7 @@ PLAYERTASKCONTROLLER.Type = { AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing" AUFTRAG.Type.CTLD = "Combat Transport" AUFTRAG.Type.CSAR = "Combat Rescue" - +AUFTRAG.Type.CONQUER = "Conquer" --- -- @type Scores PLAYERTASKCONTROLLER.Scores = { @@ -1383,7 +1383,8 @@ PLAYERTASKCONTROLLER.Scores = { [AUFTRAG.Type.BAI] = 100, [AUFTRAG.Type.SEAD] = 100, [AUFTRAG.Type.BOMBING] = 100, - [AUFTRAG.Type.BOMBRUNWAY] = 100, + [AUFTRAG.Type.BOMBRUNWAY] = 100, + [AUFTRAG.Type.CONQUER] = 100, } --- @@ -3218,9 +3219,11 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) local ThreatLocaleText = self.gettext:GetEntry("THREATTEXT",self.locale) text = string.format(ThreatLocaleText, taskname, ThreatGraph, targets, CoordText) local settings = _DATABASE:GetPlayerSettings(playername) or _SETTINGS -- Core.Settings#SETTINGS - local elevationmeasure = self.gettext:GetEntry("METER",self.locale) - if settings:IsMetric()() then + local elevationmeasure = self.gettext:GetEntry("FEET",self.locale) + if settings:IsMetric() then elevationmeasure = self.gettext:GetEntry("METER",self.locale) + --Elevation = math.floor(UTILS.MetersToFeet(Elevation)) + else Elevation = math.floor(UTILS.MetersToFeet(Elevation)) end -- ELEVATION = "\nTarget Elevation: %s %s", From f022c87f2524350099b0cace4491c6bce53bec7f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 May 2024 17:55:05 +0200 Subject: [PATCH 562/603] #MANTIS - Option to avoid friendly fire --- Moose Development/Moose/Functional/Mantis.lua | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 3e2f57477..a648696f9 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -22,7 +22,7 @@ -- @module Functional.Mantis -- @image Functional.Mantis.jpg -- --- Last Update: Feb 2024 +-- Last Update: May 2024 ------------------------------------------------------------------------- --- **MANTIS** class, extends Core.Base#BASE @@ -58,6 +58,7 @@ -- @field #boolean ShoradLink If true, #MANTIS has #SHORAD enabled -- @field #number ShoradTime Timer in seconds, how long #SHORAD will be active after a detection inside of the defense range -- @field #number ShoradActDistance Distance of an attacker in meters from a Mantis SAM site, on which Shorad will be switched on. Useful to not give away Shorad sites too early. Default 15km. Should be smaller than checkradius. +-- @field #boolean checkforfriendlies If true, do not activate a SAM installation if a friendly aircraft is in firing range. -- @extends Core.Base#BASE @@ -187,29 +188,34 @@ -- -- This is effectively a 3-stage filter allowing for zone overlap. A coordinate is accepted first when -- -- it is inside any AcceptZone. Then RejectZones are checked, which enforces both borders, but also overlaps of -- -- Accept- and RejectZones. Last, if it is inside a conflict zone, it is accepted. --- `mybluemantis:AddZones(AcceptZones,RejectZones,ConflictZones)` +-- mybluemantis:AddZones(AcceptZones,RejectZones,ConflictZones) -- -- -- ### 2.1.2 Change the number of long-, mid- and short-range systems going live on a detected target: -- -- -- parameters are numbers. Defaults are 1,2,2,6 respectively --- `mybluemantis:SetMaxActiveSAMs(Short,Mid,Long,Classic)` +-- mybluemantis:SetMaxActiveSAMs(Short,Mid,Long,Classic) -- -- ### 2.1.3 SHORAD will automatically be added from SAM sites of type "short-range" -- -- ### 2.1.4 Advanced features -- -- -- switch off auto mode **before** you start MANTIS. --- `mybluemantis.automode = false` +-- mybluemantis.automode = false -- -- -- switch off auto shorad **before** you start MANTIS. --- `mybluemantis.autoshorad = false` +-- mybluemantis.autoshorad = false -- -- -- scale of the activation range, i.e. don't activate at the fringes of max range, defaults below. -- -- also see engagerange below. --- ` self.radiusscale[MANTIS.SamType.LONG] = 1.1` --- ` self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2` --- ` self.radiusscale[MANTIS.SamType.SHORT] = 1.3` +-- self.radiusscale[MANTIS.SamType.LONG] = 1.1 +-- self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2 +-- self.radiusscale[MANTIS.SamType.SHORT] = 1.3 +-- +-- ### 2.1.5 Friendlies check in firing range +-- +-- -- For some scenarios, like Cold War, it might be useful not to activate SAMs if friendly aircraft are around to avoid death by friendly fire. +-- mybluemantis.checkforfriendlies = true -- -- # 3. Default settings [both modes unless stated otherwise] -- @@ -321,6 +327,7 @@ MANTIS = { automode = true, autoshorad = true, ShoradGroupSet = nil, + checkforfriendlies = false, } --- Advanced state enumerator @@ -1255,6 +1262,10 @@ do -- DEBUG set = self:_PreFilterHeight(height) end + local friendlyset -- Core.Set#SET_GROUP + if self.checkforfriendlies == true then + friendlyset = SET_GROUP:New():FilterCoalitions(self.Coalition):FilterCategories({"plane","helicopter"}):FilterOnce() + end for _,_coord in pairs (set) do local coord = _coord -- get current coord to check -- output for cross-check @@ -1279,8 +1290,16 @@ do local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug) self:T(self.lid..text) end + -- friendlies around? + local nofriendlies = true + if self.checkforfriendlies == true then + local closestfriend, distance = friendlyset:GetClosestGroup(samcoordinate) + if closestfriend and distance and distance < rad then + nofriendlies = false + end + end -- end output to cross-check - if targetdistance <= rad and zonecheck then + if targetdistance <= rad and zonecheck == true and nofriendlies == true then return true, targetdistance end end From 639c5bc71af0734816923a93289caca5f3973e8e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 19 May 2024 12:47:54 +0200 Subject: [PATCH 563/603] xxx --- .../Moose/Functional/Stratego.lua | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 74caf5cc7..e1187c07a 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -594,7 +594,7 @@ end -- @param #STRATEGO self -- @return #STRATEGO self function STRATEGO:GetToFrom(StartPoint,EndPoint) - self:T(self.lid.."GetToFrom") + self:T(self.lid.."GetToFrom "..tostring(StartPoint).." "..tostring(EndPoint)) local pstart = string.gsub(StartPoint,"[%p%s]",".") local pend = string.gsub(EndPoint,"[%p%s]",".") local fromto = pstart..";"..pend @@ -630,7 +630,7 @@ end -- @param #STRATEGO self -- @param #string Startpoint Starting Point, e.g. AIRBASE.Syria.Hatay -- @param #string Endpoint End Point, e.g. AIRBASE.Syria.H4 --- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to lila. +-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to violet. -- @param #number Linetype (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 5. -- @param #boolean Draw (Optional) If true, draw route on the F10 map. Defaukt false. -- @return #STRATEGO self @@ -1241,6 +1241,33 @@ function STRATEGO:FindNeighborNodes(Name,Enemies,Friends) return neighbors, nearest, shortestdist end +--- [INTERNAL] Route Finding - Find the next hop towards an end node from a start node +-- @param #STRATEGO self +-- @param #string Start The name of the start node. +-- @param #string End The name of the end node. +-- @param #table InRoute Table of node names making up the route so far. +-- @return #string Name of the next closest node +function STRATEGO:_GetNextClosest(Start,End,InRoute) + local ecoord = self.airbasetable[End].coord + local nodes,nearest = self:FindNeighborNodes(Start) + --self:I(tostring(nearest)) + local closest = nil + local closedist = 1000*1000 + for _name,_dist in pairs(nodes) do + local kcoord = self.airbasetable[_name].coord + local nnodes = self.airbasetable[_name].connections > 2 and true or false + if _name == End then nnodes = true end + if kcoord ~= nil and ecoord ~= nil and nnodes == true then + local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5) + if (dist < closedist and InRoute[_name] ~= true) then + closedist = dist + closest = _name + end + end + end + return closest +end + --- [USER] Find a route between two nodes. -- @param #STRATEGO self -- @param #string Start The name of the start node. @@ -1271,29 +1298,7 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) end return nil end - - local function NextClosest(Start,End) - local ecoord = self.airbasetable[End].coord - local nodes,nearest = self:FindNeighborNodes(Start) - --self:I(tostring(nearest)) - local closest = nil - local closedist = 1000*1000 - for _name,_dist in pairs(nodes) do - local kcoord = self.airbasetable[_name].coord - local nnodes = self.airbasetable[_name].connections > 2 and true or false - if _name == End then nnodes = true end - local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5) - if (dist < closedist and nnodes and InRoute[_name] ~= true) then - closedist = dist - closest = _name - end - end - if closest then - --MESSAGE:New(string.format("Start %s | End %s | Nextclosest %s",Start,End,closest),10,"STRATEGO"):ToLog():ToAll() - return closest - end - end - + local function DrawRoute(Route) for i=1,#Route-1 do local p1=Route[i] @@ -1319,8 +1324,8 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) local spoint = Start for i=1,hops do --self:I("Start="..tostring(spoint)) - local Next = NextClosest(spoint,End) - if Next then + local Next = self:_GetNextClosest(spoint,End,InRoute) + if Next ~= nil then Route[#Route+1] = Next InRoute[Next] = true local nodes = self:FindNeighborNodes(Next) @@ -1332,7 +1337,9 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) else spoint = Next end - end + else + break + end end end From 593f21bd3bd939b9e0d839059968016a6a0841fb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 19 May 2024 13:12:35 +0200 Subject: [PATCH 564/603] xxx --- Moose Development/Moose/Functional/Stratego.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index e1187c07a..0ea7c2095 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -181,7 +181,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.9", + version = "0.2.10", portweight = 3, POIweight = 1, maxrunways = 3, @@ -1257,9 +1257,9 @@ function STRATEGO:_GetNextClosest(Start,End,InRoute) local kcoord = self.airbasetable[_name].coord local nnodes = self.airbasetable[_name].connections > 2 and true or false if _name == End then nnodes = true end - if kcoord ~= nil and ecoord ~= nil and nnodes == true then + if kcoord ~= nil and ecoord ~= nil and nnodes == true and InRoute[_name] ~= true then local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5) - if (dist < closedist and InRoute[_name] ~= true) then + if (dist < closedist ) then closedist = dist closest = _name end From dd7c1a924588942a2c7d06dde8157ad4f572f90e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 20 May 2024 11:15:47 +0200 Subject: [PATCH 565/603] xxx --- Moose Development/Moose/Functional/Stratego.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 0ea7c2095..81fe4c80d 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -181,7 +181,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.10", + version = "0.2.11", portweight = 3, POIweight = 1, maxrunways = 3, @@ -1276,10 +1276,11 @@ end -- @param #boolean Draw If true, draw the route on the map. -- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to black. -- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 6. +-- @param #boolean NoOptimize If set to true, do not optimize (shorten) the resulting route if possible. -- @return #table Route Table of #string name entries of the route -- @return #boolean Complete If true, the route was found end-to-end. -- @return #boolean Reverse If true, the route was found with a reverse search, the route table will be from sorted from end point to start point. -function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) +function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType,NoOptimize) self:T(self.lid.."FindRoute") --self:I({Start,End,Hops}) --local bases = UTILS.DeepCopy(self.airbasetable) @@ -1376,7 +1377,7 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType) return Route, foundcut end - if routecomplete == true then + if routecomplete == true and NoOptimize ~= true then local foundcut = true while foundcut ~= false do Route, foundcut = OptimizeRoute(Route) From 7dec18935875e6e742740a7cc6d1e18dd134ce74 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 21 May 2024 15:18:57 +0200 Subject: [PATCH 566/603] xxx --- Moose Development/Moose/Core/Zone.lua | 2 +- .../Moose/Functional/Stratego.lua | 145 +++++++++++++++++- 2 files changed, 144 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 19055634b..5e63cff7a 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -3641,7 +3641,7 @@ do -- ZONE_ELASTIC end - --- Create a convec hull. + --- Create a convex hull. -- @param #ZONE_ELASTIC self -- @param #table pl Points -- @return #table Points diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 81fe4c80d..adb440470 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -181,7 +181,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.11", + version = "0.3.1", portweight = 3, POIweight = 1, maxrunways = 3, @@ -803,9 +803,13 @@ end -- @param #number Coalition (Optional) Find for this coalition only. E.g. coalition.side.BLUE. -- @return #table Table of nodes. -- @return #number Weight The consolidated weight associated with the nodes. +-- @return #number Highest Highest weight found. +-- @return #string Name of the node with the highest weight. function STRATEGO:GetHighestWeightNodes(Coalition) self:T(self.lid.."GetHighestWeightNodes") local weight = 0 + local highest = 0 + local highname = nil local airbases = {} for _name,_data in pairs(self.airbasetable) do local okay = true @@ -819,8 +823,12 @@ function STRATEGO:GetHighestWeightNodes(Coalition) if not airbases[weight] then airbases[weight]={} end table.insert(airbases[weight],_name) end + if _data.weight > highest and okay then + highest = _data.weight + highname = _name + end end - return airbases[weight],weight + return airbases[weight],weight,highest,highname end --- [USER] Get a list of the nodes a weight less than the given parameter. @@ -1497,6 +1505,139 @@ function STRATEGO:FindAffordableConsolidationTarget() end end +--- [INTERNAL] Internal helper function to check for islands, aka Floodtest +-- @param #STRATEGO self +-- @param #string next Name of the start node +-- @param #table filled #table of visited nodes +-- @param #table unfilled #table if unvisited nodes +-- @return #STRATEGO self +function STRATEGO:_FloodNext(next,filled,unfilled) + local start = self:FindNeighborNodes(next) + for _name,_ in pairs (start) do + if filled[_name] ~= true then + self:T("Flooding ".._name) + filled[_name] = true + unfilled[_name] = nil + self:_FloodNext(_name,filled,unfilled) + end + end + return self +end + +--- [INTERNAL] Internal helper function to check for islands, aka Floodtest +-- @param #STRATEGO self +-- @param #string Start Name of the start node +-- @param #table ABTable (Optional) #table of node names to check. +-- @return #STRATEGO self +function STRATEGO:_FloodFill(Start,ABTable) + self:T("Start = "..tostring(Start)) + if Start == nil then return end + local filled = {} + local unfilled = {} + if ABTable then + unfilled = ABTable + else + for _name,_ in pairs(self.airbasetable) do + unfilled[_name] = true + end + end + filled[Start] = true + unfilled[Start] = nil + local start = self:FindNeighborNodes(Start) + for _name,_ in pairs (start) do + if filled[_name] ~= true then + self:T("Flooding ".._name) + filled[_name] = true + unfilled[_name] = nil + self:_FloodNext(_name,filled,unfilled) + end + end + return filled, unfilled +end + +--- [INTERNAL] Internal helper function to check for islands, aka Floodtest +-- @param #STRATEGO self +-- @param #boolen connect If true, connect the two resulting islands at the shortest distance if necessary +-- @param #boolen draw If true, draw outer vertices of found node networks +-- @return #boolean Connected If true, all nodes are in one network +-- @return #table Network #table of node names in the network +-- @return #table Unconnected #table of node names **not** in the network +function STRATEGO:_FloodTest(connect,draw) + + local function GetElastic(bases) + local vec2table = {} + for _name,_ in pairs(bases) do + local coord = self.airbasetable[_name].coord + local vec2 = coord:GetVec2() + table.insert(vec2table,vec2) + end + local zone = ZONE_ELASTIC:New("STRATEGO-Floodtest-"..math.random(1,10000),vec2table) + return zone + end + + local function DrawElastic(filled,drawit) + local zone = GetElastic(filled) + if drawit then + zone:SetColor({1,1,1},1) + zone:SetDrawCoalition(-1) + zone:Update(1,true) -- draw zone + end + return zone + end + + local _,_,weight,name = self:GetHighestWeightNodes() + local filled, unfilled = self:_FloodFill(name) + local allin = true + if table.length(unfilled) > 0 then + MESSAGE:New("There is at least one node island!",15,"STRATEGO"):ToAllIf(self.debug):ToLog() + allin = false + if self.debug == true then + local zone1 = DrawElastic(filled,draw) + local zone2 = DrawElastic(unfilled,draw) + local vertices1 = zone1:GetVerticiesVec2() + local vertices2 = zone2:GetVerticiesVec2() + -- get closest vertices + local corner1 = nil + local corner2 = nil + local mindist = math.huge + local found = false + for _,_edge in pairs(vertices1) do + for _,_edge2 in pairs(vertices2) do + local dist=UTILS.VecDist2D(_edge,_edge2) + if dist < mindist then + mindist = dist + corner1 = _edge + corner2 = _edge2 + found = true + end + end + end + if found then + local Corner = COORDINATE:NewFromVec2(corner1) + local Corner2 = COORDINATE:NewFromVec2(corner2) + Corner:LineToAll(Corner2,-1,{1,1,1},1,1,true,"Island2Island") + local cornername + local cornername2 + for _name,_data in pairs(self.airbasetable) do + local zone = _data.zone + if zone:IsVec2InZone(corner1) then + cornername = _name + self:T("Corner1 = ".._name) + end + if zone:IsVec2InZone(corner2) then + cornername2 = _name + self:T("Corner2 = ".._name) + end + if cornername and cornername2 and connect == true then + self:AddRoutesManually(cornername,cornername2,Color,Linetype,self.debug) + end + end + end + end + end + return allin, filled, unfilled +end + --------------------------------------------------------------------------------------------------------------- -- -- End From dead785133aae7637915d27549b76b5558bf1455 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 24 May 2024 10:04:08 +0200 Subject: [PATCH 567/603] Autolase --- .../Moose/Functional/Autolase.lua | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 0f6a8e52d..181137a00 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -74,7 +74,7 @@ -- @image Designation.JPG -- -- Date: 24 Oct 2021 --- Last Update: Jan 2024 +-- Last Update: May 2024 -- --- Class AUTOLASE -- @type AUTOLASE @@ -88,6 +88,7 @@ -- @field #table LaserCodes -- @field #table playermenus -- @field #boolean smokemenu +-- @field #boolean threatmenu -- @extends Ops.Intel#INTEL --- @@ -117,7 +118,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.1.23" +AUTOLASE.version = "0.1.25" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -205,6 +206,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes self.playermenus = {} self.smokemenu = true + self.threatmenu = true -- Set some string id for output to DCS.log file. self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -337,6 +339,13 @@ function AUTOLASE:SetPilotMenu() local smoketext = string.format("Switch smoke targets to %s",smoke) local smokemenu = MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets)) end + if self.threatmenu then + local threatmenutop = MENU_GROUP:New(Group,"Set min lasing threat",lasetopm) + for i=0,10,2 do + local text = "Threatlevel "..tostring(i) + local threatmenu = MENU_GROUP_COMMAND:New(Group,text,threatmenutop,self.SetMinThreatLevel,self,i) + end + end for _,_grp in pairs(self.RecceSet.Set) do local grp = _grp -- Wrapper.Group#GROUP local unit = grp:GetUnit(1) @@ -602,6 +611,21 @@ function AUTOLASE:DisableSmokeMenu() return self end +--- (User) Show the "Switch min threat lasing..." menu entry for pilots. On by default. +-- @param #AUTOLASE self +-- @return #AUTOLASE self +function AUTOLASE:EnableThreatLevelMenu() + self.threatmenu = true + return self +end + +--- (User) Do not show the "Switch min threat lasing..." menu entry for pilots. +-- @param #AUTOLASE self +-- @return #AUTOLASE self +function AUTOLASE:DisableThreatLevelMenu() + self.threatmenu = false + return self +end --- (Internal) Function to calculate line of sight. -- @param #AUTOLASE self @@ -730,6 +754,7 @@ function AUTOLASE:ShowStatus(Group,Unit) report:Add(string.format("Recce %s has code %d",name,code)) end end + report:Add(string.format("Lasing min threat level %d",self.minthreatlevel)) local lines = 0 for _ind,_entry in pairs(self.CurrentLasing) do local entry = _entry -- #AUTOLASE.LaserSpot From 97635f4bbae664f0eda4901f0967987d776e10cf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 27 May 2024 17:07:12 +0200 Subject: [PATCH 568/603] xxx --- Moose Development/Moose/Core/ClientMenu.lua | 4 ++-- Moose Development/Moose/Core/Menu.lua | 1 + Moose Development/Moose/Ops/PlayerTask.lua | 10 ++++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index bcc348814..dae6195d2 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -20,7 +20,7 @@ -- -- @module Core.ClientMenu -- @image Core_Menu.JPG --- last change: Apr 2024 +-- last change: May 2024 -- TODO ---------------------------------------------------------------------------------------------------------------- @@ -691,7 +691,7 @@ function CLIENTMENUMANAGER:Propagate(Client) local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then local playerunit = client:GetName() - local playergroup = client:GetGroup() + --local playergroup = client:GetGroup() local playername = client:GetPlayerName() or "none" if not knownunits[playerunit] then knownunits[playerunit] = true diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 1ae35d35d..44c15b08d 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -1050,6 +1050,7 @@ do -- @param #MENU_GROUP_DELAYED self -- @return #MENU_GROUP_DELAYED function MENU_GROUP_DELAYED:Set() + if not self.GroupID then return end do if not self.MenuSet then missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath ) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 8140be16d..2f02d9ce3 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -2123,10 +2123,12 @@ function PLAYERTASKCONTROLLER:_GetPlayerName(Client) local ttsplayername = nil if not self.customcallsigns[playername] then local playergroup = Client:GetGroup() - ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) - local newplayername = self:_GetTextForSpeech(ttsplayername) - self.customcallsigns[playername] = newplayername - ttsplayername = newplayername + if playergroup ~= nil then + ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations) + local newplayername = self:_GetTextForSpeech(ttsplayername) + self.customcallsigns[playername] = newplayername + ttsplayername = newplayername + end else ttsplayername = self.customcallsigns[playername] end From 26d0f2f4e3176fff37fe4f388ee9d3191c7d365b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 27 May 2024 18:16:57 +0200 Subject: [PATCH 569/603] xxx --- .../Moose/Functional/Autolase.lua | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 181137a00..612162389 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -325,46 +325,51 @@ end function AUTOLASE:SetPilotMenu() if self.usepilotset then local pilottable = self.pilotset:GetSetObjects() or {} + local grouptable = {} for _,_unit in pairs (pilottable) do local Unit = _unit -- Wrapper.Unit#UNIT if Unit and Unit:IsAlive() then local Group = Unit:GetGroup() + local GroupName = Group:GetName() or "none" local unitname = Unit:GetName() - if self.playermenus[unitname] then self.playermenus[unitname]:Remove() end - local lasetopm = MENU_GROUP:New(Group,"Autolase",nil) - self.playermenus[unitname] = lasetopm - local lasemenu = MENU_GROUP_COMMAND:New(Group,"Status",lasetopm,self.ShowStatus,self,Group,Unit) - if self.smokemenu then - local smoke = (self.smoketargets == true) and "off" or "on" - local smoketext = string.format("Switch smoke targets to %s",smoke) - local smokemenu = MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets)) - end - if self.threatmenu then - local threatmenutop = MENU_GROUP:New(Group,"Set min lasing threat",lasetopm) - for i=0,10,2 do - local text = "Threatlevel "..tostring(i) - local threatmenu = MENU_GROUP_COMMAND:New(Group,text,threatmenutop,self.SetMinThreatLevel,self,i) - end - end - for _,_grp in pairs(self.RecceSet.Set) do - local grp = _grp -- Wrapper.Group#GROUP - local unit = grp:GetUnit(1) - --local name = grp:GetName() - if unit and unit:IsAlive() then - local name = unit:GetName() - local mname = string.gsub(name,".%d+.%d+$","") - local code = self:GetLaserCode(name) - local unittop = MENU_GROUP:New(Group,"Change laser code for "..mname,lasetopm) - for _,_code in pairs(self.LaserCodes) do - local text = tostring(_code) - if _code == code then text = text.."(*)" end - local changemenu = MENU_GROUP_COMMAND:New(Group,text,unittop,self.SetRecceLaserCode,self,name,_code,true) - end - end - end + if not grouptable[GroupName] == true then + if self.playermenus[unitname] then self.playermenus[unitname]:Remove() end -- menus + local lasetopm = MENU_GROUP:New(Group,"Autolase",nil) + self.playermenus[unitname] = lasetopm + local lasemenu = MENU_GROUP_COMMAND:New(Group,"Status",lasetopm,self.ShowStatus,self,Group,Unit) + if self.smokemenu then + local smoke = (self.smoketargets == true) and "off" or "on" + local smoketext = string.format("Switch smoke targets to %s",smoke) + local smokemenu = MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets)) + end -- smokement + if self.threatmenu then + local threatmenutop = MENU_GROUP:New(Group,"Set min lasing threat",lasetopm) + for i=0,10,2 do + local text = "Threatlevel "..tostring(i) + local threatmenu = MENU_GROUP_COMMAND:New(Group,text,threatmenutop,self.SetMinThreatLevel,self,i) + end -- threatlevel + end -- threatmenu + for _,_grp in pairs(self.RecceSet.Set) do + local grp = _grp -- Wrapper.Group#GROUP + local unit = grp:GetUnit(1) + --local name = grp:GetName() + if unit and unit:IsAlive() then + local name = unit:GetName() + local mname = string.gsub(name,".%d+.%d+$","") + local code = self:GetLaserCode(name) + local unittop = MENU_GROUP:New(Group,"Change laser code for "..mname,lasetopm) + for _,_code in pairs(self.LaserCodes) do + local text = tostring(_code) + if _code == code then text = text.."(*)" end + local changemenu = MENU_GROUP_COMMAND:New(Group,text,unittop,self.SetRecceLaserCode,self,name,_code,true) + end -- Codes + end -- unit alive + end -- Recceset + grouptable[GroupName] = true + end -- grouptable[GroupName] --lasemenu:Refresh() - end - end + end -- unit alive + end -- pilot loop else if not self.NoMenus then self.Menu = MENU_COALITION_COMMAND:New(self.coalition,"Autolase",nil,self.ShowStatus,self) From f4ae0c954707b0bb7923ed52cb4531fabfc035a3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 8 Jun 2024 11:48:41 +0200 Subject: [PATCH 570/603] #MANTIS - fix omission to set own name --- Moose Development/Moose/Functional/Mantis.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 4fea096f4..b6d711964 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -509,7 +509,8 @@ do -- DONE: Treat Awacs separately, since they might be >80km off site -- DONE: Allow tables of prefixes for the setup -- DONE: Auto-Mode with range setups for various known SAM types. - + + self.name = name or "mymantis" self.SAM_Templates_Prefix = samprefix or "Red SAM" self.EWR_Templates_Prefix = ewrprefix or "Red EWR" self.HQ_Template_CC = hq or nil @@ -638,7 +639,7 @@ do -- TODO Version -- @field #string version - self.version="0.8.17" + self.version="0.8.18" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) --- FSM Functions --- From 5e62791837dbc80c9205164b23c5749dbae75bfa Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 11 Jun 2024 09:02:31 +0200 Subject: [PATCH 571/603] fixes --- Moose Development/Moose/Core/Set.lua | 2 +- .../Moose/Functional/Designate.lua | 18 +++++++++++------- .../Moose/Functional/Detection.lua | 14 ++++++++------ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index a401b6f3f..43812c1e1 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -3478,7 +3478,7 @@ do -- SET_STATIC --- Add STATIC(s) to SET_STATIC. -- @param #SET_STATIC self - -- @param #string AddStatic A single STATIC. + -- @param Wrapper.Static#STATIC AddStatic A single STATIC. -- @return #SET_STATIC self function SET_STATIC:AddStatic( AddStatic ) self:F2( AddStatic:GetName() ) diff --git a/Moose Development/Moose/Functional/Designate.lua b/Moose Development/Moose/Functional/Designate.lua index c34b38ea9..236f07dc7 100644 --- a/Moose Development/Moose/Functional/Designate.lua +++ b/Moose Development/Moose/Functional/Designate.lua @@ -1198,7 +1198,7 @@ do -- DESIGNATE --local ReportTypes = REPORT:New() --local ReportLaserCodes = REPORT:New() - TargetSetUnit:Flush( self ) + --TargetSetUnit:Flush( self ) --self:F( { Recces = self.Recces } ) for TargetUnit, RecceData in pairs( self.Recces ) do @@ -1229,6 +1229,8 @@ do -- DESIGNATE end end + if TargetSetUnit == nil then return end + if self.AutoLase or ( not self.AutoLase and ( self.LaseStart + Duration >= timer.getTime() ) ) then TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0, @@ -1253,7 +1255,7 @@ do -- DESIGNATE local RecceUnit = UnitData -- Wrapper.Unit#UNIT local RecceUnitDesc = RecceUnit:GetDesc() - --self:F( { RecceUnit = RecceUnit:GetName(), RecceDescription = RecceUnitDesc } ) + --self:F( { RecceUnit = RecceUnit:GetName(), RecceDescription = RecceUnitDesc } )x if RecceUnit:IsLasing() == false then --self:F( { IsDetected = RecceUnit:IsDetected( TargetUnit ), IsLOS = RecceUnit:IsLOS( TargetUnit ) } ) @@ -1275,9 +1277,10 @@ do -- DESIGNATE local Spot = RecceUnit:LaseUnit( TargetUnit, LaserCode, Duration ) local AttackSet = self.AttackSet local DesignateName = self.DesignateName + local typename = TargetUnit:GetTypeName() function Spot:OnAfterDestroyed( From, Event, To ) - self.Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() .. " destroyed. " .. TargetSetUnit:Count() .. " targets left.", + self.Recce:MessageToSetGroup( "Target " ..typename .. " destroyed. " .. TargetSetUnit:CountAlive() .. " targets left.", 5, AttackSet, self.DesignateName ) end @@ -1285,7 +1288,7 @@ do -- DESIGNATE -- OK. We have assigned for the Recce a TargetUnit. We can exit the function. MarkingCount = MarkingCount + 1 local TargetUnitType = TargetUnit:GetTypeName() - RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.", + RecceUnit:MessageToSetGroup( "Marking " .. TargetUnitType .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.", 10, self.AttackSet, DesignateName ) if not MarkedTypes[TargetUnitType] then MarkedTypes[TargetUnitType] = true @@ -1457,9 +1460,10 @@ do -- DESIGNATE -- @param #DESIGNATE self -- @return #DESIGNATE function DESIGNATE:onafterDoneSmoking( From, Event, To, Index ) - - self.Designating[Index] = string.gsub( self.Designating[Index], "S", "" ) - self:SetDesignateMenu() + if self.Designating[Index] ~= nil then + self.Designating[Index] = string.gsub( self.Designating[Index], "S", "" ) + self:SetDesignateMenu() + end end --- DoneIlluminating diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index bc65c4d4f..a6196f861 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -545,7 +545,7 @@ do -- DETECTION_BASE -- @param #string To The To State string. function DETECTION_BASE:onafterDetect( From, Event, To ) - local DetectDelay = 0.1 + local DetectDelay = 0.15 self.DetectionCount = 0 self.DetectionRun = 0 self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table @@ -604,7 +604,7 @@ do -- DETECTION_BASE -- @param #number DetectionTimeStamp Time stamp of detection event. function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp ) - -- self:F( { DetectedObjects = self.DetectedObjects } ) + self:I( { DetectedObjects = self.DetectedObjects } ) self.DetectionRun = self.DetectionRun + 1 @@ -612,14 +612,14 @@ do -- DETECTION_BASE if Detection and Detection:IsAlive() then - -- self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) + self:I( { "DetectionGroup is Alive", Detection:GetName() } ) local DetectionGroupName = Detection:GetName() local DetectionUnit = Detection:GetUnit( 1 ) local DetectedUnits = {} - local DetectedTargets = Detection:GetDetectedTargets( + local DetectedTargets = DetectionUnit:GetDetectedTargets( self.DetectVisual, self.DetectOptical, self.DetectRadar, @@ -628,8 +628,10 @@ do -- DETECTION_BASE self.DetectDLINK ) - self:F( { DetectedTargets = DetectedTargets } ) - + --self:I( { DetectedTargets = DetectedTargets } ) + --self:I(UTILS.PrintTableToLog(DetectedTargets)) + + for DetectionObjectID, Detection in pairs( DetectedTargets ) do local DetectedObject = Detection.object -- DCS#Object From fe6d69fdccf3b5001ccabed449159114667c2b06 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 14 Jun 2024 16:37:36 +0200 Subject: [PATCH 572/603] xxx --- Moose Development/Moose/Ops/Chief.lua | 8 ++++---- Moose Development/Moose/Ops/Commander.lua | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 189f554d4..35697e8cb 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -1463,7 +1463,7 @@ end --- Add a CAP zone. Flights will engage detected targets inside this zone. -- @param #CHIEF self -- @param Core.Zone#ZONE Zone CAP Zone. Has to be a circular zone. --- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet. +-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet. -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. @@ -1479,7 +1479,7 @@ end --- Add a GCI CAP. -- @param #CHIEF self -- @param Core.Zone#ZONE Zone Zone, where the flight orbits. --- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet. +-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet. -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. @@ -1506,7 +1506,7 @@ end --- Add an AWACS zone. -- @param #CHIEF self -- @param Core.Zone#ZONE Zone Zone. --- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet. +-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet. -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. @@ -1533,7 +1533,7 @@ end --- Add a refuelling tanker zone. -- @param #CHIEF self -- @param Core.Zone#ZONE Zone Zone. --- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet. +-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet. -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index 1c649dd85..596eb5661 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -663,7 +663,7 @@ end --- Add a CAP zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE Zone CapZone Zone. --- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet. +-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet. -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. @@ -689,7 +689,7 @@ end --- Add a GCICAP zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE Zone CapZone Zone. --- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet. +-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet. -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. @@ -735,7 +735,7 @@ end --- Add an AWACS zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE Zone Zone. --- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet. +-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet. -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. @@ -782,7 +782,7 @@ end --- Add a refuelling tanker zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE Zone Zone. --- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet. +-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet. -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. From b701fa61fabc4bc69056ca52fe6e26109a29cc26 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 22 Jun 2024 11:54:35 +0200 Subject: [PATCH 573/603] xxx --- Moose Development/Moose/Wrapper/Unit.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 3053194cc..ae6371d60 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -445,7 +445,11 @@ function UNIT:IsPlayer() if not group then return false end -- Units of template group. - local units=group:GetTemplate().units + local template = group:GetTemplate() + + if (template == nil) or (template.units == nil ) then return false end + + local units=template.units -- Get numbers. for _,unit in pairs(units) do From f15e89e7aa615ffb27a6024ac2a0cd057c227abd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 23 Jun 2024 19:15:57 +0200 Subject: [PATCH 574/603] xxx --- Moose Development/Moose/Ops/OpsZone.lua | 8 +++ Moose Development/Moose/Wrapper/Unit.lua | 66 ++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsZone.lua b/Moose Development/Moose/Ops/OpsZone.lua index ee83650aa..e97b0688d 100644 --- a/Moose Development/Moose/Ops/OpsZone.lua +++ b/Moose Development/Moose/Ops/OpsZone.lua @@ -604,6 +604,14 @@ function OPSZONE:GetAttackDuration() return nil end +--- Find an OPSZONE using the Zone Name. +-- @param #OPSZONE self +-- @param #string ZoneName The zone name. +-- @return #OPSZONE The OPSZONE or nil if not found. +function OPSZONE:FindByName( ZoneName ) + local Found = _DATABASE:FindOpsZone( ZoneName ) + return Found +end --- Check if the red coalition is currently owning the zone. -- @param #OPSZONE self diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index ae6371d60..497d9fb1a 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -793,11 +793,13 @@ end --- Get the number of ammunition and in particular the number of shells, rockets, bombs and missiles a unit currently has. -- @param #UNIT self -- @return #number Total amount of ammo the unit has left. This is the sum of shells, rockets, bombs and missiles. --- @return #number Number of shells left. +-- @return #number Number of shells left. Shells include MG ammunition, AP and HE shells, and artillery shells where applicable. -- @return #number Number of rockets left. -- @return #number Number of bombs left. -- @return #number Number of missiles left. --- @return #number Number of artillery shells left (with explosive mass, included in shells; shells can also be machine gun ammo) +-- @return #number Number of artillery shells left (with explosive mass, included in shells; HE will also be reported as artillery shells for tanks) +-- @return #number Number of tank AP shells left (for tanks, if applicable) +-- @return #number Number of tank HE shells left (for tanks, if applicable) function UNIT:GetAmmunition() -- Init counter. @@ -807,6 +809,8 @@ function UNIT:GetAmmunition() local nmissiles=0 local nbombs=0 local narti=0 + local nAPshells = 0 + local nHEshells = 0 local unit=self @@ -848,6 +852,14 @@ function UNIT:GetAmmunition() narti=narti+Nammo end + if ammotable[w].desc.typeName and string.find(ammotable[w].desc.typeName,"_AP",1,true) then + nAPshells = nAPshells+Nammo + end + + if ammotable[w].desc.typeName and string.find(ammotable[w].desc.typeName,"_HE",1,true) then + nHEshells = nHEshells+Nammo + end + elseif Category==Weapon.Category.ROCKET then -- Add up all rockets. @@ -884,7 +896,55 @@ function UNIT:GetAmmunition() -- Total amount of ammunition. nammo=nshells+nrockets+nmissiles+nbombs - return nammo, nshells, nrockets, nbombs, nmissiles, narti + return nammo, nshells, nrockets, nbombs, nmissiles, narti, nAPshells, nHEshells +end + +--- Checks if a tank still has AP shells. +-- @param #UNIT self +-- @return #boolean HasAPShells +function UNIT:HasAPShells() + local _,_,_,_,_,_,shells = self:GetAmmunition() + if shells > 0 then return true else return false end +end + +--- Get number of AP shells from a tank. +-- @param #UNIT self +-- @return #number Number of AP shells +function UNIT:GetAPShells() + local _,_,_,_,_,_,shells = self:GetAmmunition() + return shells or 0 +end + +--- Get number of HE shells from a tank. +-- @param #UNIT self +-- @return #number Number of HE shells +function UNIT:GetHEShells() + local _,_,_,_,_,_,_,shells = self:GetAmmunition() + return shells or 0 +end + +--- Checks if a tank still has HE shells. +-- @param #UNIT self +-- @return #boolean HasHEShells +function UNIT:HasHEShells() + local _,_,_,_,_,_,_,shells = self:GetAmmunition() + if shells > 0 then return true else return false end +end + +--- Checks if an artillery unit still has artillery shells. +-- @param #UNIT self +-- @return #boolean HasArtiShells +function UNIT:HasArtiShells() + local _,_,_,_,_,shells = self:GetAmmunition() + if shells > 0 then return true else return false end +end + +--- Get number of artillery shells from an artillery unit. +-- @param #UNIT self +-- @return #number Number of artillery shells +function UNIT:GetArtiShells() + local _,_,_,_,_,shells = self:GetAmmunition() + return shells or 0 end --- Returns the unit sensors. From 2fc2f5d798c8a0b4ff4ea51e1db7b7129238e8e6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 25 Jun 2024 10:44:53 +0200 Subject: [PATCH 575/603] #ARTY fixed counting the right artillery shells and some logic problems --- .../Moose/Functional/Artillery.lua | 92 +++++++++++-------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 7425d3212..c1745cac0 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -45,6 +45,7 @@ -- @field #table currentMove Holds the current commanded move, if there is one assigned. -- @field #number Nammo0 Initial amount total ammunition (shells+rockets+missiles) of the whole group. -- @field #number Nshells0 Initial amount of shells of the whole group. +-- @field #number Narty0 Initial amount of artillery shells of the whole group. -- @field #number Nrockets0 Initial amount of rockets of the whole group. -- @field #number Nmissiles0 Initial amount of missiles of the whole group. -- @field #number Nukes0 Initial amount of tactical nukes of the whole group. Default is 0. @@ -415,7 +416,7 @@ -- arty set, battery "Paladin Alpha", rearming place -- -- Setting the rearming group is independent of the position of the mark. Just create one anywhere on the map and type --- arty set, battery "Mortar Bravo", rearming group "Ammo Truck M818" +-- arty set, battery "Mortar Bravo", rearming group "Ammo Truck M939" -- Note that the name of the rearming group has to be given in quotation marks and spelt exactly as the group name defined in the mission editor. -- -- ## Transporting @@ -453,7 +454,7 @@ -- -- Creat a new ARTY object from a Paladin group. -- paladin=ARTY:New(GROUP:FindByName("Blue Paladin")) -- --- -- Define a rearming group. This is a Transport M818 truck. +-- -- Define a rearming group. This is a Transport M939 truck. -- paladin:SetRearmingGroup(GROUP:FindByName("Blue Ammo Truck")) -- -- -- Set the max firing range. A Paladin unit has a range of 20 km. @@ -694,7 +695,7 @@ ARTY.db={ --- Arty script version. -- @field #string version -ARTY.version="1.3.0" +ARTY.version="1.3.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -707,7 +708,7 @@ ARTY.version="1.3.0" -- DONE: Add user defined rearm weapon types. -- DONE: Check if target is in range. Maybe this requires a data base with the ranges of all arty units. -- DONE: Make ARTY move to rearming position. --- DONE: Check that right rearming vehicle is specified. Blue M818, Red Ural-375. Are there more? +-- DONE: Check that right rearming vehicle is specified. Blue M939, Red Ural-375. Are there more? -- DONE: Check if ARTY group is still alive. -- DONE: Handle dead events. -- DONE: Abort firing task if no shooting event occured with 5(?) minutes. Something went wrong then. Min/max range for example. @@ -1532,7 +1533,7 @@ end --- Assign a group, which is responsible for rearming the ARTY group. If the group is too far away from the ARTY group it will be guided towards the ARTY group. -- @param #ARTY self --- @param Wrapper.Group#GROUP group Group that is supposed to rearm the ARTY group. For the blue coalition, this is often a unarmed M818 transport whilst for red an unarmed Ural-375 transport can be used. +-- @param Wrapper.Group#GROUP group Group that is supposed to rearm the ARTY group. For the blue coalition, this is often a unarmed M939 transport whilst for red an unarmed Ural-375 transport can be used. -- @return self function ARTY:SetRearmingGroup(group) self:F({group=group}) @@ -1887,7 +1888,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) MESSAGE:New(text, 5):ToAllIf(self.Debug) -- Get Ammo. - self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0=self:GetAmmo(self.Debug) + self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0, self.Narty0=self:GetAmmo(self.Debug) -- Init nuclear explosion parameters if they were not set by user. if self.nukerange==nil then @@ -2093,7 +2094,7 @@ function ARTY:_StatusReport(display) end -- Get Ammo. - local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() + local Nammo, Nshells, Nrockets, Nmissiles, Narty=self:GetAmmo() local Nnukes=self.Nukes local Nillu=self.Nillu local Nsmoke=self.Nsmoke @@ -2106,7 +2107,7 @@ function ARTY:_StatusReport(display) text=text..string.format("Clock = %s\n", Clock) text=text..string.format("FSM state = %s\n", self:GetState()) text=text..string.format("Total ammo count = %d\n", Nammo) - text=text..string.format("Number of shells = %d\n", Nshells) + text=text..string.format("Number of shells = %d\n", Narty) text=text..string.format("Number of rockets = %d\n", Nrockets) text=text..string.format("Number of missiles = %d\n", Nmissiles) text=text..string.format("Number of nukes = %d\n", Nnukes) @@ -2293,7 +2294,7 @@ function ARTY:OnEventShot(EventData) end -- Get current ammo. - local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() + local _nammo,_nshells,_nrockets,_nmissiles,_narty=self:GetAmmo() -- Decrease available nukes because we just fired one. if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes then @@ -2323,7 +2324,7 @@ function ARTY:OnEventShot(EventData) -- Weapon type name for current target. local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype) - self:T(self.lid..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d", self.groupname, _nammo, _nshells, _nrockets, _nmissiles)) + self:T(self.lid..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d", self.groupname, _nammo, _narty, _nrockets, _nmissiles)) self:T(self.lid..string.format("Group %s uses weapontype %s for current target.", self.groupname, _weapontype)) -- Default switches for cease fire and relocation. @@ -2771,7 +2772,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To) self:_EventFromTo("onafterStatus", Event, From, To) -- Get ammo. - local nammo, nshells, nrockets, nmissiles=self:GetAmmo() + local nammo, nshells, nrockets, nmissiles, narty=self:GetAmmo() -- We have a cargo group ==> check if group was loaded into a carrier. if self.iscargo and self.cargogroup then @@ -2788,7 +2789,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- FSM state. local fsmstate=self:GetState() - self:T(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d", fsmstate, nammo, nshells, self.Nsmoke, self.Nillu, self.Nukes, self.nukewarhead/1000000, nrockets, nmissiles)) + self:T(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d", fsmstate, nammo, narty, self.Nsmoke, self.Nillu, self.Nukes, self.nukewarhead/1000000, nrockets, nmissiles)) if self.Controllable and self.Controllable:IsAlive() then @@ -2871,20 +2872,19 @@ function ARTY:onafterStatus(Controllable, From, Event, To) if self.currentTarget then self:CeaseFire(self.currentTarget) end - - -- Open fire on timed target. - self:OpenFire(_timedTarget) - + + if self:is("CombatReady") then + -- Open fire on timed target. + self:OpenFire(_timedTarget) + end elseif _normalTarget then - - -- Open fire on normal target. - self:OpenFire(_normalTarget) - + + if self:is("CombatReady") then + -- Open fire on normal target. + self:OpenFire(_normalTarget) + end end - -- Get ammo. - --local nammo, nshells, nrockets, nmissiles=self:GetAmmo() - -- Check if we have a target in the queue for which weapons are still available. local gotsome=false if #self.targets>0 then @@ -3045,14 +3045,14 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) local range=Controllable:GetCoordinate():Get2DDistance(target.coord) -- Get ammo. - local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() - local nfire=Nammo + local Nammo, Nshells, Nrockets, Nmissiles, Narty=self:GetAmmo() + local nfire=Narty local _type="shots" if target.weapontype==ARTY.WeaponType.Auto then - nfire=Nammo + nfire=Narty _type="shots" elseif target.weapontype==ARTY.WeaponType.Cannon then - nfire=Nshells + nfire=Narty _type="shells" elseif target.weapontype==ARTY.WeaponType.TacticalNukes then nfire=self.Nukes @@ -3337,7 +3337,7 @@ function ARTY:_CheckRearmed() self:F2() -- Get current ammo. - local nammo,nshells,nrockets,nmissiles=self:GetAmmo() + local nammo,nshells,nrockets,nmissiles,narty=self:GetAmmo() -- Number of units still alive. local units=self.Controllable:GetUnits() @@ -3603,7 +3603,11 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) if weapontype==ARTY.WeaponType.TacticalNukes or weapontype==ARTY.WeaponType.IlluminationShells or weapontype==ARTY.WeaponType.SmokeShells then weapontype=ARTY.WeaponType.Cannon end - + + if group:HasTask() then + group:ClearTasks() + end + -- Set ROE to weapon free. group:OptionROEOpenFire() @@ -3614,7 +3618,7 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) local fire=group:TaskFireAtPoint(vec2, radius, nshells, weapontype) -- Execute task. - group:SetTask(fire) + group:SetTask(fire,1) end --- Set task for attacking a group. @@ -3631,7 +3635,11 @@ function ARTY:_AttackGroup(target) if weapontype==ARTY.WeaponType.TacticalNukes or weapontype==ARTY.WeaponType.IlluminationShells or weapontype==ARTY.WeaponType.SmokeShells then weapontype=ARTY.WeaponType.Cannon end - + + if group:HasTask() then + group:ClearTasks() + end + -- Set ROE to weapon free. group:OptionROEOpenFire() @@ -3642,7 +3650,7 @@ function ARTY:_AttackGroup(target) local fire=group:TaskAttackGroup(targetgroup, weapontype, AI.Task.WeaponExpend.ONE, 1) -- Execute task. - group:SetTask(fire) + group:SetTask(fire,1) end @@ -3915,6 +3923,7 @@ end -- @return #number Number of shells the group has left. -- @return #number Number of rockets the group has left. -- @return #number Number of missiles the group has left. +-- @return #number Number of artillery shells the group has left. function ARTY:GetAmmo(display) self:F3({display=display}) @@ -3928,6 +3937,7 @@ function ARTY:GetAmmo(display) local nshells=0 local nrockets=0 local nmissiles=0 + local nartyshells=0 -- Get all units. local units=self.Controllable:GetUnits() @@ -4030,7 +4040,8 @@ function ARTY:GetAmmo(display) -- Add up all shells. nshells=nshells+Nammo - + local _,_,_,_,_,shells = unit:GetAmmunition() + nartyshells=nartyshells+shells -- Debug info. text=text..string.format("- %d shells of type %s\n", Nammo, _weaponName) @@ -4076,7 +4087,7 @@ function ARTY:GetAmmo(display) -- Total amount of ammunition. nammo=nshells+nrockets+nmissiles - return nammo, nshells, nrockets, nmissiles + return nammo, nshells, nrockets, nmissiles, nartyshells end --- Returns a name of a missile category. @@ -4827,7 +4838,10 @@ function ARTY:_CheckShootingStarted() -- Check if we waited long enough and no shot was fired. --if dt > self.WaitForShotTime and self.Nshots==0 then - if dt > self.WaitForShotTime and (self.Nshots==0 or self.currentTarget.nshells >= self.Nshots) then --https://github.com/FlightControl-Master/MOOSE/issues/1356 + + self:T(string.format("dt = %d WaitTime = %d | shots = %d TargetShells = %d",dt,self.WaitForShotTime,self.Nshots,self.currentTarget.nshells)) + + if (dt > self.WaitForShotTime and self.Nshots==0) or (self.currentTarget.nshells <= self.Nshots) then --https://github.com/FlightControl-Master/MOOSE/issues/1356 -- Debug info. self:T(self.lid..string.format("%s, no shot event after %d seconds. Removing current target %s from list.", self.groupname, self.WaitForShotTime, name)) @@ -4889,7 +4903,7 @@ end function ARTY:_CheckOutOfAmmo(targets) -- Get current ammo. - local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() + local _nammo,_nshells,_nrockets,_nmissiles,_narty=self:GetAmmo() -- Special weapon type requested ==> Check if corresponding ammo is empty. local _partlyoutofammo=false @@ -4901,7 +4915,7 @@ function ARTY:_CheckOutOfAmmo(targets) self:T(self.lid..string.format("Group %s, auto weapon requested for target %s but all ammo is empty.", self.groupname, Target.name)) _partlyoutofammo=true - elseif Target.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then + elseif Target.weapontype==ARTY.WeaponType.Cannon and _narty==0 then self:T(self.lid..string.format("Group %s, cannons requested for target %s but shells empty.", self.groupname, Target.name)) _partlyoutofammo=true @@ -4945,14 +4959,14 @@ end function ARTY:_CheckWeaponTypeAvailable(target) -- Get current ammo of group. - local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() + local Nammo, Nshells, Nrockets, Nmissiles, Narty=self:GetAmmo() -- Check if enough ammo is there for the selected weapon type. local nfire=Nammo if target.weapontype==ARTY.WeaponType.Auto then nfire=Nammo elseif target.weapontype==ARTY.WeaponType.Cannon then - nfire=Nshells + nfire=Narty elseif target.weapontype==ARTY.WeaponType.TacticalNukes then nfire=self.Nukes elseif target.weapontype==ARTY.WeaponType.IlluminationShells then From 897a0d4f40510c2a8dfd1ca18f031590b9f5ccad Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 25 Jun 2024 13:41:31 +0200 Subject: [PATCH 576/603] xxx --- .../Moose/Functional/Artillery.lua | 49 +------------------ 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index c1745cac0..f48e36d07 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -2301,12 +2301,12 @@ function ARTY:OnEventShot(EventData) self.Nukes=self.Nukes-1 end - -- Decrease available illuminatin shells because we just fired one. + -- Decrease available illumination shells because we just fired one. if self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells then self.Nillu=self.Nillu-1 end - -- Decrease available illuminatin shells because we just fired one. + -- Decrease available smoke shells because we just fired one. if self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells then self.Nsmoke=self.Nsmoke-1 end @@ -3717,51 +3717,6 @@ function ARTY:_NuclearBlast(_coord) ignite(_fires) end ---[[ - local ZoneNuke=ZONE_RADIUS:New("Nukezone", _coord:GetVec2(), 2000) - - -- Scan for Scenery objects. - ZoneNuke:Scan(Object.Category.SCENERY) - - -- Array with all possible hideouts, i.e. scenery objects in the vicinity of the group. - local scenery={} - - for SceneryTypeName, SceneryData in pairs(ZoneNuke:GetScannedScenery()) do - for SceneryName, SceneryObject in pairs(SceneryData) do - - local SceneryObject = SceneryObject -- Wrapper.Scenery#SCENERY - - -- Position of the scenery object. - local spos=SceneryObject:GetCoordinate() - - -- Distance from group to impact point. - local distance= spos:Get2DDistance(_coord) - - -- Place markers on every possible scenery object. - if self.Debug then - local MarkerID=spos:MarkToAll(string.format("%s scenery object %s", self.Controllable:GetName(), SceneryObject:GetTypeName())) - local text=string.format("%s scenery: %s, Coord %s", self.Controllable:GetName(), SceneryObject:GetTypeName(), SceneryObject:GetCoordinate():ToStringLLDMS()) - self:T2(SUPPRESSION.id..text) - end - - -- Add to table. - table.insert(scenery, {object=SceneryObject, distance=distance}) - - --SceneryObject:Destroy() - end - end - - -- Sort scenery wrt to distance from impact point. --- local _sort = function(a,b) return a.distance < b.distance end --- table.sort(scenery,_sort) - --- for _,object in pairs(scenery) do --- local sobject=object -- Wrapper.Scenery#SCENERY --- sobject:Destroy() --- end - -]] - end --- Route group to a certain point. From a4b187d5610b6687db195d6bd29d7e731a64ff01 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 30 Jun 2024 15:20:17 +0200 Subject: [PATCH 577/603] xx --- Moose Development/Moose/Modules_local.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index b9aa9b0ab..ecb494af1 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -1,7 +1,7 @@ __Moose.Include( 'Utilities\\Enums.lua' ) __Moose.Include( 'Utilities\\Utils.lua' ) __Moose.Include( 'Utilities\\Profiler.lua' ) -__Moose.Include( 'Utilities\\STTS.lua' ) +--__Moose.Include( 'Utilities\\STTS.lua' ) __Moose.Include( 'Utilities\\FiFo.lua' ) __Moose.Include( 'Utilities\\Socket.lua' ) From b6ac1e86e4899505e46f89e62cc8d2a48022c37c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 30 Jun 2024 15:20:23 +0200 Subject: [PATCH 578/603] xx --- Moose Development/Moose/Modules.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 1710b59d1..831f807ef 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -2,7 +2,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Enums.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Utils.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Profiler.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Templates.lua' ) -__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/STTS.lua' ) +--__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/STTS.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/FiFo.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Socket.lua' ) From 9479b338c54724bf73dd95fef7635960551cbe34 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 30 Jun 2024 15:57:09 +0200 Subject: [PATCH 579/603] xxx --- Moose Development/Moose/Core/Message.lua | 35 +++++-------------- Moose Development/Moose/Ops/ATIS.lua | 2 +- Moose Development/Moose/Ops/Awacs.lua | 2 +- Moose Development/Moose/Sound/SoundOutput.lua | 2 +- Moose Development/Moose/Utilities/Utils.lua | 19 +++++----- 5 files changed, 22 insertions(+), 38 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 0a3defdec..1ccbd5b89 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -177,40 +177,22 @@ end -- -- -- Send the 2 messages created with the @{New} method to the Client Group. -- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1. --- ClientGroup = Group.getByName( "ClientGroup" ) +-- Client = CLIENT:FindByName("NameOfClientUnit") -- --- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup ) +-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( Client ) +-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( Client ) -- or --- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25 ):ToClient( ClientGroup ) --- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25 ):ToClient( ClientGroup ) +-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25 ):ToClient( Client ) +-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25 ):ToClient( Client ) -- or -- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25 ) -- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25 ) --- MessageClient1:ToClient( ClientGroup ) --- MessageClient2:ToClient( ClientGroup ) +-- MessageClient1:ToClient( Client ) +-- MessageClient2:ToClient( Client ) -- function MESSAGE:ToClient( Client, Settings ) self:F( Client ) - - if Client and Client:GetClientGroupID() then - - if self.MessageType then - local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS - self.MessageDuration = Settings:GetMessageTime( self.MessageType ) - self.MessageCategory = "" -- self.MessageType .. ": " - end - - local Unit = Client:GetClient() - - if self.MessageDuration ~= 0 then - local ClientGroupID = Client:GetClientGroupID() - self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - --trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen) - trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen) - end - end - + self:ToUnit(Client,Settings) return self end @@ -257,6 +239,7 @@ function MESSAGE:ToUnit( Unit, Settings ) if self.MessageDuration ~= 0 then self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) + local ID = Unit:GetID() trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen ) end end diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index a98245b0b..2f845cb4d 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -2916,7 +2916,7 @@ function ATIS:onafterReport( From, Event, To, Text ) self:T( "SRS TTS: " .. text ) -- Play text-to-speech report. - local duration = STTS.getSpeechTime(text,0.95) + local duration = MSRS.getSpeechTime(text,0.95) self.msrsQ:NewTransmission(text,duration,self.msrs,nil,2) --self.msrs:PlayText( text ) self.SRSText = text diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 4ddabce63..ac62198d6 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1626,7 +1626,7 @@ function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,Fro RadioEntry.TextScreen = TextScreen or TextTTS RadioEntry.GroupID = GID RadioEntry.ToScreen = ToScreen - RadioEntry.Duration = STTS.getSpeechTime(TextTTS,0.95,false) or 8 + RadioEntry.Duration = MSRS.getSpeechTime(TextTTS,0.95,false) or 8 RadioEntry.FromAI = FromAI RadioEntry.IsGroup = IsGroup if Tactical then diff --git a/Moose Development/Moose/Sound/SoundOutput.lua b/Moose Development/Moose/Sound/SoundOutput.lua index a042c2392..b2671b91f 100644 --- a/Moose Development/Moose/Sound/SoundOutput.lua +++ b/Moose Development/Moose/Sound/SoundOutput.lua @@ -356,7 +356,7 @@ do -- Text-To-Speech local self=BASE:Inherit(self, BASE:New()) -- #SOUNDTEXT self:SetText(Text) - self:SetDuration(Duration or STTS.getSpeechTime(Text)) + self:SetDuration(Duration or MSRS.getSpeechTime(Text)) --self:SetGender() --self:SetCulture() diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 360fc9c99..9010766b0 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -482,6 +482,15 @@ UTILS.BasicSerialize = function(s) end end +--- Counts the number of elements in a table. +-- @param #table T Table to count +-- @return #int Number of elements in the table +function UTILS.TableLength(T) + local count = 0 + for _ in pairs(T or {}) do count = count + 1 end + return count +end + --- Print a table to log in a nice format -- @param #table table The table to print -- @param #number indent Number of indents @@ -496,7 +505,7 @@ function UTILS.PrintTableToLog(table, indent, noprint) if not indent then indent = 0 end for k, v in pairs(table) do if string.find(k," ") then k='"'..k..'"'end - if type(v) == "table" then + if type(v) == "table" and UTILS.TableLength(v) > 0 then if not noprint then env.info(string.rep(" ", indent) .. tostring(k) .. " = {") end @@ -3676,14 +3685,6 @@ function table.index_of(table, element) return nil end ---- Counts the number of elements in a table. --- @param #table T Table to count --- @return #int Number of elements in the table -function table.length(T) - local count = 0 - for _ in pairs(T) do count = count + 1 end - return count -end --- Slices a table between two indices, much like Python's my_list[2:-1] -- @param #table tbl Table to slice From 19ff7ce9a0124536b092afe71dd6e1f6a714ba48 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 2 Jul 2024 12:46:35 +0200 Subject: [PATCH 580/603] #EASYGCICAP - add options to set default despawn after holding or landing --- Moose Development/Moose/Ops/EasyGCICAP.lua | 48 ++++++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index e9db4aea1..06ae1d6ca 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -65,6 +65,8 @@ -- @field #boolean TankerInvisible -- @field #number CapFormation -- @field #table ReadyFlightGroups +-- @field #boolean DespawnAfterLanding +-- @field #boolean DespawnAfterHolding -- @extends Core.Fsm#FSM --- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown. @@ -209,6 +211,8 @@ EASYGCICAP = { TankerInvisible = true, CapFormation = nil, ReadyFlightGroups = {}, + DespawnAfterLanding = false, + DespawnAfterHolding = true, } --- Internal Squadron data type @@ -244,7 +248,7 @@ EASYGCICAP = { --- EASYGCICAP class version. -- @field #string version -EASYGCICAP.version="0.1.10" +EASYGCICAP.version="0.1.11" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -292,6 +296,8 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName) self.Monitor = false self.TankerInvisible = true self.CapFormation = ENUMS.Formation.FixedWing.FingerFour.Group + self.DespawnAfterLanding = false + self.DespawnAfterHolding = true -- Set some string id for output to DCS.log file. self.lid=string.format("EASYGCICAP %s | ", self.alias) @@ -341,7 +347,7 @@ end -- @param #number Maxiumum Maxmimum number of parallel missions allowed. Count is Cap-Missions + Intercept-Missions + Alert5-Missionsm default is 6 -- @return #EASYGCICAP self function EASYGCICAP:SetMaxAliveMissions(Maxiumum) - self:T(self.lid.."SetDefaultResurrection") + self:T(self.lid.."SetMaxAliveMissions") self.MaxAliveMissions = Maxiumum or 8 return self end @@ -458,6 +464,26 @@ function EASYGCICAP:SetDefaultOverhead(Overhead) return self end +--- Set default despawning after landing. +-- @param #EASYGCICAP self +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultDespawnAfterLanding() + self:T(self.lid.."SetDefaultDespawnAfterLanding") + self.DespawnAfterLanding = true + self.DespawnAfterHolding = false + return self +end + +--- Set default despawning after holding (despawn in air close to AFB). +-- @param #EASYGCICAP self +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultDespawnAfterHolding() + self:T(self.lid.."SetDefaultDespawnAfterLanding") + self.DespawnAfterLanding = false + self.DespawnAfterHolding = true + return self +end + --- Set CAP mission start to vary randomly between Start end End seconds. -- @param #EASYGCICAP self -- @param #number Start @@ -512,6 +538,8 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) self:T(self.lid.."_AddAirwing "..Airbasename) local CapFormation = self.CapFormation + local DespawnAfterLanding = self.DespawnAfterLanding + local DespawnAfterHolding = self.DespawnAfterHolding -- Create Airwing local CAP_Wing = AIRWING:New(Airbasename,Alias) @@ -553,8 +581,11 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) function CAP_Wing:OnAfterFlightOnMission(From, Event, To, Flightgroup, Mission) local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP - --flightgroup:SetDespawnAfterLanding() - flightgroup:SetDespawnAfterHolding() + if DespawnAfterLanding then + flightgroup:SetDespawnAfterLanding() + elseif DespawnAfterHolding then + flightgroup:SetDespawnAfterHolding() + end flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename)) flightgroup:GetGroup():CommandEPLRS(true,5) flightgroup:GetGroup():SetOptionRadarUsingForContinousSearch() @@ -577,10 +608,11 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) flightgroup:GetGroup():OptionROTEvadeFire() flightgroup:SetFuelLowRTB(true) Intel:AddAgent(flightgroup) - function flightgroup:OnAfterHolding(From,Event,To) - self:Despawn(1,true) - end - + if DespawnAfterHolding then + function flightgroup:OnAfterHolding(From,Event,To) + self:Despawn(1,true) + end + end end if self.noaltert5 > 0 then From 336ac9eddbcfb15e0d279fcbcab8f6de04cc3c7a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 11 Jul 2024 08:35:16 +0200 Subject: [PATCH 581/603] #AWACS - MS TTS - fix spelling out B-R-A in Threat call --- Moose Development/Moose/Ops/Awacs.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index ac62198d6..0f4bef1f5 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -508,7 +508,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.64", -- #string + version = "0.2.65", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -5585,6 +5585,12 @@ function AWACS:_ThreatRangeCall(GID,Contact) local grptxt = self.gettext:GetEntry("GROUP",self.locale) local thrt = self.gettext:GetEntry("THREAT",self.locale) local text = string.format("%s. %s. %s %s, %s. %s",self.callsigntxt,pilotcallsign,contacttag,grptxt, thrt, BRATExt) + -- DONE MS TTS - fix spelling out B-R-A in this case + if string.find(text,"BRAA",1,true) then + text = string.gsub(text,"BRAA","brah") + elseif string.find(text,"BRA",1,true) then + text = string.gsub(text,"BRA","brah") + end if IsSub == false then self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) end From 29fb03e9030dc83cba12f4e46bd1f38989d50232 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 12 Jul 2024 08:09:59 +0200 Subject: [PATCH 582/603] xx --- Moose Development/Moose/Functional/Mantis.lua | 1 + Moose Development/Moose/Ops/Awacs.lua | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index b6d711964..6e7f045b9 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -59,6 +59,7 @@ -- @field #number ShoradTime Timer in seconds, how long #SHORAD will be active after a detection inside of the defense range -- @field #number ShoradActDistance Distance of an attacker in meters from a Mantis SAM site, on which Shorad will be switched on. Useful to not give away Shorad sites too early. Default 15km. Should be smaller than checkradius. -- @field #boolean checkforfriendlies If true, do not activate a SAM installation if a friendly aircraft is in firing range. +-- @field #table FilterZones Table of Core.Zone#ZONE Zones Consider SAM groups in this zone(s) only for this MANTIS instance, must be handed as #table of Zone objects. -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 0f4bef1f5..3b048ada3 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -17,7 +17,7 @@ -- === -- -- ### Author: **applevangelist** --- @date Last Update Jan 2024 +-- @date Last Update July 2024 -- @module Ops.AWACS -- @image OPS_AWACS.jpg @@ -935,7 +935,7 @@ AWACS.TaskStatus = { --@field #boolean FromAI ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO-List 0.2.53 +-- TODO-List 0.2.54 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- -- DONE - WIP - Player tasking, VID From c7e73f3b1a4dcd9c531478a1cef732c01811abb9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 12 Jul 2024 08:38:42 +0200 Subject: [PATCH 583/603] #EASYGCICAP - fix launching interceptors from conquered, wrong coalition wings --- Moose Development/Moose/Ops/EasyGCICAP.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 06ae1d6ca..b5481c6d9 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -7,6 +7,7 @@ -- ------------------------------------------------------------------------- -- Date: September 2023 +-- Last Update: July 2024 ------------------------------------------------------------------------- -- --- **Ops** - Easy GCI & CAP Manager @@ -248,7 +249,7 @@ EASYGCICAP = { --- EASYGCICAP class version. -- @field #string version -EASYGCICAP.version="0.1.11" +EASYGCICAP.version="0.1.12" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1156,7 +1157,7 @@ function EASYGCICAP:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,Group return assigned, wingsize end ---- Add a zone to the rejected zones set. +--- Here, we'll decide if we need to launch an intercepting flight, and from where -- @param #EASYGCICAP self -- @param Ops.Intel#INTEL.Cluster Cluster -- @return #EASYGCICAP self @@ -1200,9 +1201,11 @@ function EASYGCICAP:_AssignIntercept(Cluster) local zone = _data[2] -- Core.Zone#ZONE local zonecoord = zone:GetCoordinate() local name = _data[3] -- #string + local coa = AIRBASE:FindByName(name):GetCoalition() local distance = position:DistanceFromPointVec2(zonecoord) local airframes = airwing:CountAssets(true) - if distance < bestdistance and airframes >= wingsize then + local samecoalitionab = coa == self.coalition and true or false + if distance < bestdistance and airframes >= wingsize and samecoalitionab == true then bestdistance = distance targetairwing = airwing targetawname = name @@ -1218,10 +1221,11 @@ function EASYGCICAP:_AssignIntercept(Cluster) local name = data.AirbaseName local zonecoord = data.Coordinate local airwing = wings[name][1] - + local coa = AIRBASE:FindByName(name):GetCoalition() + local samecoalitionab = coa == self.coalition and true or false local distance = position:DistanceFromPointVec2(zonecoord) local airframes = airwing:CountAssets(true) - if distance < bestdistance and airframes >= wingsize then + if distance < bestdistance and airframes >= wingsize and samecoalitionab == true then bestdistance = distance targetairwing = airwing -- Ops.Airwing#AIRWING targetawname = name From 925233709b46feb0f4682bc1363c7ff6079b3b0b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 12 Jul 2024 11:46:12 +0200 Subject: [PATCH 584/603] xx --- Moose Development/Moose/Utilities/Utils.lua | 10 ++++-- Moose Development/Moose/Wrapper/Airbase.lua | 37 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 6d09da638..e9bbe64ea 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -66,7 +66,8 @@ DCSMAP = { MarianaIslands="MarianaIslands", Falklands="Falklands", Sinai="SinaiMap", - Kola="Kola" + Kola="Kola", + Afghanistan="Afghanistan", } @@ -1753,7 +1754,8 @@ end -- * Mariana Islands +2 (East) -- * Falklands +12 (East) - note there's a LOT of deviation across the map, as we're closer to the South Pole -- * Sinai +4.8 (East) --- * Kola +15 (East) - not there is a lot of deviation across the map (-1° to +24°), as we are close to the North pole +-- * Kola +15 (East) - note there is a lot of deviation across the map (-1° to +24°), as we are close to the North pole +-- * Afghanistan +3 (East) - actually +3.6 (NW) to +2.3 (SE) -- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre -- @return #number Declination in degrees. function UTILS.GetMagneticDeclination(map) @@ -1782,6 +1784,8 @@ function UTILS.GetMagneticDeclination(map) declination=4.8 elseif map==DCSMAP.Kola then declination=15 + elseif map==DCSMAP.Afghanistan then + declination=3 else declination=0 end @@ -2013,6 +2017,8 @@ function UTILS.GMTToLocalTimeDifference() return 2 -- Currently map is +2 but should be +3 (DCS bug?) elseif theatre==DCSMAP.Kola then return 3 -- Currently map is +2 but should be +3 (DCS bug?) + elseif theatre==DCSMAP.Afghanistan then + return 4.5 -- UTC +4:30 else BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre))) return 0 diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 85de483d6..733975015 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -753,6 +753,43 @@ AIRBASE.Kola = { ["Severomorsk_3"] = "Severomorsk-3", } +--- Airbases of the Afghanistan map +-- +-- * AIRBASE.Afghanistan.Bost +-- * AIRBASE.Afghanistan.Camp_Bastion +-- * AIRBASE.Afghanistan.Camp_Bastion_Heliport +-- * AIRBASE.Afghanistan.Chaghcharan +-- * AIRBASE.Afghanistan.Dwyer +-- * AIRBASE.Afghanistan.Farah +-- * AIRBASE.Afghanistan.Herat +-- * AIRBASE.Afghanistan.Kandahar +-- * AIRBASE.Afghanistan.Kandahar_Heliport +-- * AIRBASE.Afghanistan.Maymana_Zahiraddin_Faryabi +-- * AIRBASE.Afghanistan.Nimroz +-- * AIRBASE.Afghanistan.Qala_i_Naw +-- * AIRBASE.Afghanistan.Shindand +-- * AIRBASE.Afghanistan.Shindand_Heliport +-- * AIRBASE.Afghanistan.Tarinkot +-- +-- @field Afghanistan +AIRBASE.Afghanistan = { + ["Bost"] = "Bost", + ["Camp_Bastion"] = "Camp Bastion", + ["Camp_Bastion_Heliport"] = "Camp Bastion Heliport", + ["Chaghcharan"] = "Chaghcharan", + ["Dwyer"] = "Dwyer", + ["Farah"] = "Farah", + ["Herat"] = "Herat", + ["Kandahar"] = "Kandahar", + ["Kandahar_Heliport"] = "Kandahar Heliport", + ["Maymana_Zahiraddin_Faryabi"] = "Maymana Zahiraddin Faryabi", + ["Nimroz"] = "Nimroz", + ["Qala_i_Naw"] = "Qala i Naw", + ["Shindand"] = "Shindand", + ["Shindand_Heliport"] = "Shindand Heliport", + ["Tarinkot"] = "Tarinkot", +} + --- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". -- @type AIRBASE.ParkingSpot -- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot. From af8574543377d7f70206ed11685c50b2764e1628 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 12 Jul 2024 19:33:35 +0200 Subject: [PATCH 585/603] CONTROLLABLE:TaskLandAtVec2( Vec2, Duration , CombatLanding, DirectionAfterLand) --- Moose Development/Moose/Ops/Auftrag.lua | 9 +++++--- .../Moose/Wrapper/Controllable.lua | 21 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 1ae498766..a58e933ff 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1062,8 +1062,10 @@ end -- @param #number Time Time in seconds to stay. Default 300 seconds. -- @param #number Speed Speed in knots to fly to the target coordinate. Default 150kn. -- @param #number MissionAlt Altitude to fly towards the mission in feet AGL. Default 1000ft. +-- @param #boolean CombatLanding (Optional) If true, set the Combat Landing option. +-- @param #number DirectionAfterLand (Optional) Heading after landing in degrees. -- @return #AUFTRAG self -function AUFTRAG:NewLANDATCOORDINATE(Coordinate, OuterRadius, InnerRadius, Time, Speed, MissionAlt) +function AUFTRAG:NewLANDATCOORDINATE(Coordinate, OuterRadius, InnerRadius, Time, Speed, MissionAlt, CombatLanding, DirectionAfterLand) local mission=AUFTRAG:New(AUFTRAG.Type.LANDATCOORDINATE) @@ -1071,6 +1073,8 @@ function AUFTRAG:NewLANDATCOORDINATE(Coordinate, OuterRadius, InnerRadius, Time, mission.stayTime = Time or 300 mission.stayAt = Coordinate + mission.combatLand = CombatLanding + mission.directionAfter = DirectionAfterLand self:SetMissionSpeed(Speed or 150) self:SetMissionAltitude(MissionAlt or 1000) @@ -6492,8 +6496,7 @@ function AUFTRAG:GetDCSMissionTask() local DCStask={} local Vec2 = self.stayAt:GetVec2() - local DCStask = CONTROLLABLE.TaskLandAtVec2(nil,Vec2,self.stayTime) - + local DCStask = CONTROLLABLE.TaskLandAtVec2(nil,Vec2,self.stayTime, self.combatLand, self.directionAfter) table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.ONGUARD or self.type==AUFTRAG.Type.ARMOREDGUARD then diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 2817147e4..365a201d6 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -58,7 +58,7 @@ -- * @{#CONTROLLABLE.TaskFollow}: (AIR) Following another airborne controllable. -- * @{#CONTROLLABLE.TaskHold}: (GROUND) Hold ground controllable from moving. -- * @{#CONTROLLABLE.TaskHoldPosition}: (AIR) Hold position at the current position of the first unit of the controllable. --- * @{#CONTROLLABLE.TaskLand}: (AIR HELICOPTER) Landing at the ground. For helicopters only. +-- * @{#CONTROLLABLE.TaskLandAtVec2}: (AIR HELICOPTER) Landing at the ground. For helicopters only. -- * @{#CONTROLLABLE.TaskLandAtZone}: (AIR) Land the controllable at a @{Core.Zone#ZONE_RADIUS). -- * @{#CONTROLLABLE.TaskOrbitCircle}: (AIR) Orbit at the current position of the first unit of the controllable at a specified altitude. -- * @{#CONTROLLABLE.TaskOrbitCircleAtVec2}: (AIR) Orbit at a specified position at a specified altitude during a specified duration with a specified speed. @@ -1516,8 +1516,10 @@ end -- @param #CONTROLLABLE self -- @param DCS#Vec2 Vec2 The point where to land. -- @param #number Duration The duration in seconds to stay on the ground. +-- @param #boolean CombatLanding (optional) If true, set the Combat Landing option. +-- @param #number DirectionAfterLand (optional) Heading after landing in degrees. -- @return #CONTROLLABLE self -function CONTROLLABLE:TaskLandAtVec2( Vec2, Duration ) +function CONTROLLABLE:TaskLandAtVec2( Vec2, Duration , CombatLanding, DirectionAfterLand) local DCSTask = { id = 'Land', @@ -1525,9 +1527,15 @@ function CONTROLLABLE:TaskLandAtVec2( Vec2, Duration ) point = Vec2, durationFlag = Duration and true or false, duration = Duration, + combatLandingFlag = CombatLanding == true and true or false, }, } - + + if DirectionAfterLand ~= nil and type(DirectionAfterLand) == "number" then + DCSTask.params.directionEnabled = true + DCSTask.params.direction = math.rad(DirectionAfterLand) + end + return DCSTask end @@ -1535,13 +1543,16 @@ end -- @param #CONTROLLABLE self -- @param Core.Zone#ZONE Zone The zone where to land. -- @param #number Duration The duration in seconds to stay on the ground. +-- @param #boolean RandomPoint (optional) If true,land at a random point inside of the zone. +-- @param #boolean CombatLanding (optional) If true, set the Combat Landing option. +-- @param #number DirectionAfterLand (optional) Heading after landing in degrees. -- @return DCS#Task The DCS task structure. -function CONTROLLABLE:TaskLandAtZone( Zone, Duration, RandomPoint ) +function CONTROLLABLE:TaskLandAtZone( Zone, Duration, RandomPoint, CombatLanding, DirectionAfterLand ) -- Get landing point local Point = RandomPoint and Zone:GetRandomVec2() or Zone:GetVec2() - local DCSTask = CONTROLLABLE.TaskLandAtVec2( self, Point, Duration ) + local DCSTask = CONTROLLABLE.TaskLandAtVec2( self, Point, Duration, CombatLanding, DirectionAfterLand) return DCSTask end From a833703731c53e850a1028f845facb54982105e9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 13 Jul 2024 15:21:51 +0200 Subject: [PATCH 586/603] xx --- Moose Development/Moose/Core/Database.lua | 11 +++++---- Moose Development/Moose/Core/Event.lua | 29 ++++++++++++----------- Moose Development/Moose/Core/Set.lua | 4 ++++ Moose Development/Moose/Core/Spawn.lua | 19 ++++++++++----- Moose Development/Moose/Ops/Airboss.lua | 1 + Moose Development/Moose/Ops/ArmyGroup.lua | 1 + Moose Development/Moose/Ops/NavyGroup.lua | 3 ++- 7 files changed, 42 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index dec6bd64c..b4931a644 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -135,7 +135,7 @@ function DATABASE:New() self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) - --self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) -- DCS 2.7.1 for Aerial units no dead event ATM + self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) -- DCS 2.7.1 for Aerial units no dead event ATM self:HandleEvent( EVENTS.Hit, self.AccountHits ) self:HandleEvent( EVENTS.NewCargo ) self:HandleEvent( EVENTS.DeleteCargo ) @@ -188,6 +188,7 @@ end --- Deletes a Unit from the DATABASE based on the Unit Name. -- @param #DATABASE self function DATABASE:DeleteUnit( DCSUnitName ) + self:T("DeleteUnit "..tostring(DCSUnitName)) self.UNITS[DCSUnitName] = nil end @@ -1569,7 +1570,6 @@ end -- @param #DATABASE self -- @param Core.Event#EVENTDATA Event function DATABASE:_EventOnDeadOrCrash( Event ) - if Event.IniDCSUnit then local name=Event.IniDCSUnitName @@ -1577,7 +1577,7 @@ function DATABASE:_EventOnDeadOrCrash( Event ) if Event.IniObjectCategory == 3 then --- - -- STATICS + -- STATICS --- if self.STATICS[Event.IniDCSUnitName] then @@ -1587,7 +1587,7 @@ function DATABASE:_EventOnDeadOrCrash( Event ) --- -- Maybe a UNIT? --- - + -- Delete unit. if self.UNITS[Event.IniDCSUnitName] then self:T("STATIC Event for UNIT "..tostring(Event.IniDCSUnitName)) @@ -1610,7 +1610,8 @@ function DATABASE:_EventOnDeadOrCrash( Event ) -- Delete unit. if self.UNITS[Event.IniDCSUnitName] then - self:DeleteUnit(Event.IniDCSUnitName) + self:ScheduleOnce(1,self.DeleteUnit,self,Event.IniDCSUnitName) + --self:DeleteUnit(Event.IniDCSUnitName) end -- Remove client players. diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index b6bbe84a4..6df0cd70e 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -262,9 +262,10 @@ EVENTS = { WeaponRearm = world.event.S_EVENT_WEAPON_REARM or -1, WeaponDrop = world.event.S_EVENT_WEAPON_DROP or -1, -- Added with DCS 2.9.x - UnitTaskTimeout = world.event.S_EVENT_UNIT_TASK_TIMEOUT or -1, + --UnitTaskTimeout = world.event.S_EVENT_UNIT_TASK_TIMEOUT or -1, + UnitTaskComplete = world.event.S_EVENT_UNIT_TASK_COMPLETE or -1, UnitTaskStage = world.event.S_EVENT_UNIT_TASK_STAGE or -1, - MacSubtaskScore = world.event.S_EVENT_MAC_SUBTASK_SCORE or -1, + --MacSubtaskScore = world.event.S_EVENT_MAC_SUBTASK_SCORE or -1, MacExtraScore = world.event.S_EVENT_MAC_EXTRA_SCORE or -1, MissionRestart = world.event.S_EVENT_MISSION_RESTART or -1, MissionWinner = world.event.S_EVENT_MISSION_WINNER or -1, @@ -652,24 +653,24 @@ local _EVENTMETA = { Text = "S_EVENT_WEAPON_DROP" }, -- DCS 2.9 - [EVENTS.UnitTaskTimeout] = { - Order = 1, - Side = "I", - Event = "OnEventUnitTaskTimeout", - Text = "S_EVENT_UNIT_TASK_TIMEOUT " - }, + --[EVENTS.UnitTaskTimeout] = { + -- Order = 1, + -- Side = "I", + -- Event = "OnEventUnitTaskTimeout", + -- Text = "S_EVENT_UNIT_TASK_TIMEOUT " + --}, [EVENTS.UnitTaskStage] = { Order = 1, Side = "I", Event = "OnEventUnitTaskStage", Text = "S_EVENT_UNIT_TASK_STAGE " }, - [EVENTS.MacSubtaskScore] = { - Order = 1, - Side = "I", - Event = "OnEventMacSubtaskScore", - Text = "S_EVENT_MAC_SUBTASK_SCORE" - }, + --[EVENTS.MacSubtaskScore] = { + -- Order = 1, + --Side = "I", + --Event = "OnEventMacSubtaskScore", + --Text = "S_EVENT_MAC_SUBTASK_SCORE" + --}, [EVENTS.MacExtraScore] = { Order = 1, Side = "I", diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index ca82b1ada..798f4c4cd 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1516,6 +1516,7 @@ do self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash ) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) @@ -1548,6 +1549,7 @@ do self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.Crash) self:UnHandleEvent(EVENTS.RemoveUnit) + self:UnHandleEvent(EVENTS.UnitLost) if self.Filter.Zones and self.ZoneTimer and self.ZoneTimer:IsRunning() then self.ZoneTimer:Stop() @@ -2622,6 +2624,7 @@ do -- SET_UNIT self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) local timing = self.ZoneTimerInterval or 30 @@ -7845,6 +7848,7 @@ do -- SET_OPSGROUP self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) end return self diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 75664cee9..ad430a33a 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1238,7 +1238,7 @@ function SPAWN:InitPositionVec2(Vec2) self:T( { self.SpawnTemplatePrefix, Vec2} ) self.SpawnInitPosition = Vec2 self.SpawnFromNewPosition = true - self:I("MaxGroups:"..self.SpawnMaxGroups) + self:T("MaxGroups:"..self.SpawnMaxGroups) for SpawnGroupID = 1, self.SpawnMaxGroups do self:_SetInitialPosition( SpawnGroupID ) end @@ -1390,9 +1390,10 @@ function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) self.SpawnGroups[SpawnGroupID].Visible = true self:HandleEvent( EVENTS.Birth, self._OnBirth ) - self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) + --self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.UnitLost, self._OnDeadOrCrash ) if self.Repeat then self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) self:HandleEvent( EVENTS.Land, self._OnLand ) @@ -1797,8 +1798,9 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth ) if not NoBirth then self:HandleEvent( EVENTS.Birth, self._OnBirth ) end - self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) + --self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.UnitLost, self._OnDeadOrCrash ) self:HandleEvent( EVENTS.RemoveUnit, self._OnDeadOrCrash ) if self.Repeat then self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) @@ -3853,11 +3855,11 @@ end -- @param #number SpawnIndex Spawn index. -- @return #number self.SpawnIndex function SPAWN:_GetSpawnIndex( SpawnIndex ) - self:F2( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnMaxGroups, self.SpawnMaxUnitsAlive, self.AliveUnits, #self.SpawnTemplate.units } ) + self:T( { template=self.SpawnTemplatePrefix, SpawnIndex=SpawnIndex, SpawnMaxGroups=self.SpawnMaxGroups, SpawnMaxUnitsAlive=self.SpawnMaxUnitsAlive, AliveUnits=self.AliveUnits, TemplateUnits=#self.SpawnTemplate.units } ) if (self.SpawnMaxGroups == 0) or (SpawnIndex <= self.SpawnMaxGroups) then if (self.SpawnMaxUnitsAlive == 0) or (self.AliveUnits + #self.SpawnTemplate.units <= self.SpawnMaxUnitsAlive) or self.UnControlled == true then - self:F( { SpawnCount = self.SpawnCount, SpawnIndex = SpawnIndex } ) + self:T( { SpawnCount = self.SpawnCount, SpawnIndex = SpawnIndex } ) if SpawnIndex and SpawnIndex >= self.SpawnCount + 1 then self.SpawnCount = self.SpawnCount + 1 SpawnIndex = self.SpawnCount @@ -3898,12 +3900,17 @@ function SPAWN:_OnBirth( EventData ) end +--- -- @param #SPAWN self -- @param Core.Event#EVENTDATA EventData function SPAWN:_OnDeadOrCrash( EventData ) - self:F( self.SpawnTemplatePrefix ) + self:T( "Dead or crash event ID "..EventData.id) + self:T( "Dead or crash event for "..self.SpawnTemplatePrefix ) + + if EventData.id == EVENTS.Dead then return end local unit=UNIT:FindByName(EventData.IniUnitName) + --local group=GROUP:FindByName(EventData.IniGroupName) if unit then diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 244e252ed..31116dde9 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -3623,6 +3623,7 @@ function AIRBOSS:onafterStart( From, Event, To ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self._PlayerLeft ) self:HandleEvent( EVENTS.MissionEnd ) self:HandleEvent( EVENTS.RemoveUnit ) + self:HandleEvent( EVENTS.UnitLost, self.OnEventRemoveUnit ) -- self.StatusScheduler=SCHEDULER:New(self) -- self.StatusScheduler:Schedule(self, self._Status, {}, 1, 0.5) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 62f3dfec4..40f04de61 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -403,6 +403,7 @@ function ARMYGROUP:New(group) self:HandleEvent(EVENTS.Birth, self.OnEventBirth) self:HandleEvent(EVENTS.Dead, self.OnEventDead) self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit) + self:HandleEvent(EVENTS.UnitLost, self.OnEventRemoveUnit) self:HandleEvent(EVENTS.Hit, self.OnEventHit) -- Start the status monitoring. diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 0ed715fc9..585b39d98 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -393,7 +393,8 @@ function NAVYGROUP:New(group) -- Handle events: self:HandleEvent(EVENTS.Birth, self.OnEventBirth) self:HandleEvent(EVENTS.Dead, self.OnEventDead) - self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit) + self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit) + self:HandleEvent(EVENTS.UnitLost, self.OnEventRemoveUnit) -- Start the status monitoring. self.timerStatus=TIMER:New(self.Status, self):Start(1, 30) From cfc0cc66b2db62c74e1b4c5792549340e4b94025 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 14 Jul 2024 17:48:48 +0200 Subject: [PATCH 587/603] xxx --- Moose Development/Moose/Core/Database.lua | 77 ++++++++++++++++++----- Moose Development/Moose/Core/Settings.lua | 2 +- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index b4931a644..e79dea2df 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -170,10 +170,11 @@ end --- Adds a Unit based on the Unit Name in the DATABASE. -- @param #DATABASE self -- @param #string DCSUnitName Unit name. +-- @param #boolean force -- @return Wrapper.Unit#UNIT The added unit. -function DATABASE:AddUnit( DCSUnitName ) +function DATABASE:AddUnit( DCSUnitName, force ) - if not self.UNITS[DCSUnitName] then + if not self.UNITS[DCSUnitName] or force == true then -- Debug info. self:T( { "Add UNIT:", DCSUnitName } ) @@ -832,15 +833,25 @@ end function DATABASE:FindGroup( GroupName ) local GroupFound = self.GROUPS[GroupName] + + if GroupFound == nil and GroupName ~= nil then + -- see if the group exists in the API, maybe a dynamic slot + self:_RegisterDynamicGroup(GroupName) + return self.GROUPS[GroupName] + end + return GroupFound end --- Adds a GROUP based on the GroupName in the DATABASE. -- @param #DATABASE self -function DATABASE:AddGroup( GroupName ) +-- @param #string GroupName +-- @param #boolean force +-- @return Wrapper.Group#GROUP The Group +function DATABASE:AddGroup( GroupName, force ) - if not self.GROUPS[GroupName] then + if not self.GROUPS[GroupName] or force == true then self:T( { "Add GROUP:", GroupName } ) self.GROUPS[GroupName] = GROUP:Register( GroupName ) end @@ -1346,6 +1357,36 @@ function DATABASE:_RegisterPlayers() return self end +--- Private method that registers a single dynamic slot Group and Units within in the mission. +-- @param #DATABASE self +-- @return #DATABASE self +function DATABASE:_RegisterDynamicGroup(Groupname) + local DCSGroup = Group.getByName(Groupname) + if DCSGroup:isExist() then + + -- Group name. + local DCSGroupName = DCSGroup:getName() + + -- Add group. + self:I(string.format("Register Group: %s", tostring(DCSGroupName))) + self:AddGroup( DCSGroupName, true ) + + -- Loop over units in group. + for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do + + -- Get unit name. + local DCSUnitName = DCSUnit:getName() + + -- Add unit. + self:I(string.format("Register Unit: %s", tostring(DCSUnitName))) + self:AddUnit( DCSUnitName, true ) + + end + else + self:E({"Group does not exist: ", DCSGroup}) + end + return self +end --- Private method that registers all Groups and Units within in the mission. -- @param #DATABASE self @@ -1518,9 +1559,9 @@ function DATABASE:_EventOnBirth( Event ) end if Event.IniObjectCategory == Object.Category.UNIT then - - Event.IniUnit = self:FindUnit( Event.IniDCSUnitName ) + Event.IniGroup = self:FindGroup( Event.IniDCSGroupName ) + Event.IniUnit = self:FindUnit( Event.IniDCSUnitName ) -- Client local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT @@ -1536,7 +1577,7 @@ function DATABASE:_EventOnBirth( Event ) -- Debug info. self:I(string.format("Player '%s' joined unit '%s' of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName))) - + -- Add client in case it does not exist already. if not client then client=self:AddClient(Event.IniDCSUnitName) @@ -1549,14 +1590,19 @@ function DATABASE:_EventOnBirth( Event ) if not self.PLAYERS[PlayerName] then self:AddPlayer( Event.IniUnitName, PlayerName ) end - - -- Player settings. - local Settings = SETTINGS:Set( PlayerName ) - Settings:SetPlayerMenu(Event.IniUnit) - - -- Create an event. - self:CreateEventPlayerEnterAircraft(Event.IniUnit) - + + local function SetPlayerSettings(self,PlayerName,IniUnit) + -- Player settings. + local Settings = SETTINGS:Set( PlayerName ) + --Settings:SetPlayerMenu(Event.IniUnit) + Settings:SetPlayerMenu(IniUnit) + -- Create an event. + self:CreateEventPlayerEnterAircraft(IniUnit) + --self:CreateEventPlayerEnterAircraft(Event.IniUnit) + end + + self:ScheduleOnce(1,SetPlayerSettings,self,PlayerName,Event.IniUnit) + end end @@ -1719,6 +1765,7 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event ) local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT if client then client:RemovePlayer(PlayerName) + --self.PLAYERSETTINGS[PlayerName] = nil end end diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index f16279271..5f814b9df 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -741,7 +741,7 @@ do -- SETTINGS local PlayerGroup = PlayerUnit:GetGroup() local PlayerName = PlayerUnit:GetPlayerName() - local PlayerNames = PlayerGroup:GetPlayerNames() + --local PlayerNames = PlayerGroup:GetPlayerNames() local PlayerMenu = MENU_GROUP:New( PlayerGroup, 'Settings "' .. PlayerName .. '"' ) From 9ecaca365f86eceb84fd2b7009833d9277ee7525 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 14 Jul 2024 18:48:23 +0200 Subject: [PATCH 588/603] xx --- Moose Development/Moose/Ops/CSAR.lua | 10 +++++----- Moose Development/Moose/Ops/CTLD.lua | 6 +++--- Moose Development/Moose/Wrapper/Group.lua | 2 +- Moose Development/Moose/Wrapper/Unit.lua | 9 ++++++++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 7b716ed5c..fd1eda22b 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -31,7 +31,7 @@ -- @image OPS_CSAR.jpg --- --- Last Update April 2024 +-- Last Update July 2024 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM @@ -296,7 +296,7 @@ CSAR.AircraftType["OH58D"] = 2 --- CSAR class version. -- @field #string version -CSAR.version="1.0.24" +CSAR.version="1.0.25" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -2111,12 +2111,12 @@ function CSAR:_AddMedevacMenuItem() local coalition = self.coalition local allheligroupset = self.allheligroupset -- Core.Set#SET_GROUP local _allHeliGroups = allheligroupset:GetSetObjects() - -- rebuild units table local _UnitList = {} for _key, _group in pairs (_allHeliGroups) do - local _unit = _group:GetUnit(1) -- Asume that there is only one unit in the flight for players - if _unit then + local _unit = _group:GetFirstUnitAlive() -- Asume that there is only one unit in the flight for players + if _unit then + --self:T("Unitname ".._unit:GetName().." IsAlive "..tostring(_unit:IsAlive()).." IsPlayer "..tostring(_unit:IsPlayer())) if _unit:IsAlive() and _unit:IsPlayer() then local unitName = _unit:GetName() _UnitList[unitName] = unitName diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 037446ea4..16a87ca11 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -24,7 +24,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update April 2024 +-- Last Update July 2024 do @@ -1255,7 +1255,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.54" +CTLD.version="1.0.55" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3670,7 +3670,7 @@ function CTLD:_RefreshF10Menus() -- rebuild units table local _UnitList = {} for _key, _group in pairs (PlayerTable) do - local _unit = _group:GetUnit(1) -- Wrapper.Unit#UNIT Asume that there is only one unit in the flight for players + local _unit = _group:GetFirstUnitAlive() -- Wrapper.Unit#UNIT Asume that there is only one unit in the flight for players if _unit then if _unit:IsAlive() and _unit:IsPlayer() then if _unit:IsHelicopter() or (self:IsHercules(_unit) and self.enableHercules) then --ensure no stupid unit entries here diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 714fc2faf..633eb5a32 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2745,7 +2745,7 @@ do -- Players local PlayerNames = {} local Units = self:GetUnits() - for UnitID, UnitData in pairs( Units ) do + for UnitID, UnitData in pairs( Units or {}) do local Unit = UnitData -- Wrapper.Unit#UNIT local PlayerName = Unit:GetPlayerName() if PlayerName and PlayerName ~= "" then diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 497d9fb1a..c7f3f02be 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -447,7 +447,14 @@ function UNIT:IsPlayer() -- Units of template group. local template = group:GetTemplate() - if (template == nil) or (template.units == nil ) then return false end + if (template == nil) or (template.units == nil ) then + local DCSObject = self:GetDCSObject() + if DCSObject then + if DCSObject:getPlayerName() ~= nil then return true else return false end + else + return false + end + end local units=template.units From 87b866eca1f4e26d5cfd66d0dbfa52a9a0f73e38 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 14 Jul 2024 19:15:33 +0200 Subject: [PATCH 589/603] xx --- Moose Development/Moose/Core/Event.lua | 2 +- Moose Development/Moose/DCS.lua | 94 +++++++++++++++++--------- 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 6df0cd70e..54813f39c 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -1382,7 +1382,7 @@ function EVENT:onEvent( Event ) end -- Weapon. - if Event.weapon then + if Event.weapon and type(Event.weapon) == "table" then Event.Weapon = Event.weapon Event.WeaponName = Event.weapon:isExist() and Event.weapon:getTypeName() or "Unknown Weapon" Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit! diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 26a0a99e7..c9af9b5b7 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -25,38 +25,68 @@ do -- world --- [https://wiki.hoggitworld.com/view/DCS_enum_world](https://wiki.hoggitworld.com/view/DCS_enum_world) -- @type world.event - -- @field S_EVENT_INVALID - -- @field S_EVENT_SHOT [https://wiki.hoggitworld.com/view/DCS_event_shot](https://wiki.hoggitworld.com/view/DCS_event_shot) - -- @field S_EVENT_HIT [https://wiki.hoggitworld.com/view/DCS_event_hit](https://wiki.hoggitworld.com/view/DCS_event_hit) - -- @field S_EVENT_TAKEOFF [https://wiki.hoggitworld.com/view/DCS_event_takeoff](https://wiki.hoggitworld.com/view/DCS_event_takeoff) - -- @field S_EVENT_LAND [https://wiki.hoggitworld.com/view/DCS_event_land](https://wiki.hoggitworld.com/view/DCS_event_land) - -- @field S_EVENT_CRASH [https://wiki.hoggitworld.com/view/DCS_event_crash](https://wiki.hoggitworld.com/view/DCS_event_crash) - -- @field S_EVENT_EJECTION [https://wiki.hoggitworld.com/view/DCS_event_ejection](https://wiki.hoggitworld.com/view/DCS_event_ejection) - -- @field S_EVENT_REFUELING [https://wiki.hoggitworld.com/view/DCS_event_refueling](https://wiki.hoggitworld.com/view/DCS_event_refueling) - -- @field S_EVENT_DEAD [https://wiki.hoggitworld.com/view/DCS_event_dead](https://wiki.hoggitworld.com/view/DCS_event_dead) - -- @field S_EVENT_PILOT_DEAD [https://wiki.hoggitworld.com/view/DCS_event_pilot_dead](https://wiki.hoggitworld.com/view/DCS_event_pilot_dead) - -- @field S_EVENT_BASE_CAPTURED [https://wiki.hoggitworld.com/view/DCS_event_base_captured](https://wiki.hoggitworld.com/view/DCS_event_base_captured) - -- @field S_EVENT_MISSION_START [https://wiki.hoggitworld.com/view/DCS_event_mission_start](https://wiki.hoggitworld.com/view/DCS_event_mission_start) - -- @field S_EVENT_MISSION_END [https://wiki.hoggitworld.com/view/DCS_event_mission_end](https://wiki.hoggitworld.com/view/DCS_event_mission_end) - -- @field S_EVENT_TOOK_CONTROL - -- @field S_EVENT_REFUELING_STOP [https://wiki.hoggitworld.com/view/DCS_event_refueling_stop](https://wiki.hoggitworld.com/view/DCS_event_refueling_stop) - -- @field S_EVENT_BIRTH [https://wiki.hoggitworld.com/view/DCS_event_birth](https://wiki.hoggitworld.com/view/DCS_event_birth) - -- @field S_EVENT_HUMAN_FAILURE [https://wiki.hoggitworld.com/view/DCS_event_human_failure](https://wiki.hoggitworld.com/view/DCS_event_human_failure) - -- @field S_EVENT_ENGINE_STARTUP [https://wiki.hoggitworld.com/view/DCS_event_engine_startup](https://wiki.hoggitworld.com/view/DCS_event_engine_startup) - -- @field S_EVENT_ENGINE_SHUTDOWN [https://wiki.hoggitworld.com/view/DCS_event_engine_shutdown](https://wiki.hoggitworld.com/view/DCS_event_engine_shutdown) - -- @field S_EVENT_PLAYER_ENTER_UNIT [https://wiki.hoggitworld.com/view/DCS_event_player_enter_unit](https://wiki.hoggitworld.com/view/DCS_event_player_enter_unit) - -- @field S_EVENT_PLAYER_LEAVE_UNIT [https://wiki.hoggitworld.com/view/DCS_event_player_leave_unit](https://wiki.hoggitworld.com/view/DCS_event_player_leave_unit) - -- @field S_EVENT_PLAYER_COMMENT - -- @field S_EVENT_SHOOTING_START [https://wiki.hoggitworld.com/view/DCS_event_shooting_start](https://wiki.hoggitworld.com/view/DCS_event_shooting_start) - -- @field S_EVENT_SHOOTING_END [https://wiki.hoggitworld.com/view/DCS_event_shooting_end](https://wiki.hoggitworld.com/view/DCS_event_shooting_end) - -- @field S_EVENT_MARK ADDED [https://wiki.hoggitworld.com/view/DCS_event_mark_added](https://wiki.hoggitworld.com/view/DCS_event_mark_added) DCS>=2.5.1 - -- @field S_EVENT_MARK CHANGE [https://wiki.hoggitworld.com/view/DCS_event_mark_change](https://wiki.hoggitworld.com/view/DCS_event_mark_change) DCS>=2.5.1 - -- @field S_EVENT_MARK REMOVE [https://wiki.hoggitworld.com/view/DCS_event_mark_remove](https://wiki.hoggitworld.com/view/DCS_event_mark_remove) DCS>=2.5.1 - -- @field S_EVENT_KILL [https://wiki.hoggitworld.com/view/DCS_event_kill](https://wiki.hoggitworld.com/view/DCS_event_kill) DCS>=2.5.6 - -- @field S_EVENT_SCORE [https://wiki.hoggitworld.com/view/DCS_event_score](https://wiki.hoggitworld.com/view/DCS_event_score) DCS>=2.5.6 - -- @field S_EVENT_UNIT_LOST [https://wiki.hoggitworld.com/view/DCS_event_unit_lost](https://wiki.hoggitworld.com/view/DCS_event_unit_lost) DCS>=2.5.6 - -- @field S_EVENT_LANDING_AFTER_EJECTION [https://wiki.hoggitworld.com/view/DCS_event_landing_after_ejection](https://wiki.hoggitworld.com/view/DCS_event_landing_after_ejection) DCS>=2.5.6 - -- @field S_EVENT_MAX + -- @field S_EVENT_INVALID = 0 + -- @field S_EVENT_SHOT = 1 + -- @field S_EVENT_HIT = 2 + -- @field S_EVENT_TAKEOFF = 3 + -- @field S_EVENT_LAND = 4 + -- @field S_EVENT_CRASH = 5 + -- @field S_EVENT_EJECTION = 6 + -- @field S_EVENT_REFUELING = 7 + -- @field S_EVENT_DEAD = 8 + -- @field S_EVENT_PILOT_DEAD = 9 + -- @field S_EVENT_BASE_CAPTURED = 10 + -- @field S_EVENT_MISSION_START = 11 + -- @field S_EVENT_MISSION_END = 12 + -- @field S_EVENT_TOOK_CONTROL = 13 + -- @field S_EVENT_REFUELING_STOP = 14 + -- @field S_EVENT_BIRTH = 15 + -- @field S_EVENT_HUMAN_FAILURE = 16 + -- @field S_EVENT_DETAILED_FAILURE = 17 + -- @field S_EVENT_ENGINE_STARTUP = 18 + -- @field S_EVENT_ENGINE_SHUTDOWN = 19 + -- @field S_EVENT_PLAYER_ENTER_UNIT = 20 + -- @field S_EVENT_PLAYER_LEAVE_UNIT = 21 + -- @field S_EVENT_PLAYER_COMMENT = 22 + -- @field S_EVENT_SHOOTING_START = 23 + -- @field S_EVENT_SHOOTING_END = 24 + -- @field S_EVENT_MARK_ADDED = 25 + -- @field S_EVENT_MARK_CHANGE = 26 + -- @field S_EVENT_MARK_REMOVED = 27 + -- @field S_EVENT_KILL = 28 + -- @field S_EVENT_SCORE = 29 + -- @field S_EVENT_UNIT_LOST = 30 + -- @field S_EVENT_LANDING_AFTER_EJECTION = 31 + -- @field S_EVENT_PARATROOPER_LENDING = 32 -- who's lending whom what? ;) + -- @field S_EVENT_DISCARD_CHAIR_AFTER_EJECTION = 33 + -- @field S_EVENT_WEAPON_ADD = 34 + -- @field S_EVENT_TRIGGER_ZONE = 35 + -- @field S_EVENT_LANDING_QUALITY_MARK = 36 + -- @field S_EVENT_BDA = 37 -- battle damage assessment + -- @field S_EVENT_AI_ABORT_MISSION = 38 + -- @field S_EVENT_DAYNIGHT = 39 + -- @field S_EVENT_FLIGHT_TIME = 40 + -- @field S_EVENT_PLAYER_SELF_KILL_PILOT = 41 + -- @field S_EVENT_PLAYER_CAPTURE_AIRFIELD = 42 + -- @field S_EVENT_EMERGENCY_LANDING = 43 + -- @field S_EVENT_UNIT_CREATE_TASK = 44 + -- @field S_EVENT_UNIT_DELETE_TASK = 45 + -- @field S_EVENT_SIMULATION_START = 46 + -- @field S_EVENT_WEAPON_REARM = 47 + -- @field S_EVENT_WEAPON_DROP = 48 + -- @field S_EVENT_UNIT_TASK_COMPLETE = 49 + -- @field S_EVENT_UNIT_TASK_STAGE = 50 + -- @field S_EVENT_MAC_EXTRA_SCORE= 51 -- not sure what this is + -- @field S_EVENT_MISSION_RESTART= 52 + -- @field S_EVENT_MISSION_WINNER = 53 + -- @field S_EVENT_RUNWAY_TAKEOFF= 54 + -- @field S_EVENT_RUNWAY_TOUCH= 55 + -- @field S_EVENT_MAC_LMS_RESTART= 56 -- not sure what this is + -- @field S_EVENT_SIMULATION_FREEZE = 57 + -- @field S_EVENT_SIMULATION_UNFREEZE = 58 + -- @field S_EVENT_HUMAN_AIRCRAFT_REPAIR_START = 59 + -- @field S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH = 60 + -- @field S_EVENT_MAX = 61 --- The birthplace enumerator is used to define where an aircraft or helicopter has spawned in association with birth events. -- @type world.BirthPlace From 703589d8e4e1313e4ac70303880de643d1152f74 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 15 Jul 2024 14:32:38 +0200 Subject: [PATCH 590/603] xx --- Moose Development/Moose/Core/Spawn.lua | 48 +++++++++++++++++--------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index ad430a33a..b5fddf5c0 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1540,11 +1540,11 @@ function SPAWN:ReSpawn( SpawnIndex ) SpawnGroup:WayPointExecute( 1, 5 ) end - if SpawnGroup.ReSpawnFunction then + if SpawnGroup and SpawnGroup.ReSpawnFunction then SpawnGroup:ReSpawnFunction() end - SpawnGroup:ResetEvents() + if SpawnGroup then SpawnGroup:ResetEvents() end return SpawnGroup end @@ -1565,8 +1565,24 @@ end -- @param #string SpawnIndex The index of the group to be spawned. -- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth ) - self:F2( { SpawnTemplatePrefix = self.SpawnTemplatePrefix, SpawnIndex = SpawnIndex, AliveUnits = self.AliveUnits, SpawnMaxGroups = self.SpawnMaxGroups } ) - + + local set = SET_GROUP:New():FilterAlive():FilterPrefixes({self.SpawnTemplatePrefix, self.SpawnAliasPrefix}):FilterOnce() + local aliveunits = 0 + set:ForEachGroupAlive( + function(grp) + aliveunits = aliveunits + grp:CountAliveUnits() + end + ) + + if aliveunits ~= self.AliveUnits then + self.AliveUnits = aliveunits + self:T("***** self.AliveUnits accounting failure! Corrected! *****") + end + + set= nil + + self:T( { SpawnTemplatePrefix = self.SpawnTemplatePrefix, SpawnIndex = SpawnIndex, AliveUnits = self.AliveUnits, SpawnMaxGroups = self.SpawnMaxGroups } ) + if self:_GetSpawnIndex( SpawnIndex ) then if self.SpawnFromNewPosition then @@ -1613,12 +1629,12 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth ) RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnOuterRadius, self.SpawnInnerRadius ) numTries = numTries + 1 inZone = SpawnZone:IsVec2InZone(RandomVec2) - --self:I("Retrying " .. numTries .. "spawn " .. SpawnTemplate.name .. " in Zone " .. SpawnZone:GetName() .. "!") - --self:I(SpawnZone) + --self:T("Retrying " .. numTries .. "spawn " .. SpawnTemplate.name .. " in Zone " .. SpawnZone:GetName() .. "!") + --self:T(SpawnZone) end end if (not inZone) then - self:I("Could not place unit within zone and within radius!") + self:T("Could not place unit within zone and within radius!") RandomVec2 = SpawnZone:GetRandomVec2() end end @@ -3905,9 +3921,9 @@ end -- @param Core.Event#EVENTDATA EventData function SPAWN:_OnDeadOrCrash( EventData ) self:T( "Dead or crash event ID "..EventData.id) - self:T( "Dead or crash event for "..self.SpawnTemplatePrefix ) + self:T( "Dead or crash event for "..EventData.IniUnitName ) - if EventData.id == EVENTS.Dead then return end + --if EventData.id == EVENTS.Dead then return end local unit=UNIT:FindByName(EventData.IniUnitName) --local group=GROUP:FindByName(EventData.IniGroupName) @@ -3918,14 +3934,11 @@ function SPAWN:_OnDeadOrCrash( EventData ) if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! self:T( { "Dead event: " .. EventPrefix } ) - - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - - self.AliveUnits = self.AliveUnits - 1 - - self:T( "Alive Units: " .. self.AliveUnits ) + self:T(string.format("EventPrefix = %s | SpawnAliasPrefix = %s | Old AliveUnits = %d",EventPrefix,self.SpawnAliasPrefix,self.AliveUnits)) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) and self.AliveUnits > 0 then + self.AliveUnits = self.AliveUnits - 1 end - + self:T( "New Alive Units: " .. self.AliveUnits ) end end end @@ -4051,7 +4064,8 @@ function SPAWN:_SpawnCleanUpScheduler() -- If the plane is not moving or dead , and is on the ground, assign it with a timestamp... if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then self:T( { "CleanUp Scheduler:", "ReSpawning:", SpawnGroup:GetName() } ) - self:ReSpawn( SpawnCursor ) + --self:ReSpawn( SpawnCursor ) + SCHEDULER:New( nil, self.ReSpawn, { self, SpawnCursor }, 3 ) Stamp.Vec2 = nil Stamp.Time = nil end From b890dc22a33ae8c83b7f8108e8c2f3d6cdab20fb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 15 Jul 2024 17:23:45 +0200 Subject: [PATCH 591/603] xx --- Moose Development/Moose/Core/Set.lua | 24 ++++++++++++------- .../Moose/Wrapper/Identifiable.lua | 15 +++++++++--- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 798f4c4cd..9023297f6 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4748,11 +4748,12 @@ do -- SET_CLIENT local MClientCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) + local UnitCategory if ClientCategoryID==nil and MClient:IsAlive()~=nil then - ClientCategoryID=MClient:GetCategory() + ClientCategoryID,UnitCategory=MClient:GetCategory() end - self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and ClientCategoryID and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then + self:T3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then MClientCategory = true end end @@ -5196,12 +5197,15 @@ do -- SET_PLAYER if MClient then local MClientName = MClient.UnitName - if self.Filter.Coalitions then + if self.Filter.Coalitions and MClientInclude then local MClientCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) + if ClientCoalitionID==nil and MClient:IsAlive()~=nil then + ClientCoalitionID=MClient:GetCoalition() + end self:T3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then + if self.FilterMeta.Coalitions[CoalitionName] and ClientCoalitionID and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then MClientCoalition = true end end @@ -5209,12 +5213,16 @@ do -- SET_PLAYER MClientInclude = MClientInclude and MClientCoalition end - if self.Filter.Categories then + if self.Filter.Categories and MClientInclude then local MClientCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) - self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then + local UnitCategory + if ClientCategoryID==nil and MClient:IsAlive()~=nil then + ClientCategoryID,UnitCategory=MClient:GetCategory() + end + self:T3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then MClientCategory = true end end diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 1dfdda3bf..8561d5b2b 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -112,19 +112,28 @@ end -- * Object.Category.SCENERY = 5 -- * Object.Category.Cargo = 6 -- +-- For UNITs this returns a second value, one of +-- +-- Unit.Category.AIRPLANE = 0 +-- Unit.Category.HELICOPTER = 1 +-- Unit.Category.GROUND_UNIT = 2 +-- Unit.Category.SHIP = 3 +-- Unit.Category.STRUCTURE = 4 +-- -- @param #IDENTIFIABLE self -- @return DCS#Object.Category The category ID, i.e. a number. +-- @return DCS#Unit.Category The unit category ID, i.e. a number. For units only. function IDENTIFIABLE:GetCategory() self:F2( self.ObjectName ) local DCSObject = self:GetDCSObject() if DCSObject then - local ObjectCategory = DCSObject:getCategory() + local ObjectCategory, UnitCategory = DCSObject:getCategory() self:T3( ObjectCategory ) - return ObjectCategory + return ObjectCategory, UnitCategory end - return nil + return nil,nil end From b6b99a2e99921485435fdad0ea2aa11f47c36886 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 16 Jul 2024 13:38:14 +0200 Subject: [PATCH 592/603] xx --- Moose Development/Moose/Core/Database.lua | 2 +- Moose Development/Moose/Core/Set.lua | 36 ++++++++++++++-------- Moose Development/Moose/Sound/Radio.lua | 14 ++++++--- Moose Development/Moose/Wrapper/Client.lua | 4 +-- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 6cdf05da7..ed1271037 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -834,7 +834,7 @@ function DATABASE:FindGroup( GroupName ) local GroupFound = self.GROUPS[GroupName] - if GroupFound == nil and GroupName ~= nil then + if GroupFound == nil and GroupName ~= nil and self.Templates.Groups[GroupName] == nil then -- see if the group exists in the API, maybe a dynamic slot self:_RegisterDynamicGroup(GroupName) return self.GROUPS[GroupName] diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 9023297f6..2ba9c7996 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4743,18 +4743,23 @@ do -- SET_CLIENT self:T( { "Evaluated Coalition", MClientCoalition } ) MClientInclude = MClientInclude and MClientCoalition end - + if self.Filter.Categories and MClientInclude then local MClientCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) - local UnitCategory - if ClientCategoryID==nil and MClient:IsAlive()~=nil then + local UnitCategory = 0 + if ClientCategoryID==nil and MClient:IsExist() then ClientCategoryID,UnitCategory=MClient:GetCategory() - end - self:T3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then - MClientCategory = true + self:T3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then + MClientCategory = true + end + else + self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and ClientCategoryID and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then + MClientCategory = true + end end end self:T( { "Evaluated Category", MClientCategory } ) @@ -5217,13 +5222,18 @@ do -- SET_PLAYER local MClientCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) - local UnitCategory - if ClientCategoryID==nil and MClient:IsAlive()~=nil then + local UnitCategory = 0 + if ClientCategoryID==nil and MClient:IsExist() then ClientCategoryID,UnitCategory=MClient:GetCategory() - end - self:T3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then - MClientCategory = true + self:T3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then + MClientCategory = true + end + else + self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and ClientCategoryID and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then + MClientCategory = true + end end end self:T( { "Evaluated Category", MClientCategory } ) diff --git a/Moose Development/Moose/Sound/Radio.lua b/Moose Development/Moose/Sound/Radio.lua index 68acf2a5a..961ccdc6a 100644 --- a/Moose Development/Moose/Sound/Radio.lua +++ b/Moose Development/Moose/Sound/Radio.lua @@ -97,6 +97,7 @@ RADIO = { Power = 100, Loop = false, alias = nil, + moduhasbeenset = false, } --- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast. @@ -167,12 +168,13 @@ function RADIO:SetFrequency(Frequency) self:F2(Frequency) if type(Frequency) == "number" then - + -- If frequency is in range --if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then -- Convert frequency from MHz to Hz - self.Frequency = Frequency * 1000000 + self.Frequency = Frequency + self.HertzFrequency = Frequency * 1000000 -- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then @@ -180,7 +182,7 @@ function RADIO:SetFrequency(Frequency) local commandSetFrequency={ id = "SetFrequency", params = { - frequency = self.Frequency, + frequency = self.HertzFrequency, modulation = self.Modulation, } } @@ -197,7 +199,7 @@ function RADIO:SetFrequency(Frequency) return self end ---- Set AM or FM modulation of the radio transmitter. +--- Set AM or FM modulation of the radio transmitter. Set this before you set a frequency! -- @param #RADIO self -- @param #number Modulation Modulation is either radio.modulation.AM or radio.modulation.FM. -- @return #RADIO self @@ -206,6 +208,10 @@ function RADIO:SetModulation(Modulation) if type(Modulation) == "number" then if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ? self.Modulation = Modulation + if self.moduhasbeenset == false and Modulation == radio.modulation.FM then -- override default + self:SetFrequency(self.Frequency) + end + self.moduhasbeenset = true return self end end diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index f91b7a47c..3fb20deba 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -306,7 +306,7 @@ function CLIENT:IsMultiSeated() return false end ---- Checks for a client alive event and calls a function on a continuous basis. +--- Checks for a client alive event and calls a function on a continuous basis. Does **NOT** work for dynamic spawn client slots! -- @param #CLIENT self -- @param #function CallBackFunction Create a function that will be called when a player joins the slot. -- @param ... (Optional) Arguments for callback function as comma separated list. @@ -325,7 +325,7 @@ end -- @param #CLIENT self function CLIENT:_AliveCheckScheduler( SchedulerName ) - self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } ) + self:T2( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } ) if self:IsAlive() then From e5bf7f104d4fca69448dbf309d7738480899014c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 16 Jul 2024 16:02:47 +0200 Subject: [PATCH 593/603] xx --- Moose Development/Moose/Core/Database.lua | 9 ++-- Moose Development/Moose/DCS.lua | 1 + Moose Development/Moose/Wrapper/Client.lua | 8 +++- .../Moose/Wrapper/Controllable.lua | 42 +++++++++++++++++++ Moose Development/Moose/Wrapper/Storage.lua | 26 ++++++++++-- 5 files changed, 78 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index ed1271037..184525502 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -815,10 +815,11 @@ end --- Adds a CLIENT based on the ClientName in the DATABASE. -- @param #DATABASE self -- @param #string ClientName Name of the Client unit. +-- @param #boolean Force (optional) Force registration of client. -- @return Wrapper.Client#CLIENT The client object. -function DATABASE:AddClient( ClientName ) +function DATABASE:AddClient( ClientName, Force ) - if not self.CLIENTS[ClientName] then + if not self.CLIENTS[ClientName] or Force == true then self.CLIENTS[ClientName] = CLIENT:Register( ClientName ) end @@ -1579,8 +1580,8 @@ function DATABASE:_EventOnBirth( Event ) self:I(string.format("Player '%s' joined unit '%s' of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName))) -- Add client in case it does not exist already. - if not client then - client=self:AddClient(Event.IniDCSUnitName) + if client == nil or (client and client:CountPlayers() == 0) then + client=self:AddClient(Event.IniDCSUnitName, true) end -- Add player. diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index c9af9b5b7..ad702a851 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -1695,6 +1695,7 @@ do -- AI -- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE} -- @field ENGAGE_AIR_WEAPONS -- @field AC_ENGAGEMENT_RANGE_RESTRICTION + -- @field EVASION_OF_ARM --- -- @type AI.Option.Ground.mid -- Moose added diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 3fb20deba..d39f0bb30 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -201,6 +201,13 @@ function CLIENT:AddPlayer(PlayerName) return self end +--- Get number of associated players. +-- @param #CLIENT self +-- @return #number Count +function CLIENT:CountPlayers() + return #self.Players or 0 +end + --- Get player name(s). -- @param #CLIENT self -- @return #table List of player names or an empty table `{}`. @@ -608,4 +615,3 @@ function CLIENT:GetPlayerInfo(Attribute) return nil end end - diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 365a201d6..77700891e 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -3811,6 +3811,48 @@ function CONTROLLABLE:OptionProhibitAfterburner( Prohibit ) return self end +--- [Ground] Allows AI radar units to take defensive actions to avoid anti radiation missiles. Units are allowed to shut radar off and displace. +-- @param #CONTROLLABLE self +-- @param #number Seconds Can be - nil, 0 or false = switch off this option, any positive number = number of seconds the escape sequency runs. +-- @return #CONTROLLABLE self +function CONTROLLABLE:OptionEvasionOfARM(Seconds) + self:F2( { self.ControllableName } ) + + local DCSControllable = self:GetDCSObject() + if DCSControllable then + local Controller = self:_GetController() + + if self:IsGround() then + if Seconds == nil then Seconds = false end + Controller:setOption( AI.Option.Ground.id.EVASION_OF_ARM, Seconds) + end + + end + + return self +end + +--- [Ground] Option that defines the vehicle spacing when in an on road and off road formation. +-- @param #CONTROLLABLE self +-- @param #number meters Can be zero to 100 meters. Defaults to 50 meters. +-- @return #CONTROLLABLE self +function CONTROLLABLE:OptionFormationInterval(meters) + self:F2( { self.ControllableName } ) + + local DCSControllable = self:GetDCSObject() + if DCSControllable then + local Controller = self:_GetController() + + if self:IsGround() then + if meters == nil or meters > 100 or meters < 0 then meters = 50 end + Controller:setOption( 30, meters) + end + + end + + return self +end + --- [Air] Defines the usage of Electronic Counter Measures by airborne forces. -- @param #CONTROLLABLE self -- @param #number ECMvalue Can be - 0=Never on, 1=if locked by radar, 2=if detected by radar, 3=always on, defaults to 1 diff --git a/Moose Development/Moose/Wrapper/Storage.lua b/Moose Development/Moose/Wrapper/Storage.lua index 8e45df179..6f154c575 100644 --- a/Moose Development/Moose/Wrapper/Storage.lua +++ b/Moose Development/Moose/Wrapper/Storage.lua @@ -149,7 +149,7 @@ STORAGE.Liquid = { --- STORAGE class version. -- @field #string version -STORAGE.version="0.0.1" +STORAGE.version="0.0.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -162,7 +162,7 @@ STORAGE.version="0.0.1" -- Constructor ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Create a new STORAGE object from the DCS weapon object. +--- Create a new STORAGE object from the DCS airbase object. -- @param #STORAGE self -- @param #string AirbaseName Name of the airbase. -- @return #STORAGE self @@ -182,8 +182,28 @@ function STORAGE:New(AirbaseName) return self end +--- Create a new STORAGE object from an DCS static cargo object. +-- @param #STORAGE self +-- @param #string StaticCargoName Unit name of the static. +-- @return #STORAGE self +function STORAGE:NewFromStaticCargo(StaticCargoName) ---- Find a STORAGE in the **_DATABASE** using the name associated airbase. + -- Inherit everything from BASE class. + local self=BASE:Inherit(self, BASE:New()) -- #STORAGE + + self.airbase=StaticObject.getByName(StaticCargoName) + + if Airbase.getWarehouse then + self.warehouse=Warehouse.getCargoAsWarehouse(self.airbase) + end + + self.lid = string.format("STORAGE %s", StaticCargoName) + + return self +end + + +--- Airbases only - Find a STORAGE in the **_DATABASE** using the name associated airbase. -- @param #STORAGE self -- @param #string AirbaseName The Airbase Name. -- @return #STORAGE self From a813f8299f6842bb4ac1d1440403d9064e9ef29d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 18 Jul 2024 14:32:22 +0200 Subject: [PATCH 594/603] Range --- Moose Development/Moose/Functional/Range.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index e22125af1..984dbed7e 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -2400,7 +2400,7 @@ function RANGE:onafterSave( From, Event, To ) end -- Path. - local path = lfs.writedir() .. [[Logs\]] + local path = self.targetpath or lfs.writedir() .. [[Logs\]] -- Set file name. local filename = path .. string.format( "RANGE-%s_BombingResults.csv", self.rangename ) @@ -2465,7 +2465,7 @@ function RANGE:onafterLoad( From, Event, To ) end -- Path in DCS log file. - local path = lfs.writedir() .. [[Logs\]] + local path = self.targetpath or lfs.writedir() .. [[Logs\]] -- Set file name. local filename = path .. string.format( "RANGE-%s_BombingResults.csv", self.rangename ) @@ -3856,13 +3856,13 @@ function RANGE:_TargetsheetOnOff( _unitname ) -- Inform player. if playerData and playerData.targeton == true then - text = string.format( "roger, your targetsheets are now SAVED." ) + text = string.format( "Roger, your targetsheets are now SAVED." ) else - text = string.format( "affirm, your targetsheets are NOT SAVED." ) + text = string.format( "Affirm, your targetsheets are NOT SAVED." ) end else - text = "negative, target sheet data recorder is broken on this range." + text = "Negative, target sheet data recorder is broken on this range." end -- Message to player. From bb90ac58ba545e0addd1074f6a4d469876e8a3a6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 18 Jul 2024 16:07:08 +0200 Subject: [PATCH 595/603] xx --- Moose Development/Moose/Ops/ATIS.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 700cc74bd..e835c8885 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -2631,7 +2631,7 @@ function ATIS:onafterBroadcast( From, Event, To ) if not self.ATISforFARPs then -- Active runway. local subtitle = "" - if runwayLanding then + if runwayLanding and runwayLanding ~= runwayTakeoff then local actrun = self.gettext:GetEntry("ACTIVELANDING",self.locale) From 72303ffbad776e63520dbdeffadaa5e6bf98fc98 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 28 Jul 2024 12:05:31 +0200 Subject: [PATCH 596/603] xx --- Moose Development/Moose/Ops/EasyGCICAP.lua | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index b5481c6d9..a58f487d1 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -68,6 +68,7 @@ -- @field #table ReadyFlightGroups -- @field #boolean DespawnAfterLanding -- @field #boolean DespawnAfterHolding +-- @field #list ListOfAuftrag -- @extends Core.Fsm#FSM --- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown. @@ -214,6 +215,7 @@ EASYGCICAP = { ReadyFlightGroups = {}, DespawnAfterLanding = false, DespawnAfterHolding = true, + ListOfAuftrag = {} } --- Internal Squadron data type @@ -249,7 +251,7 @@ EASYGCICAP = { --- EASYGCICAP class version. -- @field #string version -EASYGCICAP.version="0.1.12" +EASYGCICAP.version="0.1.13" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -299,6 +301,7 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName) self.CapFormation = ENUMS.Formation.FixedWing.FingerFour.Group self.DespawnAfterLanding = false self.DespawnAfterHolding = true + self.ListOfAuftrag = {} -- Set some string id for output to DCS.log file. self.lid=string.format("EASYGCICAP %s | ", self.alias) @@ -621,6 +624,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) alert:SetRequiredAssets(self.noaltert5) alert:SetRepeat(99) CAP_Wing:AddMission(alert) + table.insert(self.ListOfAuftrag,alert) end self.wings[Airbasename] = { CAP_Wing, AIRBASE:FindByName(Airbasename):GetZone(), Airbasename } @@ -1264,6 +1268,8 @@ function EASYGCICAP:_AssignIntercept(Cluster) nogozoneset ) end + + table.insert(self.ListOfAuftrag,InterceptAuftrag) local assigned, rest = self:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,contact.group,wingsize) if not assigned then InterceptAuftrag:SetRequiredAssets(rest) @@ -1355,6 +1361,20 @@ end -- @return #EASYGCICAP self function EASYGCICAP:onafterStatus(From,Event,To) self:T({From,Event,To}) + -- cleanup + local cleaned = false + local cleanlist = {} + for _,_auftrag in pairs(self.ListOfAuftrag) do + local auftrag = _auftrag -- Ops.Auftrag#AUFTRAG + if auftrag and (not (auftrag:IsCancelled() or auftrag:IsDone() or auftrag:IsOver())) then + table.insert(cleanlist,auftrag) + cleaned = true + end + end + if cleaned == true then + self.ListOfAuftrag = nil + self.ListOfAuftrag = cleanlist + end -- Gather Some Stats local function counttable(tbl) local count = 0 From 8ae8c94b27eccacf38b62bfcee8016b169e601d6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 28 Jul 2024 12:42:52 +0200 Subject: [PATCH 597/603] xx --- Moose Development/Moose/Core/Set.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 2ba9c7996..5e79318cb 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2107,7 +2107,7 @@ do if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then - local coord=group:GetCoord() + local coord=group:GetCoordinate() -- Distance between ref. coordinate and group coordinate. local d=UTILS.VecDist3D(Coordinate, coord) From c59bf3f334ffea4442e61fff94c9be06042deda9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 28 Jul 2024 13:40:59 +0200 Subject: [PATCH 598/603] xx --- Moose Development/Moose/Core/ClientMenu.lua | 4 ++-- Moose Development/Moose/Core/Settings.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index dae6195d2..59ec7169c 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -417,7 +417,7 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.1.5a", + version = "0.1.6", name = nil, clientset = nil, menutree = {}, @@ -740,7 +740,7 @@ function CLIENTMENUMANAGER:AddEntry(Entry,Client) for _,_client in pairs(Set) do local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then - local playername = client:GetPlayerName() + local playername = client:GetPlayerName() or "None" local unitname = client:GetName() if not knownunits[unitname] then knownunits[unitname] = true diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 5f814b9df..81a60a7f8 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -740,7 +740,7 @@ do -- SETTINGS if _SETTINGS.ShowPlayerMenu == true then local PlayerGroup = PlayerUnit:GetGroup() - local PlayerName = PlayerUnit:GetPlayerName() + local PlayerName = PlayerUnit:GetPlayerName() or "None" --local PlayerNames = PlayerGroup:GetPlayerNames() local PlayerMenu = MENU_GROUP:New( PlayerGroup, 'Settings "' .. PlayerName .. '"' ) From 23697f4fe3f723baa3571ae9cd61375c950ecd76 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 28 Jul 2024 17:54:27 +0200 Subject: [PATCH 599/603] xx --- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index a795d41e5..f1a291f05 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -133,7 +133,7 @@ function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType) self.TTSType = TTSType or "close air support" self.lastsmoketime = 0 - if Repeat then + if type(Repeat) == "boolean" and type(Times) == "number" then self.Repeat = true self.RepeatNo = Times or 1 end From da8ce95adfea5323c0a3fe0f3a9c3a6e903a8322 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 29 Jul 2024 13:01:06 +0200 Subject: [PATCH 600/603] xx --- Moose Development/Moose/Functional/Detection.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index a6196f861..367e460bc 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -604,7 +604,7 @@ do -- DETECTION_BASE -- @param #number DetectionTimeStamp Time stamp of detection event. function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp ) - self:I( { DetectedObjects = self.DetectedObjects } ) + self:T( { DetectedObjects = self.DetectedObjects } ) self.DetectionRun = self.DetectionRun + 1 @@ -612,7 +612,7 @@ do -- DETECTION_BASE if Detection and Detection:IsAlive() then - self:I( { "DetectionGroup is Alive", Detection:GetName() } ) + self:T( { "DetectionGroup is Alive", Detection:GetName() } ) local DetectionGroupName = Detection:GetName() local DetectionUnit = Detection:GetUnit( 1 ) @@ -628,8 +628,8 @@ do -- DETECTION_BASE self.DetectDLINK ) - --self:I( { DetectedTargets = DetectedTargets } ) - --self:I(UTILS.PrintTableToLog(DetectedTargets)) + --self:T( { DetectedTargets = DetectedTargets } ) + --self:T(UTILS.PrintTableToLog(DetectedTargets)) for DetectionObjectID, Detection in pairs( DetectedTargets ) do From f14457380f4e8c450d2e11c0262d41164da55e6c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 30 Jul 2024 07:44:55 +0200 Subject: [PATCH 601/603] xxx --- Moose Development/Moose/Ops/EasyGCICAP.lua | 3 ++- Moose Development/Moose/Ops/RecoveryTanker.lua | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index a58f487d1..15dda8cb3 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -97,7 +97,8 @@ -- -- ### Prerequisites -- --- You have to put a STATIC object on the airbase with the UNIT name according to the name of the airbase. E.g. for Kuitaisi this has to have the name Kutaisi. This object symbolizes the AirWing HQ. +-- You have to put a **STATIC WAREHOUSE** object on the airbase with the UNIT name according to the name of the airbase. **Do not put any other static type or it creates a conflict with the airbase name!** +-- E.g. for Kuitaisi this has to have the unit name Kutaisi. This object symbolizes the AirWing HQ. -- Next put a late activated template group for your CAP/GCI Squadron on the map. Last, put a zone on the map for the CAP operations, let's name it "Blue Zone 1". Size of the zone plays no role. -- Put an EW radar system on the map and name it aptly, like "Blue EWR". -- diff --git a/Moose Development/Moose/Ops/RecoveryTanker.lua b/Moose Development/Moose/Ops/RecoveryTanker.lua index a4c00cfea..cc89771a1 100644 --- a/Moose Development/Moose/Ops/RecoveryTanker.lua +++ b/Moose Development/Moose/Ops/RecoveryTanker.lua @@ -660,7 +660,7 @@ function RECOVERYTANKER:SetRecoveryAirboss(switch) return self end ---- Set that the group takes the roll of an AWACS instead of a refueling tanker. +--- Set that the group takes the role of an AWACS instead of a refueling tanker. -- @param #RECOVERYTANKER self -- @param #boolean switch If true or nil, set roll AWACS. -- @param #boolean eplrs If true or nil, enable EPLRS. If false, EPLRS will be off. From 4a36eea5e61ed7e73ae88555337eca9a56b1692e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 30 Jul 2024 07:48:01 +0200 Subject: [PATCH 602/603] xx --- Moose Development/Moose/Ops/RecoveryTanker.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/RecoveryTanker.lua b/Moose Development/Moose/Ops/RecoveryTanker.lua index cc89771a1..fd567c5fe 100644 --- a/Moose Development/Moose/Ops/RecoveryTanker.lua +++ b/Moose Development/Moose/Ops/RecoveryTanker.lua @@ -662,7 +662,7 @@ end --- Set that the group takes the role of an AWACS instead of a refueling tanker. -- @param #RECOVERYTANKER self --- @param #boolean switch If true or nil, set roll AWACS. +-- @param #boolean switch If true or nil, set role AWACS. -- @param #boolean eplrs If true or nil, enable EPLRS. If false, EPLRS will be off. -- @return #RECOVERYTANKER self function RECOVERYTANKER:SetAWACS(switch, eplrs) From e301efc690d04071f4c0c85ab72e33c8fa3990b3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 3 Aug 2024 13:47:38 +0200 Subject: [PATCH 603/603] xx --- Moose Development/Moose/Core/Set.lua | 25 +++++++++++++++++++++++-- Moose Development/Moose/Core/Spawn.lua | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 5c886ed7d..002d08392 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2149,7 +2149,7 @@ end do -- SET_UNIT --- - -- @type SET_UNIT + -- @type SET_UNIT SET\_UNIT -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -2300,7 +2300,12 @@ do -- SET_UNIT local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) -- #SET_UNIT self:FilterActive( false ) - + + --- Count Alive Units + -- @function [parent=#SET_UNIT] CountAlive + -- @param #SET_UNIT self + -- @return #SET_UNIT self + return self end @@ -2502,6 +2507,22 @@ do -- SET_UNIT self.Filter.Active = Active return self end + + --- Builds a set of units which exist and are alive. + -- @param #SET_UNIT self + -- @return #SET_UNIT self + function SET_UNIT:FilterAlive() + self:FilterFunction( + function(unit) + if unit and unit:IsExist() and unit:IsAlive() then + return true + else + return false + end + end + ) + return self + end --- Builds a set of units having a radar of give types. -- All the units having a radar of a given type will be included within the set. diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index a7cc04718..5e8621921 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1053,7 +1053,7 @@ end -- but they will all follow the same Template route and have the same prefix name. -- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group. -- @param #SPAWN self --- @param #string SpawnTemplatePrefixTable A table with the names of the groups defined within the mission editor, from which one will be chosen when a new group will be spawned. +-- @param #list<#string> SpawnTemplatePrefixTable A table with the names of the groups defined within the mission editor, from which one will be chosen when a new group will be spawned. -- @return #SPAWN -- @usage --