From b522b38d31e5e4c0373cd83a9a644bc3806a8141 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Jan 2025 09:21:48 +0100 Subject: [PATCH 1/9] #SCENERY - Some fixes for kissing getLife function on some objects, and life points being zero on some objects --- Moose Development/Moose/Wrapper/Scenery.lua | 25 ++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 2706aad71..eb69e7ab4 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -48,8 +48,8 @@ function SCENERY:Register( SceneryName, SceneryObject ) self.SceneryObject = SceneryObject - if self.SceneryObject then - self.Life0 = self.SceneryObject:getLife() + if self.SceneryObject and self.SceneryObject.getLife then -- fix some objects do not have all functions + self.Life0 = self.SceneryObject:getLife() or 0 else self.Life0 = 0 end @@ -59,7 +59,7 @@ function SCENERY:Register( SceneryName, SceneryObject ) return self end ---- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists. +--- Returns the value of the scenery 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. @@ -67,6 +67,14 @@ function SCENERY:GetProperty(PropertyName) return self.Properties[PropertyName] end +--- Checks if the value of the scenery with the given PropertyName exists. +-- @param #SCENERY self +-- @param #string PropertyName The name of a the QuadZone Property from the scenery assignment to be retrieved. +-- @return #boolean Outcome True if it exists, else false. +function SCENERY:HasProperty(PropertyName) + return self.Properties[PropertyName] ~= nil and true or false +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 . @@ -97,7 +105,7 @@ function SCENERY:GetDCSObject() return self.SceneryObject end ---- Get current life points from the SCENERY Object. +--- Get current life points from the SCENERY Object. Note - Some scenery objects always have 0 life points. -- **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. @@ -105,7 +113,7 @@ end --@return #number life function SCENERY:GetLife() local life = 0 - if self.SceneryObject then + if self.SceneryObject and self.SceneryObject.getLife then life = self.SceneryObject:getLife() if life > self.Life0 then self.Life0 = math.floor(life * 1.2) @@ -121,7 +129,7 @@ function SCENERY:GetLife0() return self.Life0 or 0 end ---- Check if SCENERY Object is alive. +--- Check if SCENERY Object is alive. Note - Some scenery objects always have 0 life points. --@param #SCENERY self --@param #number Threshold (Optional) If given, SCENERY counts as alive above this relative life in percent (1..100). --@return #number life @@ -133,7 +141,7 @@ function SCENERY:IsAlive(Threshold) end end ---- Check if SCENERY Object is dead. +--- Check if SCENERY Object is dead. Note - Some scenery objects always have 0 life points. --@param #SCENERY self --@param #number Threshold (Optional) If given, SCENERY counts as dead below this relative life in percent (1..100). --@return #number life @@ -145,12 +153,13 @@ function SCENERY:IsDead(Threshold) end end ---- Get SCENERY relative life in percent, e.g. 75. +--- Get SCENERY relative life in percent, e.g. 75. Note - Some scenery objects always have 0 life points. --@param #SCENERY self --@return #number rlife function SCENERY:GetRelativeLife() local life = self:GetLife() local life0 = self:GetLife0() + if life == 0 or life0 == 0 then return 0 end local rlife = math.floor((life/life0)*100) return rlife end From 7f7999e3e5264800fc8cf1e07c0d05aa4a8ec53b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 17 Jan 2025 09:54:41 +0100 Subject: [PATCH 2/9] #CSAR - Make the list of downed pilots' coordinate dependent on _SETTINGS global or pilot --- Moose Development/Moose/Ops/CSAR.lua | 35 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 8cc55125c..e7805f25a 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -809,6 +809,8 @@ end -- @param #boolean noMessage -- @param #string _description Description -- @param #boolean forcedesc Use the description only for the pilot track entry +-- @return Wrapper.Group#GROUP PilotInField Pilot GROUP object +-- @return #string AliasName Alias display name function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description, forcedesc ) self:T(self.lid .. " _AddCsar") self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description}) @@ -878,7 +880,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage, _playerName) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc. - return self + return _spawnedGroup, _alias end --- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. @@ -1829,8 +1831,9 @@ end --- (Internal) Function to get string of a group\'s position. -- @param #CSAR self -- @param Wrapper.Controllable#CONTROLLABLE _woundedGroup Group or Unit object. +-- @param Wrapper.Unit#UNIT _Unit Requesting helo pilot unit -- @return #string Coordinates as Text -function CSAR:_GetPositionOfWounded(_woundedGroup) +function CSAR:_GetPositionOfWounded(_woundedGroup,_Unit) self:T(self.lid .. " _GetPositionOfWounded") local _coordinate = _woundedGroup:GetCoordinate() local _coordinatesText = "None" @@ -1845,6 +1848,26 @@ function CSAR:_GetPositionOfWounded(_woundedGroup) _coordinatesText = _coordinate:ToStringBULLS(self.coalition) end end + if _Unit and _Unit:GetPlayerName() then + local playername = _Unit:GetPlayerName() + if playername then + local settings = _DATABASE:GetPlayerSettings(playername) or _SETTINGS + if settings then + self:T("Get Settings ok!") + if settings:IsA2G_MGRS() then + _coordinatesText = _coordinate:ToStringMGRS(settings) + elseif settings:IsA2G_LL_DMS() then + _coordinatesText = _coordinate:ToStringLLDMS(settings) + elseif settings:IsA2G_LL_DDM() then + _coordinatesText = _coordinate:ToStringLLDDM(settings) + elseif settings:IsA2G_BR() then + -- attention this is the distance from the ASKING unit to target, not from RECCE to target! + local startcoordinate = _Unit:GetCoordinate() + _coordinatesText = _coordinate:ToStringBR(startcoordinate,settings) + end + end + end + end return _coordinatesText end @@ -1870,13 +1893,17 @@ function CSAR:_DisplayActiveSAR(_unitName) self:T({Table=_value}) local _woundedGroup = _value.group if _woundedGroup and _value.alive then - local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup) + local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup,_heli) local _helicoord = _heli:GetCoordinate() local _woundcoord = _woundedGroup:GetCoordinate() local _distance = self:_GetDistance(_helicoord, _woundcoord) self:T({_distance = _distance}) local distancetext = "" - if _SETTINGS:IsImperial() then + local settings = _SETTINGS + if _heli:GetPlayerName() then + settings = _DATABASE:GetPlayerSettings(_heli:GetPlayerName()) or _SETTINGS + end + if settings:IsImperial() then distancetext = string.format("%.1fnm",UTILS.MetersToNM(_distance)) else distancetext = string.format("%.1fkm", _distance/1000.0) From 61b7b3ead66cf4416dbb8d4a3e599740ed9f529e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 18 Jan 2025 15:30:03 +0100 Subject: [PATCH 3/9] Smaller fixes --- Moose Development/Moose/Functional/Fox.lua | 2 +- Moose Development/Moose/Wrapper/Group.lua | 8 ++++++-- Moose Development/Moose/Wrapper/Positionable.lua | 9 ++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/Fox.lua b/Moose Development/Moose/Functional/Fox.lua index 5ca6d4e51..38d8eb585 100644 --- a/Moose Development/Moose/Functional/Fox.lua +++ b/Moose Development/Moose/Functional/Fox.lua @@ -1060,7 +1060,7 @@ function FOX:onafterMissileLaunch(From, Event, To, missile) -- Tracking info and init of last bomb position. local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s", missile.missileType, missile.missileName, tostring(missile.targetName), missile.shooterName) - self:I(FOX.lid..text) + self:T(FOX.lid..text) MESSAGE:New(text, 10):ToAllIf(self.Debug) -- Loop over players. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 3760a84fc..ef0561f84 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -3003,7 +3003,7 @@ end -- local callsign = mygroup:GetCustomCallSign(true,false,nil,function(groupname,playername) return string.match(playername,"([%a]+)$") end) -- function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations,CustomFunction,...) - --self:I("GetCustomCallSign") + self:T("GetCustomCallSign") local callsign = "Ghost 1" if self:IsAlive() then @@ -3016,8 +3016,12 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations,C local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9 local callnumberminor = string.char(string.byte(callnumber,2)) -- 1 local personalized = false - local playername = IsPlayer == true and self:GetPlayerName() or shortcallsign + --local playername = IsPlayer == true and self:GetPlayerName() or shortcallsign + local playername = shortcallsign + if IsPlayer then playername = self:GetPlayerName() end + + self:T2("GetCustomCallSign outcome = "..playername) if CustomFunction and IsPlayer then local arguments = arg or {} local callsign = CustomFunction(groupname,playername,unpack(arguments)) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 9d8c77873..df5fdcb37 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -110,14 +110,17 @@ function POSITIONABLE:Destroy( GenerateEvent ) if GenerateEvent and GenerateEvent == true then if self:IsAir() then + --self:ScheduleOnce(1,self.CreateEventCrash,self,timer.getTime(),DCSObject) self:CreateEventCrash( timer.getTime(), DCSObject ) else + --self:ScheduleOnce(1,self.CreateEventDead,self,timer.getTime(),DCSObject) self:CreateEventDead( timer.getTime(), DCSObject ) end elseif GenerateEvent == false then -- Do nothing! else self:CreateEventRemoveUnit( timer.getTime(), DCSObject ) + --self:ScheduleOnce(1,self.CreateEventRemoveUnit,self,timer.getTime(),DCSObject) end USERFLAG:New( UnitGroupName ):Set( 100 ) @@ -142,7 +145,11 @@ function POSITIONABLE:GetPosition() self:F2( self.PositionableName ) local DCSPositionable = self:GetDCSObject() - + + if self:IsInstanceOf("GROUP") then + DCSPositionable = self:GetFirstUnitAlive():GetDCSObject() + end + if DCSPositionable then local PositionablePosition = DCSPositionable:getPosition() self:T3( PositionablePosition ) From bd074728fe5e66f96dde3a56d4bdaa56e029d2ab Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 18 Jan 2025 22:34:21 +0100 Subject: [PATCH 4/9] Update Artillery.lua - Update Arty DB --- .../Moose/Functional/Artillery.lua | 122 ++++++++++++------ 1 file changed, 80 insertions(+), 42 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 5b283ee20..afe21a14c 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -534,7 +534,7 @@ ARTY={ ClassName="ARTY", lid=nil, - Debug=false, + Debug=true, targets={}, moves={}, currentTarget=nil, @@ -619,63 +619,101 @@ ARTY.WeaponType={ } --- Database of common artillery unit properties. +-- @type ARTY.dbitem +-- @field #string displayname Name displayed in ME. +-- @field #number minrange Minimum firing range in meters. +-- @field #number maxrange Maximum firing range in meters. +-- @field #number reloadtime Reload time in seconds. + +--- Database of common artillery unit properties. +-- Table key is the "type name" and table value is and `ARTY.dbitem`. -- @type ARTY.db ARTY.db={ - ["2B11 mortar"] = { -- type "2B11 mortar" - minrange = 500, -- correct? - maxrange = 7000, -- 7 km - reloadtime = 30, -- 30 sec + ["2B11 mortar"] = { + displayname = "Mortar 2B11 120mm", + minrange = 500, -- correct? + maxrange = 7000, -- 7 km + reloadtime = 30, -- 30 sec }, - ["SPH 2S1 Gvozdika"] = { -- type "SAU Gvozdika" - minrange = 300, -- correct? - maxrange = 15000, -- 15 km - reloadtime = nil, -- unknown + ["SAU Gvozdika"] = { + displayname = "SPH 2S1 Gvozdika 122mm", + minrange = 300, -- correct? + maxrange = 15000, -- 15 km + reloadtime = nil, -- unknown }, - ["SPH 2S19 Msta"] = { --type "SAU Msta", alias "2S19 Msta" - minrange = 300, -- correct? - maxrange = 23500, -- 23.5 km - reloadtime = nil, -- unknown + ["SAU Msta"] = { + displayname = "SPH 2S19 Msta 152mm", + minrange = 300, -- correct? + maxrange = 23500, -- 23.5 km + reloadtime = nil, -- unknown }, - ["SPH 2S3 Akatsia"] = { -- type "SAU Akatsia", alias "2S3 Akatsia" - minrange = 300, -- correct? - maxrange = 17000, -- 17 km - reloadtime = nil, -- unknown + ["SAU Akatsia"] = { + displayname = "SPH 2S3 Akatsia 152mm", + minrange = 300, + maxrange = 17000, + reloadtime = nil, }, - ["SPH 2S9 Nona"] = { --type "SAU 2-C9" - minrange = 500, -- correct? - maxrange = 7000, -- 7 km - reloadtime = nil, -- unknown + ["SAU 2-C9"] = { + displayname = "SPM 2S9 Nona 120mm M", + minrange = 500, + maxrange = 7000, + reloadtime = nil, }, - ["SPH M109 Paladin"] = { -- type "M-109", alias "M109" - minrange = 300, -- correct? - maxrange = 22000, -- 22 km - reloadtime = nil, -- unknown + ["M-109"] = { + displayname = "SPH M109 Paladin 155mm", + minrange = 300, + maxrange = 22000, + reloadtime = nil, }, - ["SpGH Dana"] = { -- type "SpGH_Dana" - minrange = 300, -- correct? - maxrange = 18700, -- 18.7 km - reloadtime = nil, -- unknown + ["SpGH_Dana"] = { + displayname = "SPH Dana vz77 152mm", + minrange = 300, + maxrange = 18700, + reloadtime = nil, }, ["MLRS BM-21 Grad"] = { --type "Grad-URAL", alias "MLRS BM-21 Grad" - minrange = 5000, -- 5 km - maxrange = 19000, -- 19 km - reloadtime = 420, -- 7 min + minrange = 5000, + maxrange = 19000, + reloadtime = 420, }, ["MLRS 9K57 Uragan BM-27"] = { -- type "Uragan_BM-27" - minrange = 11500, -- 11.5 km - maxrange = 35800, -- 35.8 km - reloadtime = 840, -- 14 min + minrange = 11500, + maxrange = 35800, + reloadtime = 840, }, ["MLRS 9A52 Smerch"] = { -- type "Smerch" - minrange = 20000, -- 20 km - maxrange = 70000, -- 70 km - reloadtime = 2160, -- 36 min + minrange = 20000, + maxrange = 70000, + reloadtime = 2160, }, ["MLRS M270"] = { --type "MRLS", alias "M270 MRLS" - minrange = 10000, -- 10 km - maxrange = 32000, -- 32 km - reloadtime = 540, -- 9 min + minrange = 10000, + maxrange = 32000, + reloadtime = 540, }, + ["M12_GMC"] = { + displayname = "SPH M12 GMC 155mm", + minrange = 300, + maxrange = 18200, + reloadtime = nil, + }, + ["Wespe124"] = { + displayname = "SPH Sd.Kfz.124 Wespe 105mm", + minrange = 300, + maxrange = 7000, + reloadtime = nil, + }, + ["T155_Firtina"] = { + displayname = "SPH T155 Firtina 155mm", + minrange = 300, + maxrange = 41000, + reloadtime = nil, + }, + ["LeFH_18-40-105"] = { + minrange = 500, -- 500 m(?) + maxrange = 10500, -- 32 km + reloadtime = 540, -- 9 min + }, } --- Target. @@ -1923,7 +1961,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) end -- Check if we have and arty type that is in the DB. - local _dbproperties=self:_CheckDB(self.DisplayName) + local _dbproperties=self:_CheckDB(self.Type) self:T({dbproperties=_dbproperties}) if _dbproperties~=nil then for property,value in pairs(_dbproperties) do From 92a05ca74a44b56c7c7c2cc1ea5c72821be13400 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 19 Jan 2025 18:22:21 +0100 Subject: [PATCH 5/9] #CTLD - If troops or vehicles have a stock set, you can only inject as many as there are in stock. Specifically, when using persistence, the load function will restrict to inject more objects than are in stock, each inject draws on the stock. --- Moose Development/Moose/Ops/CTLD.lua | 46 ++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 26295b17a..bfb47f706 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1352,7 +1352,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.1.23" +CTLD.version="1.1.24" --- Instantiate a new CTLD. -- @param #CTLD self @@ -5512,14 +5512,18 @@ end local match = false local cgotbl = self.Cargo_Troops local name = cargo:GetName() + local CargoObject + local CargoName for _,_cgo in pairs (cgotbl) do local cname = _cgo:GetName() if name == cname then match = true + CargoObject = _cgo + CargoName = cname break end end - return match + return match, CargoObject, CargoName end local function Cruncher(group,typename,anzahl) @@ -5565,11 +5569,24 @@ end end end - if not IsTroopsMatch(cargo) then + local match,CargoObject,CargoName = IsTroopsMatch(cargo) + + if not match then self.CargoCounter = self.CargoCounter + 1 cargo.ID = self.CargoCounter cargo.Stock = 1 - table.insert(self.Cargo_Troops,cargo) + table.insert(self.Cargo_Crates,cargo) + end + + if match and CargoObject then + local stock = CargoObject:GetStock() + if stock ~= -1 and stock ~= nil and stock == 0 then + -- stock empty + self:T(self.lid.."Stock of "..CargoName.." is empty. Cannot inject.") + return + else + CargoObject:RemoveStock(1) + end end local type = cargo:GetType() -- #CTLD_CARGO.Enum @@ -5637,14 +5654,18 @@ end local match = false local cgotbl = self.Cargo_Crates local name = cargo:GetName() + local CargoObject + local CargoName for _,_cgo in pairs (cgotbl) do local cname = _cgo:GetName() if name == cname then match = true + CargoObject = _cgo + CargoName = cname break end end - return match + return match,CargoObject,CargoName end local function Cruncher(group,typename,anzahl) @@ -5690,13 +5711,26 @@ end end end - if not IsVehicMatch(cargo) then + local match,CargoObject,CargoName = IsVehicMatch(cargo) + + if not match then self.CargoCounter = self.CargoCounter + 1 cargo.ID = self.CargoCounter cargo.Stock = 1 table.insert(self.Cargo_Crates,cargo) end + if match and CargoObject then + local stock = CargoObject:GetStock() + if stock ~= -1 and stock ~= nil and stock == 0 then + -- stock empty + self:T(self.lid.."Stock of "..CargoName.." is empty. Cannot inject.") + return + else + CargoObject:RemoveStock(1) + end + end + local type = cargo:GetType() -- #CTLD_CARGO.Enum if (type == CTLD_CARGO.Enum.VEHICLE or type == CTLD_CARGO.Enum.FOB) then -- unload From 5957124e9e86e20529fddd23f5349b04c6d79564 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 19 Jan 2025 19:17:59 +0100 Subject: [PATCH 6/9] ARTY v1.3.3 - Added missing/new arty units to DB (min/max firing range) - Adjusted immobile speed check because some DCS units report a speed of 1 m/s --- .../Moose/Functional/Artillery.lua | 151 ++++++++++++------ 1 file changed, 99 insertions(+), 52 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index afe21a14c..ff352b555 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -534,7 +534,7 @@ ARTY={ ClassName="ARTY", lid=nil, - Debug=true, + Debug=false, targets={}, moves={}, currentTarget=nil, @@ -629,23 +629,95 @@ ARTY.WeaponType={ -- Table key is the "type name" and table value is and `ARTY.dbitem`. -- @type ARTY.db ARTY.db={ + ["LeFH_18-40-105"] = { + displayname = "FH LeFH-18 105mm", -- name displayed in the ME + minrange = 500, -- min range (green circle) in meters + maxrange = 10500, -- max range (red circle) in meters + reloadtime = nil, -- reload time in seconds + }, + ["M2A1-105"] = { + displayname = "FH M2A1 105mm", + minrange = 500, + maxrange = 11500, + reloadtime = nil, + }, + ["Pak40"] = { + displayname = "FH Pak 40 75mm", + minrange = 500, + maxrange = 3000, + reloadtime = nil, + }, + ["L118_Unit"] = { + displayname = "L118 Light Artillery Gun", + minrange = 500, + maxrange = 17500, + reloadtime = nil, + }, + ["Smerch"] = { + displayname = "MLRS 9A52 Smerch CM 300mm", + minrange = 20000, + maxrange = 70000, + reloadtime = 2160, + }, + ["Smerch_HE"] = { + displayname = "MLRS 9A52 Smerch HE 300mm", + minrange = 20000, + maxrange = 70000, + reloadtime = 2160, + }, + ["Uragan_BM-27"] = { + displayname = "MLRS 9K57 Uragan BM-27 220mm", + minrange = 11500, + maxrange = 35800, + reloadtime = 840, + }, + ["Grad-URAL"] = { + displayname = "MLRS BM-21 Grad 122mm", + minrange = 5000, + maxrange = 19000, + reloadtime = 420, + }, + ["HL_B8M1"] = { + displayname = "MLRS HL with B8M1 80mm", + minrange = 500, + maxrange = 5000, + reloadtime = nil, + }, + ["tt_B8M1"] = { + displayname = "MLRS LC with B8M1 80mm", + minrange = 500, + maxrange = 5000, + reloadtime = nil, + }, + ["MLRS"] = { + displayname = "MLRS M270 227mm", + minrange = 10000, + maxrange = 32000, + reloadtime = 540, + }, ["2B11 mortar"] = { displayname = "Mortar 2B11 120mm", - minrange = 500, -- correct? - maxrange = 7000, -- 7 km - reloadtime = 30, -- 30 sec + minrange = 500, + maxrange = 7000, + reloadtime = 30, + }, + ["PLZ05"] = { + displayname = "PLZ-05", + minrange = 500, + maxrange = 23500, + reloadtime = nil, }, ["SAU Gvozdika"] = { displayname = "SPH 2S1 Gvozdika 122mm", - minrange = 300, -- correct? - maxrange = 15000, -- 15 km - reloadtime = nil, -- unknown + minrange = 300, + maxrange = 15000, + reloadtime = nil, }, ["SAU Msta"] = { displayname = "SPH 2S19 Msta 152mm", - minrange = 300, -- correct? - maxrange = 23500, -- 23.5 km - reloadtime = nil, -- unknown + minrange = 300, + maxrange = 23500, + reloadtime = nil, }, ["SAU Akatsia"] = { displayname = "SPH 2S3 Akatsia 152mm", @@ -653,44 +725,18 @@ ARTY.db={ maxrange = 17000, reloadtime = nil, }, - ["SAU 2-C9"] = { - displayname = "SPM 2S9 Nona 120mm M", - minrange = 500, - maxrange = 7000, - reloadtime = nil, - }, - ["M-109"] = { - displayname = "SPH M109 Paladin 155mm", - minrange = 300, - maxrange = 22000, - reloadtime = nil, - }, ["SpGH_Dana"] = { displayname = "SPH Dana vz77 152mm", minrange = 300, maxrange = 18700, reloadtime = nil, }, - ["MLRS BM-21 Grad"] = { --type "Grad-URAL", alias "MLRS BM-21 Grad" - minrange = 5000, - maxrange = 19000, - reloadtime = 420, - }, - ["MLRS 9K57 Uragan BM-27"] = { -- type "Uragan_BM-27" - minrange = 11500, - maxrange = 35800, - reloadtime = 840, - }, - ["MLRS 9A52 Smerch"] = { -- type "Smerch" - minrange = 20000, - maxrange = 70000, - reloadtime = 2160, - }, - ["MLRS M270"] = { --type "MRLS", alias "M270 MRLS" - minrange = 10000, - maxrange = 32000, - reloadtime = 540, - }, + ["M-109"] = { + displayname = "SPH M109 Paladin 155mm", + minrange = 300, + maxrange = 22000, + reloadtime = nil, + }, ["M12_GMC"] = { displayname = "SPH M12 GMC 155mm", minrange = 300, @@ -708,11 +754,12 @@ ARTY.db={ minrange = 300, maxrange = 41000, reloadtime = nil, - }, - ["LeFH_18-40-105"] = { - minrange = 500, -- 500 m(?) - maxrange = 10500, -- 32 km - reloadtime = 540, -- 9 min + }, + ["SAU 2-C9"] = { + displayname = "SPM 2S9 Nona 120mm M", + minrange = 500, + maxrange = 7000, + reloadtime = nil, }, } @@ -733,7 +780,7 @@ ARTY.db={ --- Arty script version. -- @field #string version -ARTY.version="1.3.2" +ARTY.version="1.3.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -835,8 +882,8 @@ function ARTY:New(group, alias) -- Maximum speed in km/h. self.SpeedMax=group:GetSpeedMax() - -- Group is mobile or not (e.g. mortars). - if self.SpeedMax>1 then + -- Group is mobile or not (e.g. mortars). Some immobile units have a speed of 1 m/s = 3.6 km/h. So we check this number. + if self.SpeedMax>3.6 then self.ismobile=true else self.ismobile=false @@ -2007,8 +2054,8 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Type = %s\n", self.Type) text=text..string.format("Display Name = %s\n", self.DisplayName) text=text..string.format("Number of units = %d\n", self.IniGroupStrength) - text=text..string.format("Speed max = %d km/h\n", self.SpeedMax) - text=text..string.format("Speed default = %d km/h\n", self.Speed) + text=text..string.format("Speed max = %.1f km/h\n", self.SpeedMax) + text=text..string.format("Speed default = %.1f km/h\n", self.Speed) text=text..string.format("Is mobile = %s\n", tostring(self.ismobile)) text=text..string.format("Is cargo = %s\n", tostring(self.iscargo)) text=text..string.format("Min range = %.1f km\n", self.minrange/1000) From b03978cc3d0c636a6c25f1722ce58a75306bf52b Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:43:57 +0100 Subject: [PATCH 7/9] Update Zone.lua Correct radius sphere search in ZONE_RADIUS:SearchZone() --- 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 7750851cb..ceca5fb02 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1415,7 +1415,7 @@ function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories ) id = world.VolumeType.SPHERE, params = { point = ZoneCoord:GetVec3(), - radius = ZoneRadius / 2, + radius = ZoneRadius, } } From 068a1ab99c2a41ab539299a4d326db831ce94271 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 24 Jan 2025 10:29:47 +0100 Subject: [PATCH 8/9] #CTLD - Added `OnAfterLoaded` which gives you access to the rebuild troops and vehicles in a table of loaded groups, each entry is a table with three values: Group, TimeStamp and CargoType --- Moose Development/Moose/Ops/CTLD.lua | 64 +++++++++++++++++++++------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index bfb47f706..c73600e44 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -980,7 +980,7 @@ do -- -- ## 3.5 OnAfterCratesDropped -- --- This function is called when a player has deployed crates to a DROP zone: +-- This function is called when a player has deployed crates: -- -- function my_ctld:OnAfterCratesDropped(From, Event, To, Group, Unit, Cargotable) -- ... your code here ... @@ -1242,6 +1242,8 @@ CTLD = { TroopUnloadDistHoverHook = 5, TroopUnloadDistHover = 1.5, UserSetGroup = nil, + LoadedGroupsTable = {}, + keeploadtable = true, } ------------------------------ @@ -1352,7 +1354,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.1.24" +CTLD.version="1.1.25" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1419,7 +1421,8 @@ function CTLD:New(Coalition, Prefixes, Alias) self:AddTransition("*", "CratesRepaired", "*") -- CTLD repair event. self:AddTransition("*", "CratesBuildStarted", "*") -- CTLD build event. self:AddTransition("*", "CratesRepairStarted", "*") -- CTLD repair event. - self:AddTransition("*", "Load", "*") -- CTLD load event. + self:AddTransition("*", "Load", "*") -- CTLD load event. + self:AddTransition("*", "Loaded", "*") -- CTLD load event. self:AddTransition("*", "Save", "*") -- CTLD save event. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. @@ -1520,6 +1523,8 @@ function CTLD:New(Coalition, Prefixes, Alias) self.filepath = nil self.saveinterval = 600 self.eventoninject = true + self.keeploadtable = true + self.LoadedGroupsTable = {} -- sub categories self.usesubcats = false @@ -1832,6 +1837,14 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param #string path (Optional) Path where the file is located. Default is the DCS root installation folder or your "Saved Games\\DCS" folder if the lfs module is desanitized. -- @param #string filename (Optional) File name for loading. Default is "CTLD__Persist.csv". + --- FSM Function OnAfterLoaded. + -- @function [parent=#CTLD] OnAfterLoaded + -- @param #CTLD self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table LoadedGroups Table of loaded groups, each entry is a table with three values: Group, TimeStamp and CargoType. + --- FSM Function OnAfterSave. -- @function [parent=#CTLD] OnAfterSave -- @param #CTLD self @@ -3510,10 +3523,9 @@ function CTLD:_UnloadTroops(Group, Unit) local rad = 2.5+(tempcount*2) local Positions = self:_GetUnitPositions(randomcoord,rad,heading,_template) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) - --:InitRandomizeUnits(true,20,2) - --:InitHeading(heading) :InitDelayOff() :InitSetUnitAbsolutePositions(Positions) + :OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end) :SpawnFromVec2(randomcoord:GetVec2()) self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type) end -- template loop @@ -3934,10 +3946,12 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) --:InitRandomizeUnits(true,20,2) :InitDelayOff() + :OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end) :SpawnFromVec2(randomcoord) else -- don't random position of e.g. SAM units build as FOB self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) :InitDelayOff() + :OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end) :SpawnFromVec2(randomcoord) end if Repair then @@ -5496,6 +5510,7 @@ end -- @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. + -- @param #number TimeStamp (Optional) Timestamp used internally on re-loading from disk. -- @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 @@ -5504,7 +5519,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,Structure) + function CTLD:InjectTroops(Zone,Cargo,Surfacetypes,PreciseLocation,Structure,TimeStamp) self:T(self.lid.." InjectTroops") local cargo = Cargo -- #CTLD_CARGO @@ -5607,6 +5622,7 @@ end self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) :InitRandomizeUnits(randompositions,20,2) :InitDelayOff() + :OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp) :SpawnFromVec2(randomcoord) if self.movetroopstowpzone and type ~= CTLD_CARGO.Enum.ENGINEERS then self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) @@ -5624,6 +5640,11 @@ end BASE:ScheduleOnce(0.5,PostSpawn,{self.DroppedTroops[self.TroopCounter],Structure}) end + if self.keeploadtables and TimeStamp then + local cargotype = cargo.CargoType + table.insert(self.LoadedGroupsTable,{Group=self.DroppedTroops[self.TroopCounter], TimeStamp=TimeStamp, CargoType=cargotype}) + end + if self.eventoninject then self:__TroopsDeployed(1,nil,nil,self.DroppedTroops[self.TroopCounter],type) end @@ -5638,6 +5659,7 @@ end -- @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. + -- @param #number TimeStamp (Optional) Timestamp used internally on re-loading from disk. -- @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 @@ -5646,7 +5668,7 @@ end -- local dropzone = ZONE:New("InjectZone") -- Core.Zone#ZONE -- -- and go: -- my_ctld:InjectVehicles(dropzone,InjectVehicleType) - function CTLD:InjectVehicles(Zone,Cargo,Surfacetypes,PreciseLocation,Structure) + function CTLD:InjectVehicles(Zone,Cargo,Surfacetypes,PreciseLocation,Structure,TimeStamp) self:T(self.lid.." InjectVehicles") local cargo = Cargo -- #CTLD_CARGO @@ -5752,10 +5774,12 @@ end self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) :InitRandomizeUnits(true,20,2) :InitDelayOff() + :OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp) :SpawnFromVec2(randomcoord) else -- don't random position of e.g. SAM units build as FOB self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) :InitDelayOff() + :OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp) :SpawnFromVec2(randomcoord) end @@ -5763,6 +5787,11 @@ end BASE:ScheduleOnce(0.5,PostSpawn,{self.DroppedTroops[self.TroopCounter],Structure}) end + if self.keeploadtables and TimeStamp then + local cargotype = cargo.CargoType + table.insert(self.LoadedGroupsTable,{Group=self.DroppedTroops[self.TroopCounter], TimeStamp=TimeStamp, CargoType=cargotype}) + end + if self.eventoninject then self:__CratesBuild(1,nil,nil,self.DroppedTroops[self.TroopCounter]) end @@ -6180,7 +6209,7 @@ end --local data = "LoadedData = {\n" - local data = "Group,x,y,z,CargoName,CargoTemplates,CargoType,CratesNeeded,CrateMass,Structure,StaticCategory,StaticType,StaticShape\n" + local data = "Group,x,y,z,CargoName,CargoTemplates,CargoType,CratesNeeded,CrateMass,Structure,StaticCategory,StaticType,StaticShape,SpawnTime\n" local n = 0 for _,_grp in pairs(grouptable) do local group = _grp -- Wrapper.Group#GROUP @@ -6213,6 +6242,7 @@ end for typen,anzahl in pairs (structure) do strucdata = strucdata .. typen .. "=="..anzahl..";" end + local spawntime = group.spawntime or timer.getTime()+n if type(cgotemp) == "table" then local templates = "{" @@ -6224,8 +6254,8 @@ end end local location = group:GetVec3() - local txt = string.format("%s,%d,%d,%d,%s,%s,%s,%d,%d,%s,%s,%s,%s\n" - ,template,location.x,location.y,location.z,cgoname,cgotemp,cgotype,cgoneed,cgomass,strucdata,scat,stype,sshape or "none") + local txt = string.format("%s,%d,%d,%d,%s,%s,%s,%d,%d,%s,%s,%s,%s,%f\n" + ,template,location.x,location.y,location.z,cgoname,cgotemp,cgotype,cgoneed,cgomass,strucdata,scat,stype,sshape or "none",spawntime) data = data .. txt end end @@ -6378,10 +6408,10 @@ end -- remove header table.remove(loadeddata, 1) - + local n=0 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=Structure,11=StaticCategory,12=StaticType,13=StaticShape + -- 1=Group,2=x,3=y,4=z,5=CargoName,6=CargoTemplates,7=CargoType,8=CratesNeeded,9=CrateMass,10=Structure,11=StaticCategory,12=StaticType,13=StaticShape,14=Timestamp local groupname = dataset[1] local vec2 = {} vec2.x = tonumber(dataset[2]) @@ -6394,6 +6424,8 @@ end local StaticCategory = dataset[11] local StaticType = dataset[12] local StaticShape = dataset[13] + n=n+1 + local timestamp = tonumber(dataset[14]) or timer.getTime()+n if type(groupname) == "string" and groupname ~= "STATIC" then cargotemplates = string.gsub(cargotemplates,"{","") cargotemplates = string.gsub(cargotemplates,"}","") @@ -6408,10 +6440,10 @@ end 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) injectvehicle:SetStaticTypeAndShape(StaticCategory,StaticType,StaticShape) - self:InjectVehicles(dropzone,injectvehicle,self.surfacetypes,self.useprecisecoordloads,structure) + self:InjectVehicles(dropzone,injectvehicle,self.surfacetypes,self.useprecisecoordloads,structure,timestamp) 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,structure) + self:InjectTroops(dropzone,injecttroops,self.surfacetypes,self.useprecisecoordloads,structure,timestamp) end elseif (type(groupname) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR then local dropzone = ZONE_RADIUS:New("DropZone",vec2,20) @@ -6433,7 +6465,9 @@ end end end end - + if self.keeploadtable then -- keeploadtables + self:__Loaded(1,self.LoadedGroupsTable) + end return self end end -- end do From 48bc41873acdcdc02c1bd66fc1e2cde1477976ba Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 24 Jan 2025 12:21:44 +0100 Subject: [PATCH 9/9] #STORAGE - Added small helper to get Syria "H" Helipad warehouses via a zone --- Moose Development/Moose/Wrapper/Storage.lua | 31 ++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Storage.lua b/Moose Development/Moose/Wrapper/Storage.lua index 3acaa9d16..4b445613c 100644 --- a/Moose Development/Moose/Wrapper/Storage.lua +++ b/Moose Development/Moose/Wrapper/Storage.lua @@ -227,7 +227,7 @@ function STORAGE:New(AirbaseName) self.airbase=Airbase.getByName(AirbaseName) - if Airbase.getWarehouse then + if Airbase.getWarehouse and self.airbase then self.warehouse=self.airbase:getWarehouse() end @@ -840,6 +840,35 @@ function STORAGE:StopAutoSave() return self end +--- Try to find the #STORAGE object of one of the many "H"-Helipads in Syria. You need to put a (small, round) zone on top of it, because the name is not unique(!). +-- @param #STORAGE self +-- @param #string ZoneName The name of the zone where to find the helipad. +-- @return #STORAGE self or nil if not found. +function STORAGE:FindSyriaHHelipadWarehouse(ZoneName) + local findzone = ZONE:New(ZoneName) + local base = world.getAirbases() + for i = 1, #base do + local info = {} + --info.desc = Airbase.getDesc(base[i]) + info.callsign = Airbase.getCallsign(base[i]) + info.id = Airbase.getID(base[i]) + --info.cat = Airbase.getCategory(base[i]) + info.point = Airbase.getPoint(base[i]) + info.coordinate = COORDINATE:NewFromVec3(info.point) + info.DCSObject = base[i] + --if Airbase.getUnit(base[i]) then + --info.unitId = Airbase.getUnit(base[i]):getID() + --end + if info.callsign == "H" and findzone:IsCoordinateInZone(info.coordinate) then + info.warehouse = info.DCSObject:getWarehouse() + info.Storage = STORAGE:New(info.callsign..info.id) + info.Storage.airbase = info.DCSObject + info.Storage.warehouse = info.warehouse + return info.Storage + end + end +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Private Functions