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: diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 8a76a867d..405c5da76 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -833,6 +833,12 @@ do -- Warehouse -- The warehouse class gives control over warehouses that exist in airbase objects. These warehouses can limit the aircraft, munitions, and fuel available to coalition aircraft. -- @type Warehouse + + --- Get a warehouse by passing its name. + -- @function [parent=#Warehouse] getByName + -- @param #string Name Name of the warehouse. + -- @return #Warehouse The warehouse object. + --- Adds the passed amount of a given item to the warehouse. -- itemName is the typeName associated with the item: "weapons.missiles.AIM_54C_Mk47" -- A wsType table can also be used, however the last digit with wsTypes has been known to change. {4, 4, 7, 322} 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. diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index a7222956b..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 @@ -1278,7 +1279,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 +1342,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 +1364,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 +1378,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 +1407,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() @@ -2156,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 @@ -2417,6 +2421,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/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 6afe0d51e..3e03fc39d 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -489,11 +489,17 @@ OPSGROUP.CargoStatus={ --- Element cargo bay data. -- @type OPSGROUP.MyCargo -- @field #OPSGROUP group The cargo group. +-- @field #number storageType Type of storage. +-- @field #number storageAmount Amount of storage. +-- @field #number storageWeight Weight of storage item. -- @field #boolean reserved If `true`, the cargo bay space is reserved but cargo has not actually been loaded yet. --- Cargo group data. -- @type OPSGROUP.CargoGroup +-- @field #number uid Unique ID of this cargo data. +-- @field #string type Type of cargo: "OPSGROUP" or "STORAGE". -- @field #OPSGROUP opsgroup The cargo opsgroup. +-- @field Ops.OpsTransport#OPSTRANSPORT.Storage storage Storage data. -- @field #boolean delivered If `true`, group was delivered. -- @field #boolean disembarkActivation If `true`, group is activated. If `false`, group is late activated. -- @field Core.Zone#ZONE disembarkZone Zone where this group is disembarked to. @@ -2268,21 +2274,26 @@ end -- @param #OPSGROUP self -- @param #number Delay Delay in seconds. Default now. -- @param #number ExplosionPower (Optional) Explosion power in kg TNT. Default 100 kg. +-- @param #string ElementName Name of the element that should be destroyed. Default is all elements. -- @return #OPSGROUP self -function OPSGROUP:SelfDestruction(Delay, ExplosionPower) +function OPSGROUP:SelfDestruction(Delay, ExplosionPower, ElementName) if Delay and Delay>0 then - self:ScheduleOnce(Delay, OPSGROUP.SelfDestruction, self, 0, ExplosionPower) + self:ScheduleOnce(Delay, OPSGROUP.SelfDestruction, self, 0, ExplosionPower, ElementName) else -- Loop over all elements. for i,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element + + if ElementName==nil or ElementName==element.name then - local unit=element.unit - - if unit and unit:IsAlive() then - unit:Explode(ExplosionPower or 100) + local unit=element.unit + + if unit and unit:IsAlive() then + unit:Explode(ExplosionPower or 100) + end + end end end @@ -5452,10 +5463,10 @@ function OPSGROUP:onafterMissionExecute(From, Event, To, Mission) if self.isFlightgroup then if Mission.prohibitABExecute == true then self:SetProhibitAfterburner() - self:I("Set prohibit AB") + self:T(self.lid.."Set prohibit AB") elseif Mission.prohibitABExecute == false then self:SetAllowAfterburner() - self:T2("Set allow AB") + self:T2(self.lid.."Set allow AB") end end @@ -7446,35 +7457,54 @@ function OPSGROUP:onafterElementDead(From, Event, To, Element) -- Clear cargo bay of element. for i=#Element.cargoBay,1,-1 do - local cargo=Element.cargoBay[i] --#OPSGROUP.MyCargo - - -- Remove from cargo bay. - self:_DelCargobay(cargo.group) - - if cargo.group and not (cargo.group:IsDead() or cargo.group:IsStopped()) then - - -- Remove my carrier - cargo.group:_RemoveMyCarrier() - - if cargo.reserved then - - -- This group was not loaded yet ==> Not cargo any more. - cargo.group:_NewCargoStatus(OPSGROUP.CargoStatus.NOTCARGO) - - else - - -- Carrier dead ==> cargo dead. - for _,cargoelement in pairs(cargo.group.elements) do - - -- Debug info. - self:T2(self.lid.."Cargo element dead "..cargoelement.name) - - -- Trigger dead event. - cargo.group:ElementDead(cargoelement) + local mycargo=Element.cargoBay[i] --#OPSGROUP.MyCargo + + if mycargo.group then + -- Remove from cargo bay. + self:_DelCargobay(mycargo.group) + + if mycargo.group and not (mycargo.group:IsDead() or mycargo.group:IsStopped()) then + + -- Remove my carrier + mycargo.group:_RemoveMyCarrier() + + if mycargo.reserved then + + -- This group was not loaded yet ==> Not cargo any more. + mycargo.group:_NewCargoStatus(OPSGROUP.CargoStatus.NOTCARGO) + + else + + -- Carrier dead ==> cargo dead. + for _,cargoelement in pairs(mycargo.group.elements) do + + -- Debug info. + self:T2(self.lid.."Cargo element dead "..cargoelement.name) + + -- Trigger dead event. + mycargo.group:ElementDead(cargoelement) + + end end + + end + + else + + -- Add cargo to lost. + if self.cargoTZC then + for _,_cargo in pairs(self.cargoTZC.Cargos) do + local cargo=_cargo --#OPSGROUP.CargoGroup + if cargo.uid==mycargo.cargoUID then + cargo.storage.cargoLost=cargo.storage.cargoLost+mycargo.storageAmount + end + end end + -- Remove cargo from cargo bay. + self:_DelCargobayElement(Element, mycargo) + end end @@ -7972,7 +8002,12 @@ function OPSGROUP:_CheckCargoTransport() local element=_element --#OPSGROUP.Element for _,_cargo in pairs(element.cargoBay) do local cargo=_cargo --#OPSGROUP.MyCargo - text=text..string.format("\n- %s in carrier %s, reserved=%s", tostring(cargo.group:GetName()), tostring(element.name), tostring(cargo.reserved)) + if cargo.group then + text=text..string.format("\n- %s in carrier %s, reserved=%s", tostring(cargo.group:GetName()), tostring(element.name), tostring(cargo.reserved)) + else + text=text..string.format("\n- storage %s=%d kg in carrier %s [UID=%s]", + tostring(cargo.storageType), tostring(cargo.storageAmount*cargo.storageWeight), tostring(element.name), tostring(cargo.cargoUID)) + end end end if text=="" then @@ -7993,13 +8028,17 @@ function OPSGROUP:_CheckCargoTransport() text=text..string.format("\n[%d] UID=%d Status=%s: %s --> %s", i, transport.uid, transport:GetState(), pickupname, deployname) for j,_cargo in pairs(transport:GetCargos()) do local cargo=_cargo --#OPSGROUP.CargoGroup - local state=cargo.opsgroup:GetState() - local status=cargo.opsgroup.cargoStatus - local name=cargo.opsgroup.groupname - local carriergroup, carrierelement, reserved=cargo.opsgroup:_GetMyCarrier() - local carrierGroupname=carriergroup and carriergroup.groupname or "none" - local carrierElementname=carrierelement and carrierelement.name or "none" - text=text..string.format("\n (%d) %s [%s]: %s, carrier=%s(%s), delivered=%s", j, name, state, status, carrierGroupname, carrierElementname, tostring(cargo.delivered)) + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + local state=cargo.opsgroup:GetState() + local status=cargo.opsgroup.cargoStatus + local name=cargo.opsgroup.groupname + local carriergroup, carrierelement, reserved=cargo.opsgroup:_GetMyCarrier() + local carrierGroupname=carriergroup and carriergroup.groupname or "none" + local carrierElementname=carrierelement and carrierelement.name or "none" + text=text..string.format("\n (%d) %s [%s]: %s, carrier=%s(%s), delivered=%s", j, name, state, status, carrierGroupname, carrierElementname, tostring(cargo.delivered)) + else + --TODO: STORAGE + end end end if text~="" then @@ -8074,42 +8113,51 @@ function OPSGROUP:_CheckCargoTransport() -- Current pickup time. local tloading=Time-self.Tloading - --TODO: Check max loading time. If exceeded ==> abort transport. + --TODO: Check max loading time. If exceeded ==> abort transport. Time might depend on required cargos, because we need to give them time to arrive. -- Debug info. - self:T(self.lid..string.format("Loading at %s [TZC UID=%d] for %s sec...", self.cargoTZC.PickupZone and self.cargoTZC.PickupZone:GetName() or "unknown", self.cargoTZC.uid, tloading)) + self:T(self.lid..string.format("Loading at %s [TZC UID=%d] for %.1f sec...", self.cargoTZC.PickupZone and self.cargoTZC.PickupZone:GetName() or "unknown", self.cargoTZC.uid, tloading)) local boarding=false local gotcargo=false for _,_cargo in pairs(self.cargoTZC.Cargos) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup + + if cargo.type==OPSTRANSPORT.CargoType.OPSTRANPORT then - -- Check if anyone is still boarding. - if cargo.opsgroup:IsBoarding(self.groupname) then - boarding=true - end - - -- Check if we have any cargo to transport. - if cargo.opsgroup:IsLoaded(self.groupname) then - gotcargo=true + -- Check if anyone is still boarding. + if cargo.opsgroup and cargo.opsgroup:IsBoarding(self.groupname) then + boarding=true + end + + -- Check if we have any cargo to transport. + if cargo.opsgroup and cargo.opsgroup:IsLoaded(self.groupname) then + gotcargo=true + end + + else + + -- Get cargo if it is in the cargo bay of any carrier element. + local mycargo=self:_GetMyCargoBayFromUID(cargo.uid) + + if mycargo and mycargo.storageAmount>0 then + gotcargo=true + end + end end -- Boarding finished ==> Transport cargo. if gotcargo and self.cargoTransport:_CheckRequiredCargos(self.cargoTZC, self) and not boarding then - self:T(self.lid.."Boarding finished ==> Loaded") + self:T(self.lid.."Boarding/loading finished ==> Loaded") + self.Tloading=nil self:LoadingDone() else -- No cargo and no one is boarding ==> check again if we can make anyone board. self:Loading() end - -- No cargo and no one is boarding ==> check again if we can make anyone board. - if not gotcargo and not boarding then - --self:Loading() - end - elseif self:IsTransporting() then -- Set time stamp. @@ -8135,13 +8183,30 @@ function OPSGROUP:_CheckCargoTransport() local delivered=true for _,_cargo in pairs(self.cargoTZC.Cargos) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup + + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then - local carrierGroup=cargo.opsgroup:_GetMyCarrierGroup() - - -- Check that this group is - if (carrierGroup and carrierGroup:GetName()==self:GetName()) and not cargo.delivered then - delivered=false - break + local carrierGroup=cargo.opsgroup:_GetMyCarrierGroup() + + -- Check that this group is + if (carrierGroup and carrierGroup:GetName()==self:GetName()) and not cargo.delivered then + delivered=false + break + end + + else + --- + -- STORAGE + --- + + -- Get cargo if it is in the cargo bay of any carrier element. + local mycargo=self:_GetMyCargoBayFromUID(cargo.uid) + + if mycargo and not cargo.delivered then + delivered=false + break + end + end end @@ -8165,14 +8230,18 @@ function OPSGROUP:_CheckCargoTransport() local text=string.format("Carrier [%s]: %s --> %s", self.carrierStatus, pickupname, deployname) for _,_cargo in pairs(self.cargoTransport:GetCargos(self.cargoTZC)) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - local name=cargo.opsgroup:GetName() - local gstatus=cargo.opsgroup:GetState() - local cstatus=cargo.opsgroup.cargoStatus - local weight=cargo.opsgroup:GetWeightTotal() - local carriergroup, carrierelement, reserved=cargo.opsgroup:_GetMyCarrier() - local carrierGroupname=carriergroup and carriergroup.groupname or "none" - local carrierElementname=carrierelement and carrierelement.name or "none" - text=text..string.format("\n- %s (%.1f kg) [%s]: %s, carrier=%s (%s), delivered=%s", name, weight, gstatus, cstatus, carrierElementname, carrierGroupname, tostring(cargo.delivered)) + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + local name=cargo.opsgroup:GetName() + local gstatus=cargo.opsgroup:GetState() + local cstatus=cargo.opsgroup.cargoStatus + local weight=cargo.opsgroup:GetWeightTotal() + local carriergroup, carrierelement, reserved=cargo.opsgroup:_GetMyCarrier() + local carrierGroupname=carriergroup and carriergroup.groupname or "none" + local carrierElementname=carrierelement and carrierelement.name or "none" + text=text..string.format("\n- %s (%.1f kg) [%s]: %s, carrier=%s (%s), delivered=%s", name, weight, gstatus, cstatus, carrierElementname, carrierGroupname, tostring(cargo.delivered)) + else + --TODO: Storage + end end self:I(self.lid..text) end @@ -8216,6 +8285,8 @@ function OPSGROUP:_AddCargobay(CargoGroup, CarrierElement, Reserved) if cargo then cargo.reserved=Reserved else + + --cargo=self:_CreateMyCargo(CargoUID, CargoGroup) cargo={} --#OPSGROUP.MyCargo cargo.group=CargoGroup @@ -8244,6 +8315,95 @@ function OPSGROUP:_AddCargobay(CargoGroup, CarrierElement, Reserved) return self end +--- Add warehouse storage to cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #OPSGROUP.Element CarrierElement The element of the carrier. +-- @param #number CargoUID UID of the cargo data. +-- @param #string StorageType Storage type. +-- @param #number StorageAmount Storage amount. +-- @param #number StorageWeight Weight of a single storage item in kg. +function OPSGROUP:_AddCargobayStorage(CarrierElement, CargoUID, StorageType, StorageAmount, StorageWeight) + + local MyCargo=self:_CreateMyCargo(CargoUID, nil, StorageType, StorageAmount, StorageWeight) + + self:_AddMyCargoBay(MyCargo, CarrierElement) + + +end + +--- Add OPSGROUP to cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #number CargoUID UID of the cargo data. +-- @param #OPSGROUP OpsGroup Cargo group. +-- @param #string StorageType Storage type. +-- @param #number StorageAmount Storage amount. +-- @param #number StorageWeight Weight of a single storage item in kg. +-- @return #OPSGROUP.MyCargo My cargo object. +function OPSGROUP:_CreateMyCargo(CargoUID, OpsGroup, StorageType, StorageAmount, StorageWeight) + + local cargo={} --#OPSGROUP.MyCargo + + cargo.cargoUID=CargoUID + cargo.group=OpsGroup + cargo.storageType=StorageType + cargo.storageAmount=StorageAmount + cargo.storageWeight=StorageWeight + cargo.reserved=false + + return cargo +end + + +--- Add storage to cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #OPSGROUP.MyCargo MyCargo My cargo. +-- @param #OPSGROUP.Element CarrierElement The element of the carrier. +function OPSGROUP:_AddMyCargoBay(MyCargo, CarrierElement) + + table.insert(CarrierElement.cargoBay, MyCargo) + + if not MyCargo.reserved then + + -- Cargo weight. + local weight=0 + + if MyCargo.group then + weight=MyCargo.group:GetWeightTotal() + else + weight=MyCargo.storageAmount*MyCargo.storageWeight + end + + -- Add weight to carrier. + self:AddWeightCargo(CarrierElement.name, weight) + + end + + +end + +--- Get cargo bay data from a cargo data id. +-- @param #OPSGROUP self +-- @param #number uid Unique ID of cargo data. +-- @return #OPSGROUP.MyCargo Cargo My cargo. +-- @return #OPSGROUP.Element Element that has loaded the cargo. +function OPSGROUP:_GetMyCargoBayFromUID(uid) + + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + + for i,_mycargo in pairs(element.cargoBay) do + local mycargo=_mycargo --#OPSGROUP.MyCargo + + if mycargo.cargoUID and mycargo.cargoUID==uid then + return mycargo, element, i + end + end + end + + return nil, nil, nil +end + + --- Get all groups currently loaded as cargo. -- @param #OPSGROUP self -- @param #string CarrierName (Optional) Only return cargo groups loaded into a particular carrier unit. @@ -8291,6 +8451,51 @@ function OPSGROUP:_GetCargobay(CargoGroup) return nil, nil, nil end +--- Remove OPSGROUP from cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #OPSGROUP.Element Element Cargo group. +-- @param #number CargoUID Cargo UID. +-- @return #OPSGROUP.MyCargo MyCargo My cargo data. +function OPSGROUP:_GetCargobayElement(Element, CargoUID) + self:T3({Element=Element, CargoUID=CargoUID}) + + for i,_mycargo in pairs(Element.cargoBay) do + local mycargo=_mycargo --#OPSGROUP.MyCargo + + if mycargo.cargoUID and mycargo.cargoUID==CargoUID then + return mycargo + end + + end + + return nil +end + +--- Remove OPSGROUP from cargo bay of a carrier. +-- @param #OPSGROUP self +-- @param #OPSGROUP.Element Element Cargo group. +-- @param #OPSGROUP.MyCargo MyCargo My cargo data. +-- @return #boolean If `true`, cargo could be removed. +function OPSGROUP:_DelCargobayElement(Element, MyCargo) + + for i,_mycargo in pairs(Element.cargoBay) do + local mycargo=_mycargo --#OPSGROUP.MyCargo + + if mycargo.cargoUID and MyCargo.cargoUID and mycargo.cargoUID==MyCargo.cargoUID then + if MyCargo.group then + self:RedWeightCargo(Element.name, MyCargo.group:GetWeightTotal()) + else + self:RedWeightCargo(Element.name, MyCargo.storageAmount*MyCargo.storageWeight) + end + table.remove(Element.cargoBay, i) + return true + end + + end + + return false +end + --- Remove OPSGROUP from cargo bay of a carrier. -- @param #OPSGROUP self -- @param #OPSGROUP CargoGroup Cargo group. @@ -8385,16 +8590,18 @@ function OPSGROUP:_CheckDelivered(CargoTransport) for _,_cargo in pairs(CargoTransport:GetCargos()) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - if self:CanCargo(cargo.opsgroup) then + if self:CanCargo(cargo) then if cargo.delivered then -- This one is delivered. - elseif cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped() then + elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup==nil then + + elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then -- This one is dead. else done=false --Someone is not done! end - + end end @@ -8419,13 +8626,13 @@ function OPSGROUP:_CheckGoPickup(CargoTransport) for _,_cargo in pairs(CargoTransport:GetCargos()) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - if self:CanCargo(cargo.opsgroup) then + if self:CanCargo(cargo) then if cargo.delivered then -- This one is delivered. - elseif cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped() then + elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then -- This one is dead. - elseif cargo.opsgroup:IsLoaded(CargoTransport:_GetCarrierNames()) then + elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsLoaded(CargoTransport:_GetCarrierNames())) then -- This one is loaded into a(nother) carrier. else done=false --Someone is not done! @@ -8650,8 +8857,11 @@ function OPSGROUP:GetWeightCargo(UnitName, IncludeReserved) for _,_cargo in pairs(element.cargoBay) do local cargo=_cargo --#OPSGROUP.MyCargo if (not cargo.reserved) or (cargo.reserved==true and (IncludeReserved==true or IncludeReserved==nil)) then - local cargoweight=cargo.group:GetWeightTotal() - gewicht=gewicht+cargoweight + if cargo.group then + gewicht=gewicht+cargo.group:GetWeightTotal() + else + gewicht=gewicht+cargo.storageAmount*cargo.storageWeight + end --self:I(self.lid..string.format("unit=%s (reserved=%s): cargo=%s weight=%d, total weight=%d", tostring(UnitName), tostring(IncludeReserved), cargo.group:GetName(), cargoweight, weight)) end end @@ -8745,49 +8955,106 @@ function OPSGROUP:RedWeightCargo(UnitName, Weight) return self end +--- Get weight of warehouse storage to transport. +-- @param #OPSGROUP self +-- @param Ops.OpsTransport#OPSTRANSPORT.Storage Storage +-- @param #boolean Total Get total weight. Otherweise the amount left to deliver (total-loaded-lost-delivered). +-- @param #boolean Reserved Reduce weight that is reserved. +-- @param #boolean Amount Return amount not weight. +-- @return #number Weight of cargo in kg or amount in number of items, if `Amount=true`. +function OPSGROUP:_GetWeightStorage(Storage, Total, Reserved, Amount) + + local weight=Storage.cargoAmount + + if not Total then + weight=weight-Storage.cargoLost-Storage.cargoLoaded-Storage.cargoDelivered + end + + if Reserved then + weight=weight-Storage.cargoReserved + end + + if not Amount then + weight=weight*Storage.cargoWeight + end + + return weight +end + --- Check if the group can *in principle* be carrier of a cargo group. This checks the max cargo capacity of the group but *not* how much cargo is already loaded (if any). -- **Note** that the cargo group *cannot* be split into units, i.e. the largest cargo bay of any element of the group must be able to load the whole cargo group in one piece. -- @param #OPSGROUP self --- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier. +-- @param Ops.OpsGroup#OPSGROUP.CargoGroup Cargo Cargo data, which needs a carrier. -- @return #boolean If `true`, there is an element of the group that can load the whole cargo group. -function OPSGROUP:CanCargo(CargoGroup) +function OPSGROUP:CanCargo(Cargo) - if CargoGroup then + if Cargo then + + local weight=math.huge + + if Cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then - local weight=CargoGroup:GetWeightTotal() + local weight=Cargo.opsgroup:GetWeightTotal() + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + + -- Check that element is not dead and has + if element and element.status~=OPSGROUP.ElementStatus.DEAD and element.weightMaxCargo>=weight then + return true + end + end + + + else + + --- + -- STORAGE + --- + + -- Since storage cargo can be devided onto multiple carriers, we take the weight of a single cargo item (even 1 kg of fuel). + weight=Cargo.storage.cargoWeight + + end + + -- Calculate cargo bay space. + local bay=0 for _,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element -- Check that element is not dead and has - if element and element.status~=OPSGROUP.ElementStatus.DEAD and element.weightMaxCargo>=weight then - return true + if element and element.status~=OPSGROUP.ElementStatus.DEAD then + bay=bay+element.weightMaxCargo end - + end + + -- Check if cargo fits into cargo bay(s) of carrier group. + if bay>=weight then + return true + end + end return false end ---- Add weight to the internal cargo of an element of the group. +--- Find carrier for cargo by evaluating the free cargo bay storage. -- @param #OPSGROUP self --- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier. +-- @param #number Weight Weight of cargo in kg. -- @return #OPSGROUP.Element Carrier able to transport the cargo. -function OPSGROUP:FindCarrierForCargo(CargoGroup) - - local weight=CargoGroup:GetWeightTotal() +function OPSGROUP:FindCarrierForCargo(Weight) for _,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element local free=self:GetFreeCargobay(element.name) - if free>=weight then + if free>=Weight then return element else - self:T3(self.lid..string.format("%s: Weight %d>%d free cargo bay", element.name, weight, free)) + self:T3(self.lid..string.format("%s: Weight %d>%d free cargo bay", element.name, Weight, free)) end end @@ -9068,6 +9335,36 @@ function OPSGROUP:onafterPickup(From, Event, To) end +--- On after "Loading" event. +-- @param #OPSGROUP self +-- @param #table Cargos Table of cargos. +-- @return #table Table of sorted cargos. +function OPSGROUP:_SortCargo(Cargos) + + -- Sort results table wrt descending weight. + local function _sort(a, b) + local cargoA=a --Ops.OpsGroup#OPSGROUP.CargoGroup + local cargoB=b --Ops.OpsGroup#OPSGROUP.CargoGroup + local weightA=0 + local weightB=0 + if cargoA.opsgroup then + weightA=cargoA.opsgroup:GetWeightTotal() + else + weightA=self:_GetWeightStorage(cargoA.storage) + end + if cargoB.opsgroup then + weightB=cargoB.opsgroup:GetWeightTotal() + else + weightB=self:_GetWeightStorage(cargoB.storage) + end + return weightA>weightB + end + + table.sort(Cargos, _sort) + + return Cargos +end + --- On after "Loading" event. -- @param #OPSGROUP self @@ -9079,32 +9376,30 @@ function OPSGROUP:onafterLoading(From, Event, To) -- Set carrier status. self:_NewCarrierStatus(OPSGROUP.CarrierStatus.LOADING) - -- Loading time stamp. - self.Tloading=timer.getAbsTime() - -- Get valid cargos of the TZC. local cargos={} for _,_cargo in pairs(self.cargoTZC.Cargos) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup -- Check if this group can carry the cargo. - local canCargo=self:CanCargo(cargo.opsgroup) + local canCargo=self:CanCargo(cargo) -- Check if this group is currently acting as carrier. - local isCarrier=cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading() + local isCarrier=false + -- Check if cargo is not already cargo. - local isNotCargo=cargo.opsgroup:IsNotCargo(true) + local isNotCargo=true -- Check if cargo is holding or loaded - local isHolding=cargo.opsgroup:IsHolding() or cargo.opsgroup:IsLoaded() + local isHolding=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsHolding() or cargo.opsgroup:IsLoaded()) or true -- Check if cargo is in embark/pickup zone. -- Added InUtero here, if embark zone is moving (ship) and cargo has been spawned late activated and its position is not updated. Not sure if that breaks something else! - local inZone=cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) or cargo.opsgroup:IsInUtero() + local inZone=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) or cargo.opsgroup:IsInUtero()) or true -- Check if cargo is currently on a mission. - local isOnMission=cargo.opsgroup:IsOnMission() + local isOnMission=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsOnMission() or false -- Check if current mission is using this ops transport. if isOnMission then @@ -9114,39 +9409,114 @@ function OPSGROUP:onafterLoading(From, Event, To) end end + local isAvail=true + if cargo.type==OPSTRANSPORT.CargoType.STORAGE then + local nAvail=cargo.storage.storageFrom:GetAmount(cargo.storage.cargoType) + if nAvail>0 then + isAvail=true + else + isAvail=false + end + else + isCarrier=cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading() + isNotCargo=cargo.opsgroup:IsNotCargo(true) + end + + local isDead=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDead() or false + -- Debug message. self:T(self.lid..string.format("Loading: canCargo=%s, isCarrier=%s, isNotCargo=%s, isHolding=%s, isOnMission=%s", - tostring(canCargo), tostring(isCarrier), tostring(isNotCargo), tostring(isHolding), tostring(isOnMission))) + tostring(canCargo), tostring(isCarrier), tostring(isNotCargo), tostring(isHolding), tostring(isOnMission))) -- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport() - if canCargo and inZone and isNotCargo and isHolding and (not (cargo.delivered or cargo.opsgroup:IsDead() or isCarrier or isOnMission)) then + if canCargo and inZone and isNotCargo and isHolding and isAvail and (not (cargo.delivered or isDead or isCarrier or isOnMission)) then table.insert(cargos, cargo) end end - -- Sort results table wrt descending weight. - local function _sort(a, b) - local cargoA=a --Ops.OpsGroup#OPSGROUP.CargoGroup - local cargoB=b --Ops.OpsGroup#OPSGROUP.CargoGroup - return cargoA.opsgroup:GetWeightTotal()>cargoB.opsgroup:GetWeightTotal() - end - table.sort(cargos, _sort) + -- Sort cargos. + self:_SortCargo(cargos) -- Loop over all cargos. for _,_cargo in pairs(cargos) do local cargo=_cargo --#OPSGROUP.CargoGroup - - -- Find a carrier for this cargo. - local carrier=self:FindCarrierForCargo(cargo.opsgroup) - - if carrier then - + + local weight=nil + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + + weight=cargo.opsgroup:GetWeightTotal() + + -- Find a carrier for this cargo. + local carrier=self:FindCarrierForCargo(weight) + -- Order cargo group to board the carrier. - cargo.opsgroup:Board(self, carrier) + if carrier then + cargo.opsgroup:Board(self, carrier) + end + + else + + --- + -- STORAGE + --- - end + -- Get weight of cargo that needs to be transported. + weight=self:_GetWeightStorage(cargo.storage, false) + -- Get amount that the warehouse currently has. + local Amount=cargo.storage.storageFrom:GetAmount(cargo.storage.cargoType) + local Weight=Amount*cargo.storage.cargoWeight + + -- Make sure, we do not take more than the warehouse can provide. + weight=math.min(weight, Weight) + + -- Debug info. + self:T(self.lid..string.format("Loading storage weight=%d kg (warehouse has %d kg)!", weight, Weight)) + + -- Loop over all elements of the carrier group. + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + + -- Get the free cargo space of the carrier. + local free=self:GetFreeCargobay(element.name) + + -- Min of weight or bay. + local w=math.min(weight, free) + + -- Check that weight is >0 and also greater that at least one item. We cannot transport half a missile. + if w>=cargo.storage.cargoWeight then + + -- Calculate item amount. + local amount=math.floor(w/cargo.storage.cargoWeight) + + -- Remove items from warehouse. + cargo.storage.storageFrom:RemoveAmount(cargo.storage.cargoType, amount) + + -- Add amount to loaded cargo. + cargo.storage.cargoLoaded=cargo.storage.cargoLoaded+amount + + -- Add cargo to cargo by of element. + self:_AddCargobayStorage(element, cargo.uid, cargo.storage.cargoType, amount, cargo.storage.cargoWeight) + + -- Reduce weight for the next element (if any). + weight=weight-amount*cargo.storage.cargoWeight + + -- Debug info. + local text=string.format("Element %s: loaded amount=%d (weight=%d) ==> left=%d kg", element.name, amount, amount*cargo.storage.cargoWeight, weight) + self:T(self.lid..text) + + -- If no cargo left, break the loop. + if weight<=0 then + break + end + + end + + end + + end end + end --- Set (new) cargo status. @@ -9322,6 +9692,8 @@ function OPSGROUP:onafterTransport(From, Event, To) ready2deploy=true end end + + --env.info(string.format("FF Transport: Zone=%s inzone=%s, ready2deploy=%s", Zone:GetName(), tostring(inzone), tostring(ready2deploy))) if inzone then @@ -9479,142 +9851,205 @@ function OPSGROUP:onafterUnloading(From, Event, To) for _,_cargo in pairs(self.cargoTZC.Cargos) do local cargo=_cargo --#OPSGROUP.CargoGroup + + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then - -- Check that cargo is loaded into this group. - -- NOTE: Could be that the element carriing this cargo group is DEAD, which would mean that the cargo group is also DEAD. - if cargo.opsgroup:IsLoaded(self.groupname) and not cargo.opsgroup:IsDead() then - - -- Disembark to carrier. - local carrier=nil --Ops.OpsGroup#OPSGROUP.Element - local carrierGroup=nil --Ops.OpsGroup#OPSGROUP - local disembarkToCarriers=cargo.disembarkCarriers~=nil or self.cargoTZC.disembarkToCarriers - - -- Set specifc zone for this cargo. - if cargo.disembarkZone then - zone=cargo.disembarkZone - end - - self:T(self.lid..string.format("Unloading cargo %s to zone %s", cargo.opsgroup:GetName(), zone and zone:GetName() or "No Zone Found!")) - - -- Try to get the OPSGROUP if deploy zone is a ship. - if zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then - local shipname=zone:GetAirbase():GetName() - local ship=UNIT:FindByName(shipname) - local group=ship:GetGroup() - carrierGroup=_DATABASE:GetOpsGroup(group:GetName()) - carrier=carrierGroup:GetElementByName(shipname) - end - - if disembarkToCarriers then - - -- Debug info. - self:T(self.lid..string.format("Trying to find disembark carriers in zone %s", zone:GetName())) + -- Check that cargo is loaded into this group. + -- NOTE: Could be that the element carriing this cargo group is DEAD, which would mean that the cargo group is also DEAD. + if cargo.opsgroup:IsLoaded(self.groupname) and not cargo.opsgroup:IsDead() then + + -- Disembark to carrier. + local carrier=nil --Ops.OpsGroup#OPSGROUP.Element + local carrierGroup=nil --Ops.OpsGroup#OPSGROUP + local disembarkToCarriers=cargo.disembarkCarriers~=nil or self.cargoTZC.disembarkToCarriers - -- Disembarkcarriers. - local disembarkCarriers=cargo.disembarkCarriers or self.cargoTZC.DisembarkCarriers - - -- Try to find a carrier that can take the cargo. - carrier, carrierGroup=self.cargoTransport:FindTransferCarrierForCargo(cargo.opsgroup, zone, disembarkCarriers, self.cargoTZC.DeployAirbase) - - --TODO: max unloading time if transfer carrier does not arrive in the zone. - - end - - if (disembarkToCarriers and carrier and carrierGroup) or (not disembarkToCarriers) then - - -- Cargo was delivered (somehow). - cargo.delivered=true - - -- Increase number of delivered cargos. - self.cargoTransport.Ndelivered=self.cargoTransport.Ndelivered+1 - - if carrier and carrierGroup then - - --- - -- Delivered to another carrier group. - --- - - self:_TransferCargo(cargo.opsgroup, carrierGroup, carrier) - - elseif zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then - - --- - -- Delivered to a ship via helo that landed on its platform - --- - - -- Issue warning. - self:T(self.lid.."ERROR: Deploy/disembark zone is a ZONE_AIRBASE of a ship! Where to put the cargo? Dumping into the sea, sorry!") - - -- Unload but keep "in utero" (no coordinate provided). - self:Unload(cargo.opsgroup) - - else - - --- - -- Delivered to deploy zone - --- - - if self.cargoTransport:GetDisembarkInUtero(self.cargoTZC) then - + -- Set specifc zone for this cargo. + if cargo.disembarkZone then + zone=cargo.disembarkZone + end + + self:T(self.lid..string.format("Unloading cargo %s to zone %s", cargo.opsgroup:GetName(), zone and zone:GetName() or "No Zone Found!")) + + -- Try to get the OPSGROUP if deploy zone is a ship. + if zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then + local shipname=zone:GetAirbase():GetName() + local ship=UNIT:FindByName(shipname) + local group=ship:GetGroup() + carrierGroup=_DATABASE:GetOpsGroup(group:GetName()) + carrier=carrierGroup:GetElementByName(shipname) + end + + if disembarkToCarriers then + + -- Debug info. + self:T(self.lid..string.format("Trying to find disembark carriers in zone %s", zone:GetName())) + + -- Disembarkcarriers. + local disembarkCarriers=cargo.disembarkCarriers or self.cargoTZC.DisembarkCarriers + + -- Try to find a carrier that can take the cargo. + carrier, carrierGroup=self.cargoTransport:FindTransferCarrierForCargo(cargo.opsgroup, zone, disembarkCarriers, self.cargoTZC.DeployAirbase) + + --TODO: max unloading time if transfer carrier does not arrive in the zone. + + end + + if (disembarkToCarriers and carrier and carrierGroup) or (not disembarkToCarriers) then + + -- Cargo was delivered (somehow). + cargo.delivered=true + + -- Increase number of delivered cargos. + self.cargoTransport.Ndelivered=self.cargoTransport.Ndelivered+1 + + if carrier and carrierGroup then + + --- + -- Delivered to another carrier group. + --- + + self:_TransferCargo(cargo.opsgroup, carrierGroup, carrier) + + elseif zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then + + --- + -- Delivered to a ship via helo that landed on its platform + --- + + -- Issue warning. + self:T(self.lid.."ERROR: Deploy/disembark zone is a ZONE_AIRBASE of a ship! Where to put the cargo? Dumping into the sea, sorry!") + -- Unload but keep "in utero" (no coordinate provided). self:Unload(cargo.opsgroup) - + else - - -- Get disembark zone of this TZC. - local DisembarkZone=cargo.disembarkZone or self.cargoTransport:GetDisembarkZone(self.cargoTZC) - - local Coordinate=nil - - - if DisembarkZone then - - -- Random coordinate in disembark zone. - Coordinate=DisembarkZone:GetRandomCoordinate() - + + --- + -- Delivered to deploy zone + --- + + if self.cargoTransport:GetDisembarkInUtero(self.cargoTZC) then + + -- Unload but keep "in utero" (no coordinate provided). + self:Unload(cargo.opsgroup) + else - - local element=cargo.opsgroup:_GetMyCarrierElement() - - if element then - - -- Get random point in disembark zone. - local zoneCarrier=self:GetElementZoneUnload(element.name) - - -- Random coordinate/heading in the zone. - Coordinate=zoneCarrier:GetRandomCoordinate() - + + -- Get disembark zone of this TZC. + local DisembarkZone=cargo.disembarkZone or self.cargoTransport:GetDisembarkZone(self.cargoTZC) + + local Coordinate=nil + + + if DisembarkZone then + + -- Random coordinate in disembark zone. + Coordinate=DisembarkZone:GetRandomCoordinate() + else - self:E(self.lid..string.format("ERROR carrier element nil!")) + + local element=cargo.opsgroup:_GetMyCarrierElement() + + if element then + + -- Get random point in disembark zone. + local zoneCarrier=self:GetElementZoneUnload(element.name) + + -- Random coordinate/heading in the zone. + Coordinate=zoneCarrier:GetRandomCoordinate() + + else + self:E(self.lid..string.format("ERROR carrier element nil!")) + end + end - + + -- Random heading of the group. + local Heading=math.random(0,359) + + -- Activation on/off. + local activation=self.cargoTransport:GetDisembarkActivation(self.cargoTZC) + if cargo.disembarkActivation~=nil then + activation=cargo.disembarkActivation + end + + -- Unload to Coordinate. + self:Unload(cargo.opsgroup, Coordinate, activation, Heading) + end - - -- Random heading of the group. - local Heading=math.random(0,359) - - -- Activation on/off. - local activation=self.cargoTransport:GetDisembarkActivation(self.cargoTZC) - if cargo.disembarkActivation~=nil then - activation=cargo.disembarkActivation - end - - -- Unload to Coordinate. - self:Unload(cargo.opsgroup, Coordinate, activation, Heading) - + + -- Trigger "Unloaded" event for current cargo transport + self.cargoTransport:Unloaded(cargo.opsgroup, self) + end - - -- Trigger "Unloaded" event for current cargo transport - self.cargoTransport:Unloaded(cargo.opsgroup, self) - + + else + self:T(self.lid.."Cargo needs carrier but no carrier is avaiable (yet)!") end - + else - self:T(self.lid.."Cargo needs carrier but no carrier is avaiable (yet)!") + -- Not loaded or dead end - + else - -- Not loaded or dead + + --- + -- STORAGE + --- + + -- TODO: should proabaly move this check to the top to include OPSGROUPS as well?! + if not cargo.delivered then + + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + + -- Get my cargo from cargo bay of element. + local mycargo=self:_GetCargobayElement(element, cargo.uid) + + if mycargo then + -- Add cargo to warehouse storage. + cargo.storage.storageTo:AddAmount(mycargo.storageType, mycargo.storageAmount) + + -- Add amount to delivered. + cargo.storage.cargoDelivered=cargo.storage.cargoDelivered+mycargo.storageAmount + + -- Reduce loaded amount. + cargo.storage.cargoLoaded=cargo.storage.cargoLoaded-mycargo.storageAmount + + -- Remove cargo from bay. + self:_DelCargobayElement(element, mycargo) + + -- Debug info + self:T2(self.lid..string.format("Cargo loaded=%d, delivered=%d, lost=%d", cargo.storage.cargoLoaded, cargo.storage.cargoDelivered, cargo.storage.cargoLost)) + + end + end + + -- Get amount that was delivered. + local amountToDeliver=self:_GetWeightStorage(cargo.storage, false, false, true) + + -- Get total amount to be delivered. + local amountTotal=self:_GetWeightStorage(cargo.storage, true, false, true) + + -- Debug info. + local text=string.format("Amount delivered=%d, total=%d", amountToDeliver, amountTotal) + self:T(self.lid..text) + + if amountToDeliver<=0 then + + -- Cargo was delivered (somehow). + cargo.delivered=true + + -- Increase number of delivered cargos. + self.cargoTransport.Ndelivered=self.cargoTransport.Ndelivered+1 + + -- Debug info. + local text=string.format("Ndelivered=%d delivered=%s", self.cargoTransport.Ndelivered, tostring(cargo.delivered)) + self:T(self.lid..text) + + end + end + end end -- loop over cargos @@ -10047,7 +10482,7 @@ function OPSGROUP:onafterBoard(From, Event, To, CarrierGroup, Carrier) -- Debug info. self:T(self.lid..string.format("Group is loaded currently ==> Moving directly to new carrier - No Unload(), Disembart() events triggered!")) - -- Remove my carrier. + -- Remove old/current carrier. self:_RemoveMyCarrier() -- Trigger Load event. diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index 56613fbd0..885c0340b 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -1,8 +1,9 @@ ---- **Ops** - Troop transport assignment for OPS groups. +--- **Ops** - Transport assignment for OPS groups and storage. -- -- ## Main Features: -- -- * Transport troops from A to B +-- * Transport of warehouse storage (fuel, weapons and equipment) -- * Supports ground, naval and airborne (airplanes and helicopters) units as carriers -- * Use combined forces (ground, naval, air) to transport the troops -- * Additional FSM events to hook into and customize your mission design @@ -63,6 +64,7 @@ -- @field Ops.Chief#CHIEF chief Chief of the transport. -- @field Ops.OpsZone#OPSZONE opszone OPS zone. -- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the transport was cancelled before the request is processed. +-- @field #number cargocounter Running number to generate cargo UIDs. -- -- @extends Core.Fsm#FSM @@ -79,7 +81,7 @@ -- * Cargo groups are **not** split and distributed into different carrier *units*. That means that the whole cargo group **must fit** into one of the carrier units. -- * Cargo groups must be inside the pickup zones to be considered for loading. Groups not inside the pickup zone will not get the command to board. -- --- # Constructor +-- # Troop Transport -- -- A new cargo transport assignment is created with the @{#OPSTRANSPORT.New}() function -- @@ -101,6 +103,30 @@ -- -- You can also mix carrier types. For instance, you can assign the same transport to APCs and helicopters. Or to helicopters and airplanes. -- +-- # Storage Transport +-- +-- An instance of the OPSTRANSPORT class is created similarly to the troop transport case. However, the first parameter is `nil` as not troops +-- are transported. +-- +-- local storagetransport=OPSTRANSPORT:New(nil, PickupZone, DeployZone) +-- +-- ## Defining Storage +-- +-- The storage warehouses from which the cargo is taken and to which the cargo is delivered have to be specified +-- +-- storagetransport:AddCargoStorage(berlinStorage, batumiStorage, STORAGE.Liquid.JETFUEL, 1000) +-- +-- Here `berlinStorage` and `batumiStorage` are @{Wrapper.Storage#STORAGE} objects of DCS warehouses. +-- +-- Furthermore, that type of cargo (liquids or weapons/equipment) and the amount has to be specified. If weapons/equipment is the cargo, +-- we also need to specify the weight per storage item as this cannot be retrieved from the DCS API and is not stored in any MOOSE database. +-- +-- storagetransport:AddCargoStorage(berlinStorage, batumiStorage, ENUMS.Storage.weapons.bombs.Mk_82, 9, 230) +-- +-- Finally, the transport is assigned to one or multiple groups, which carry out the transport +-- +-- myopsgroup:AddOpsTransport(storagetransport) +-- -- # Examples -- -- A carrier group is assigned to transport infantry troops from zone "Zone Kobuleti X" to zone "Zone Alpha". @@ -131,6 +157,7 @@ OPSTRANSPORT = { legions = {}, statusLegion = {}, requestID = {}, + cargocounter = 0, } --- Cargo transport status. @@ -186,6 +213,27 @@ OPSTRANSPORT.Status={ -- @field #number radius Radomization radius for waypoints in meters. Default 0 m. -- @field #boolean reverse If `true`, path is used in reversed order. +--- Storage data. +-- @type OPSTRANSPORT.Storage +-- @field Wrapper.Storage#STORAGE storageFrom Storage from. +-- @field Wrapper.Storage#STORAGE storageTo Storage To. +-- @field #string cargoType Type of cargo. +-- @field #number cargoAmount Amount of cargo that should be transported. +-- @field #number cargoReserved Amount of cargo that is reserved for a carrier group. +-- @field #number cargoDelivered Amount of cargo that has been delivered. +-- @field #number cargoLost Amount of cargo that was lost. +-- @field #number cargoLoaded Amount of cargo that is loading. +-- @field #number cargoWeight Weight of one single cargo item in kg. Default 1 kg. + +--- Storage data. +-- @type OPSTRANSPORT.CargoType +-- @field #string OPSGROUP Cargo is an OPSGROUP. +-- @field #string STORAGE Cargo is storage of DCS warehouse. +OPSTRANSPORT.CargoType={ + OPSGROUP="OPSGROUP", + STORAGE="STORAGE", +} + --- Generic transport condition. -- @type OPSTRANSPORT.Condition -- @field #function func Callback function to check for a condition. Should return a #boolean. @@ -196,7 +244,7 @@ _OPSTRANSPORTID=0 --- Army Group version. -- @field #string version -OPSTRANSPORT.version="0.7.0" +OPSTRANSPORT.version="0.8.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -205,6 +253,7 @@ OPSTRANSPORT.version="0.7.0" -- TODO: Trains. -- TODO: Stop transport. -- TODO: Improve pickup and transport paths. +-- DONE: Storage. -- DONE: Disembark parameters per cargo group. -- DONE: Special transport cohorts/legions. Similar to mission. -- DONE: Cancel transport. @@ -576,6 +625,38 @@ function OPSTRANSPORT:AddCargoGroups(GroupSet, TransportZoneCombo, DisembarkActi return self end +--- Add cargo warehouse storage to be transported. This adds items such as fuel, weapons and other equipment, which is to be transported +-- from one DCS warehouse to another. +-- For weapons and equipment, the weight per item has to be specified explicitly as these cannot be retrieved by the DCS API. For liquids the +-- default value of 1 kg per item should be used as the amount of liquid is already given in kg. +-- @param #OPSTRANSPORT self +-- @param Wrapper.Storage#STORAGE StorageFrom Storage warehouse from which the cargo is taken. +-- @param Wrapper.Storage#STORAGE StorageTo Storage warehouse to which the cargo is delivered. +-- @param #string CargoType Type of cargo, *e.g.* `"weapons.bombs.Mk_84"` or liquid type as #number. +-- @param #number CargoAmount Amount of cargo. Liquids in kg. +-- @param #number CargoWeight Weight of a single cargo item in kg. Default 1 kg. +-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo if other than default. +-- @return #OPSTRANSPORT self +function OPSTRANSPORT:AddCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, CargoWeight, TransportZoneCombo) + + -- Use default TZC if no transport zone combo is provided. + TransportZoneCombo=TransportZoneCombo or self.tzcDefault + + -- Cargo data. + local cargo=self:_CreateCargoStorage(StorageFrom,StorageTo, CargoType, CargoAmount, CargoWeight, TransportZoneCombo) + + if cargo then + + -- Add total amount of ever assigned cargos. + self.Ncargo=self.Ncargo+1 + + -- Add to TZC table. + table.insert(TransportZoneCombo.Cargos, cargo) + + end + +end + --- Set pickup zone. -- @param #OPSTRANSPORT self @@ -1063,16 +1144,14 @@ end -- @return #table Cargo Ops groups. Can be and empty table `{}`. function OPSTRANSPORT:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo) - local cargos=self:GetCargos(TransportZoneCombo) + local cargos=self:GetCargos(TransportZoneCombo, Carrier, Delivered) local opsgroups={} for _,_cargo in pairs(cargos) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - if Delivered==nil or cargo.delivered==Delivered then + if cargo.type=="OPSGROUP" then if cargo.opsgroup and not (cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then - if Carrier==nil or Carrier:CanCargo(cargo.opsgroup) then - table.insert(opsgroups, cargo.opsgroup) - end + table.insert(opsgroups, cargo.opsgroup) end end end @@ -1080,6 +1159,27 @@ function OPSTRANSPORT:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo) return opsgroups end +--- Get (all) cargo @{Ops.OpsGroup#OPSGROUP}s. Optionally, only delivered or undelivered groups can be returned. +-- @param #OPSTRANSPORT self +-- @param #boolean Delivered If `true`, only delivered groups are returned. If `false` only undelivered groups are returned. If `nil`, all groups are returned. +-- @param Ops.OpsGroup#OPSGROUP Carrier (Optional) Only count cargo groups that fit into the given carrier group. Current cargo is not a factor. +-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. +-- @return #table Cargo Ops groups. Can be and empty table `{}`. +function OPSTRANSPORT:GetCargoStorages(Delivered, Carrier, TransportZoneCombo) + + local cargos=self:GetCargos(TransportZoneCombo, Carrier, Delivered) + + local opsgroups={} + for _,_cargo in pairs(cargos) do + local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup + if cargo.type=="STORAGE" then + table.insert(opsgroups, cargo.storage) + end + end + + return opsgroups +end + --- Get carriers. -- @param #OPSTRANSPORT self -- @return #table Carrier Ops groups. @@ -1090,22 +1190,60 @@ end --- Get cargos. -- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. +-- @param Ops.OpsGroup#OPSGROUP Carrier Specific carrier. +-- @param #boolean Delivered Delivered status. -- @return #table Cargos. -function OPSTRANSPORT:GetCargos(TransportZoneCombo) - +function OPSTRANSPORT:GetCargos(TransportZoneCombo, Carrier, Delivered) + + local tczs=self.tzCombos if TransportZoneCombo then - return TransportZoneCombo.Cargos - else - local cargos={} - for _,_tzc in pairs(self.tzCombos) do - local tzc=_tzc --#OPSTRANSPORT.TransportZoneCombo - for _,cargo in pairs(tzc.Cargos) do - table.insert(cargos, cargo) - end - end - return cargos + tczs={TransportZoneCombo} end + local cargos={} + for _,_tcz in pairs(tczs) do + local tcz=_tcz --#OPSTRANSPORT.TransportZoneCombo + for _,_cargo in pairs(tcz.Cargos) do + local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup + if Delivered==nil or cargo.delivered==Delivered then + if Carrier==nil or Carrier:CanCargo(cargo) then + table.insert(cargos, cargo) + end + end + end + end + + return cargos +end + +--- Get total weight. +-- @param #OPSTRANSPORT self +-- @param Ops.OpsGroup#OPSGROUP.CargoGroup Cargo Cargo data. +-- @param #boolean IncludeReserved Include reserved cargo. +-- @return #number Weight in kg. +function OPSTRANSPORT:GetCargoTotalWeight(Cargo, IncludeReserved) + + local weight=0 + + if Cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + weight=Cargo.opsgroup:GetWeightTotal(nil, IncludeReserved) + else + if type(Cargo.storage.cargoType)=="number" then + if IncludeReserved then + return Cargo.storage.cargoAmount+Cargo.storage.cargoReserved + else + return Cargo.storage.cargoAmount + end + else + if IncludeReserved then + return Cargo.storage.cargoAmount*100 -- Assume 100 kg per item + else + return (Cargo.storage.cargoAmount+Cargo.storage.cargoReserved)*100 -- Assume 100 kg per item + end + end + end + + return weight end --- Set transport start and stop time. @@ -1642,11 +1780,18 @@ function OPSTRANSPORT:onafterStatusUpdate(From, Event, To) text=text..string.format("\nCargos:") for _,_cargo in pairs(self:GetCargos()) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - local carrier=cargo.opsgroup:_GetMyCarrierElement() - local name=carrier and carrier.name or "none" - local cstate=carrier and carrier.status or "N/A" - text=text..string.format("\n- %s: %s [%s], weight=%d kg, carrier=%s [%s], delivered=%s [UID=%s]", - cargo.opsgroup:GetName(), cargo.opsgroup.cargoStatus:upper(), cargo.opsgroup:GetState(), cargo.opsgroup:GetWeightTotal(), name, cstate, tostring(cargo.delivered), tostring(cargo.opsgroup.cargoTransportUID)) + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + local carrier=cargo.opsgroup:_GetMyCarrierElement() + local name=carrier and carrier.name or "none" + local cstate=carrier and carrier.status or "N/A" + text=text..string.format("\n- %s: %s [%s], weight=%d kg, carrier=%s [%s], delivered=%s [UID=%s]", + cargo.opsgroup:GetName(), cargo.opsgroup.cargoStatus:upper(), cargo.opsgroup:GetState(), cargo.opsgroup:GetWeightTotal(), name, cstate, tostring(cargo.delivered), tostring(cargo.opsgroup.cargoTransportUID)) + else + --TODO: Storage + local storage=cargo.storage + text=text..string.format("\n- storage type=%s: amount: total=%d loaded=%d, lost=%d, delivered=%d, delivered=%s [UID=%s]", + storage.cargoType, storage.cargoAmount, storage.cargoLoaded, storage.cargoLost, storage.cargoDelivered, tostring(cargo.delivered), tostring(cargo.uid)) + end end text=text..string.format("\nCarriers:") @@ -1923,14 +2068,14 @@ function OPSTRANSPORT:_CheckDelivered() if cargo.delivered then -- This one is delivered. dead=false - elseif cargo.opsgroup==nil then + elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup==nil then -- This one is nil?! dead=false - elseif cargo.opsgroup:IsDestroyed() then + elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDestroyed() then -- This one was destroyed. - elseif cargo.opsgroup:IsDead() then + elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDead() then -- This one is dead. - elseif cargo.opsgroup:IsStopped() then + elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsStopped() then -- This one is stopped. dead=false else @@ -2116,10 +2261,12 @@ function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, Disembark end end + self.cargocounter=self.cargocounter+1 -- Create a new data item. local cargo={} --Ops.OpsGroup#OPSGROUP.CargoGroup - + cargo.uid=self.cargocounter + cargo.type="OPSGROUP" cargo.opsgroup=opsgroup cargo.delivered=false cargo.status="Unknown" @@ -2133,6 +2280,45 @@ function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, Disembark return cargo end +--- Create a cargo group data structure. +-- @param #OPSTRANSPORT self +-- @param Wrapper.Storage#STORAGE StorageFrom Storage from. +-- @param Wrapper.Storage#STORAGE StorageTo Storage to. +-- @param #string CargoType Type of cargo. +-- @param #number CargoAmount Total amount of cargo that should be transported. Liquids in kg. +-- @param #number CargoWeight Weight of a single cargo item in kg. Default 1 kg. +-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. +-- @return Ops.OpsGroup#OPSGROUP.CargoGroup Cargo group data. +function OPSTRANSPORT:_CreateCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, CargoWeight, TransportZoneCombo) + + local storage={} --#OPSTRANSPORT.Storage + storage.storageFrom=StorageFrom + storage.storageTo=StorageTo + storage.cargoType=CargoType + storage.cargoAmount=CargoAmount + storage.cargoDelivered=0 + storage.cargoLost=0 + storage.cargoReserved=0 + storage.cargoLoaded=0 + storage.cargoWeight=CargoWeight or 1 + + self.cargocounter=self.cargocounter+1 + + -- Create a new data item. + local cargo={} --Ops.OpsGroup#OPSGROUP.CargoGroup + cargo.uid=self.cargocounter + cargo.type="STORAGE" + cargo.opsgroup=nil + cargo.storage=storage + cargo.delivered=false + cargo.status="Unknown" + cargo.tzcUID=TransportZoneCombo + cargo.disembarkZone=nil + cargo.disembarkCarriers=nil + + return cargo +end + --- Count how many cargo groups are inside a zone. -- @param #OPSTRANSPORT self -- @param Core.Zone#ZONE Zone The zone object. @@ -2143,7 +2329,9 @@ end function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier, TransportZoneCombo) -- Get cargo ops groups. - local cargos=self:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo) + --local cargos=self:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo) + + local cargos=self:GetCargos(TransportZoneCombo, Carrier, Delivered) --- Function to check if carrier is supposed to be disembarked to. local function iscarrier(_cargo) @@ -2185,22 +2373,33 @@ function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier, TransportZone local N=0 for _,_cargo in pairs(cargos) do - local cargo=_cargo --Ops.OpsGroup#OPSGROUP - - -- Is not cargo? - local isNotCargo=cargo:IsNotCargo(true) - if not isNotCargo then - isNotCargo=iscarrier(cargo) - end - - -- Is in zone? - local isInZone=cargo:IsInZone(Zone) + local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - -- Is in utero? - local isInUtero=cargo:IsInUtero() + local isNotCargo=true + local isInZone=true + local isInUtero=true + + if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then + local opsgroup=cargo.opsgroup + + -- Is not cargo? + isNotCargo=opsgroup:IsNotCargo(true) + if not isNotCargo then + isNotCargo=iscarrier(opsgroup) + end + + -- Is in zone? + isInZone=opsgroup:IsInZone(Zone) + + -- Is in utero? + isInUtero=opsgroup:IsInUtero() + + -- Debug info. + self:T(self.lid..string.format("Cargo=%s: notcargo=%s, iscarrier=%s inzone=%s, inutero=%s", opsgroup:GetName(), tostring(opsgroup:IsNotCargo(true)), tostring(iscarrier(opsgroup)), tostring(isInZone), tostring(isInUtero))) + + + end - -- Debug info. - self:T(self.lid..string.format("Cargo=%s: notcargo=%s, iscarrier=%s inzone=%s, inutero=%s", cargo:GetName(), tostring(cargo:IsNotCargo(true)), tostring(iscarrier(cargo)), tostring(isInZone), tostring(isInUtero))) -- We look for groups that are not cargo, in the zone or in utero. if isNotCargo and (isInZone or isInUtero) then diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index df55a88d4..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 @@ -1068,7 +1075,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 +1085,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)) @@ -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 diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index b00552dcf..fc4e1d955 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -562,3 +562,578 @@ 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.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" diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 79f794b48..52a659e44 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -4056,3 +4056,1211 @@ 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 diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 571e0d48c..57e50c7be 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -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 diff --git a/Moose Development/Moose/Wrapper/Storage.lua b/Moose Development/Moose/Wrapper/Storage.lua index 191e916ca..be07bb477 100644 --- a/Moose Development/Moose/Wrapper/Storage.lua +++ b/Moose Development/Moose/Wrapper/Storage.lua @@ -336,6 +336,181 @@ function STORAGE:GetLiquidName(Type) return name end +--- Adds the amount of a given type of aircraft, liquid, weapon currently present the warehouse. +-- @param #STORAGE self +-- @param #number Type Type of liquid or name of aircraft, weapon or equipment. +-- @param #number Amount Amount of given type to add. Liquids in kg. +-- @return #STORAGE self +function STORAGE:AddAmount(Type, Amount) + + if type(Type)=="number" then + self:AddLiquid(Type, Amount) + else + self:AddItem(Type, Amount) + end + + return self +end + +--- Removes the amount of a given type of aircraft, liquid, weapon from the warehouse. +-- @param #STORAGE self +-- @param #number Type Type of liquid or name of aircraft, weapon or equipment. +-- @param #number Amount Amount of given type to remove. Liquids in kg. +-- @return #STORAGE self +function STORAGE:RemoveAmount(Type, Amount) + + if type(Type)=="number" then + self:RemoveLiquid(Type, Amount) + else + self:RemoveItem(Type, Amount) + end + + return self +end + +--- Sets the amount of a given type of aircraft, liquid, weapon currently present the warehouse. +-- @param #STORAGE self +-- @param #number Type Type of liquid or name of aircraft, weapon or equipment. +-- @param #number Amount of given type. Liquids in kg. +-- @return #STORAGE self +function STORAGE:SetAmount(Type, Amount) + + if type(Type)=="number" then + self:SetLiquid(Type, Amount) + else + self:SetItem(Type, Amount) + end + + return self +end + + +--- Gets the amount of a given type of aircraft, liquid, weapon currently present the warehouse. +-- @param #STORAGE self +-- @param #number Type Type of liquid or name of aircraft, weapon or equipment. +-- @return #number Amount of given type. Liquids in kg. +function STORAGE:GetAmount(Type) + + local N=0 + if type(Type)=="number" then + N=self:GetLiquidAmount(Type) + else + N=self:GetItemAmount(Type) + end + + return N +end + +--- Returns whether a given type of aircraft, liquid, weapon is set to be unlimited. +-- @param #STORAGE self +-- @param #string Type Name of aircraft, weapon or equipment or type of liquid (as `#number`). +-- @return #boolen If `true` the given type is unlimited or `false` otherwise. +function STORAGE:IsUnlimited(Type) + + -- Get current amount of type. + local N=self:GetAmount(Type) + + local unlimited=false + + if N>0 then + + -- Remove one item. + self:RemoveAmount(Type, 1) + + -- Get amount. + local n=self:GetAmount(Type) + + -- If amount did not change, it is unlimited. + unlimited=n==N + + -- Add item back. + if not unlimited then + self:AddAmount(Type, 1) + end + + -- Debug info. + self:I(self.lid..string.format("Type=%s: unlimited=%s (N=%d n=%d)", tostring(Type), tostring(unlimited), N, n)) + end + + return unlimited +end + +--- Returns whether a given type of aircraft, liquid, weapon is set to be limited. +-- @param #STORAGE self +-- @param #number Type Type of liquid or name of aircraft, weapon or equipment. +-- @return #boolen If `true` the given type is limited or `false` otherwise. +function STORAGE:IsLimited(Type) + + local limited=not self:IsUnlimited(Type) + + return limited +end + +--- Returns whether aircraft are unlimited. +-- @param #STORAGE self +-- @return #boolen If `true` aircraft are unlimited or `false` otherwise. +function STORAGE:IsUnlimitedAircraft() + + -- We test with a specific type but if it is unlimited, than all aircraft are. + local unlimited=self:IsUnlimited("A-10C") + + return unlimited +end + +--- Returns whether liquids are unlimited. +-- @param #STORAGE self +-- @return #boolen If `true` liquids are unlimited or `false` otherwise. +function STORAGE:IsUnlimitedLiquids() + + -- We test with a specific type but if it is unlimited, than all are. + local unlimited=self:IsUnlimited(STORAGE.Liquid.DIESEL) + + return unlimited +end + +--- Returns whether weapons and equipment are unlimited. +-- @param #STORAGE self +-- @return #boolen If `true` weapons and equipment are unlimited or `false` otherwise. +function STORAGE:IsUnlimitedWeapons() + + -- We test with a specific type but if it is unlimited, than all are. + local unlimited=self:IsUnlimited(ENUMS.Storage.weapons.bombs.Mk_82) + + return unlimited +end + +--- Returns whether aircraft are limited. +-- @param #STORAGE self +-- @return #boolen If `true` aircraft are limited or `false` otherwise. +function STORAGE:IsLimitedAircraft() + + -- We test with a specific type but if it is limited, than all are. + local limited=self:IsLimited("A-10C") + + return limited +end + +--- Returns whether liquids are limited. +-- @param #STORAGE self +-- @return #boolen If `true` liquids are limited or `false` otherwise. +function STORAGE:IsLimitedLiquids() + + -- We test with a specific type but if it is limited, than all are. + local limited=self:IsLimited(STORAGE.Liquid.DIESEL) + + return limited +end + +--- Returns whether weapons and equipment are limited. +-- @param #STORAGE self +-- @return #boolen If `true` liquids are limited or `false` otherwise. +function STORAGE:IsLimitedWeapons() + + -- We test with a specific type but if it is limited, than all are. + local limited=self:IsLimited(ENUMS.Storage.weapons.bombs.Mk_82) + + return limited +end --- Returns a full itemized list of everything currently in a warehouse. If a category is set to unlimited then the table will be returned empty. -- @param #STORAGE self