From 19f6a8d8f6543033a0c8b6c8667f2efa5ba64989 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 16 Aug 2024 09:36:29 +0200 Subject: [PATCH] #DYNAMICCARGO - Added functionalty --- Moose Development/Moose/Core/Database.lua | 106 +++- Moose Development/Moose/Core/Event.lua | 115 ++++- Moose Development/Moose/Functional/Range.lua | 26 +- Moose Development/Moose/Modules.lua | 1 + Moose Development/Moose/Ops/CTLD.lua | 224 ++++++--- .../Moose/Wrapper/DynamicCargo.lua | 457 ++++++++++++++++++ Moose Development/Moose/Wrapper/Net.lua | 2 + Moose Development/Moose/Wrapper/Storage.lua | 20 + Moose Development/Moose/Wrapper/Unit.lua | 2 +- Moose Setup/Moose.files | 1 + 10 files changed, 865 insertions(+), 89 deletions(-) create mode 100644 Moose Development/Moose/Wrapper/DynamicCargo.lua diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index a9a6e035c..bb5d65987 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -20,6 +20,7 @@ -- * Manage database of hits to units and statics. -- * Manage database of destroys of units and statics. -- * Manage database of @{Core.Zone#ZONE_BASE} objects. +-- * Manage database of @{Wrapper.DynamicCargo#DYNAMICCARGO} objects alive in the mission. -- -- === -- @@ -39,6 +40,7 @@ -- @field #table STORAGES DCS warehouse storages. -- @field #table STNS Used Link16 octal numbers for F16/15/18/AWACS planes. -- @field #table SADL Used Link16 octal numbers for A10/C-II planes. +-- @field #table DYNAMICCARGO Dynamic Cargo objects. -- @extends Core.Base#BASE --- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator. @@ -54,6 +56,7 @@ -- * PLAYERS -- * CARGOS -- * STORAGES (DCS warehouses) +-- * DYNAMICCARGO -- -- On top, for internal MOOSE administration purposes, the DATABASE administers the Unit and Group TEMPLATES as defined within the Mission Editor. -- @@ -97,6 +100,7 @@ DATABASE = { STORAGES = {}, STNS={}, SADL={}, + DYNAMICCARGO={}, } local _DATABASECoalition = @@ -143,6 +147,8 @@ function DATABASE:New() self:HandleEvent( EVENTS.DeleteZone ) --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event. self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) + -- DCS 2.9.7 Moose own dynamic cargo events + self:HandleEvent( EVENTS.DynamicCargoRemoved, self._EventOnDynamicCargoRemoved) self:_RegisterTemplates() self:_RegisterGroupsAndUnits() @@ -173,16 +179,20 @@ end -- @param #boolean force -- @return Wrapper.Unit#UNIT The added unit. function DATABASE:AddUnit( DCSUnitName, force ) - - if not self.UNITS[DCSUnitName] or force == true then + + local DCSunitName = DCSUnitName + + if type(DCSunitName) == "number" then DCSunitName = string.format("%d",DCSUnitName) end + + if not self.UNITS[DCSunitName] or force == true then -- Debug info. - self:T( { "Add UNIT:", DCSUnitName } ) + self:T( { "Add UNIT:", DCSunitName } ) -- Register unit - self.UNITS[DCSUnitName]=UNIT:Register(DCSUnitName) + self.UNITS[DCSunitName]=UNIT:Register(DCSunitName) end - return self.UNITS[DCSUnitName] + return self.UNITS[DCSunitName] end @@ -224,6 +234,34 @@ function DATABASE:FindStatic( StaticName ) return StaticFound end +--- Add a DynamicCargo to the database. +-- @param #DATABASE self +-- @param #string Name Name of the dynamic cargo. +-- @return Wrapper.DynamicCargo#DYNAMICCARGO The dynamic cargo object. +function DATABASE:AddDynamicCargo( Name ) + if not self.DYNAMICCARGO[Name] then + self.DYNAMICCARGO[Name] = DYNAMICCARGO:Register(Name) + return self.DYNAMICCARGO[Name] + end + return nil +end + +--- Finds a DYNAMICCARGO based on the Dynamic Cargo Name. +-- @param #DATABASE self +-- @param #string DynamicCargoName +-- @return Wrapper.DynamicCargo#DYNAMICCARGO The found DYNAMICCARGO. +function DATABASE:FindDynamicCargo( DynamicCargoName ) + local StaticFound = self.DYNAMICCARGO[DynamicCargoName] + return StaticFound +end + +--- Deletes a DYNAMICCARGO from the DATABASE based on the Dynamic Cargo Name. +-- @param #DATABASE self +function DATABASE:DeleteDynamicCargo( DynamicCargoName ) + self.DYNAMICCARGO[DynamicCargoName] = nil + return self +end + --- Adds a Airbase based on the Airbase Name in the DATABASE. -- @param #DATABASE self -- @param #string AirbaseName The name of the airbase. @@ -818,12 +856,16 @@ end -- @param #boolean Force (optional) Force registration of client. -- @return Wrapper.Client#CLIENT The client object. function DATABASE:AddClient( ClientName, Force ) - - if not self.CLIENTS[ClientName] or Force == true then - self.CLIENTS[ClientName] = CLIENT:Register( ClientName ) + + local DCSUnitName = ClientName + + if type(DCSUnitName) == "number" then DCSUnitName = string.format("%d",ClientName) end + + if not self.CLIENTS[DCSUnitName] or Force == true then + self.CLIENTS[DCSUnitName] = CLIENT:Register( DCSUnitName ) end - return self.CLIENTS[ClientName] + return self.CLIENTS[DCSUnitName] end @@ -863,9 +905,11 @@ end --- Adds a player based on the Player Name in the DATABASE. -- @param #DATABASE self function DATABASE:AddPlayer( UnitName, PlayerName ) - + + if type(UnitName) == "number" then UnitName = string.format("%d",UnitName) end + if PlayerName then - self:T( { "Add player for unit:", UnitName, PlayerName } ) + self:I( { "Add player for unit:", UnitName, PlayerName } ) self.PLAYERS[PlayerName] = UnitName self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName ) self.PLAYERSJOINED[PlayerName] = PlayerName @@ -873,6 +917,21 @@ function DATABASE:AddPlayer( UnitName, PlayerName ) end +--- Get a PlayerName by UnitName from PLAYERS in DATABASE. +-- @param #DATABASE self +-- @return #string PlayerName +-- @return Wrapper.Unit#UNIT PlayerUnit +function DATABASE:_FindPlayerNameByUnitName(UnitName) + if UnitName then + for playername,unitname in pairs(self.PLAYERS) do + if unitname == UnitName and self.PLAYERUNITS[playername] and self.PLAYERUNITS[playername]:IsAlive() then + return playername, self.PLAYERUNITS[playername] + end + end + end + return nil +end + --- Deletes a player from the DATABASE based on the Player Name. -- @param #DATABASE self function DATABASE:DeletePlayer( UnitName, PlayerName ) @@ -1244,7 +1303,7 @@ function DATABASE:_GetGenericStaticCargoGroupTemplate(Name,Typename,Mass,Coaliti StaticTemplate.CategoryID = "static" StaticTemplate.CoalitionID = Coalition or coalition.side.BLUE StaticTemplate.CountryID = Country or country.id.GERMANY - UTILS.PrintTableToLog(StaticTemplate) + --UTILS.PrintTableToLog(StaticTemplate) return StaticTemplate end @@ -1417,7 +1476,7 @@ function DATABASE:_RegisterDynamicGroup(Groupname) -- Add unit. self:I(string.format("Register Unit: %s", tostring(DCSUnitName))) - self:AddUnit( DCSUnitName, true ) + self:AddUnit( tostring(DCSUnitName), true ) end else @@ -1568,7 +1627,7 @@ end -- @param #DATABASE self -- @param Core.Event#EVENTDATA Event function DATABASE:_EventOnBirth( Event ) - self:F( { Event } ) + self:T( { Event } ) if Event.IniDCSUnit then @@ -1576,7 +1635,17 @@ function DATABASE:_EventOnBirth( Event ) -- Add static object to DB. self:AddStatic( Event.IniDCSUnitName ) + + elseif Event.IniObjectCategory == Object.Category.CARGO and string.match(Event.IniUnitName,".+|%d%d:%d%d|PKG%d+") then + -- Add dynamic cargo object to DB + + local cargo = self:AddDynamicCargo(Event.IniDCSUnitName) + + self:I(string.format("Adding dynamic cargo %s", tostring(Event.IniDCSUnitName))) + + self:CreateEventNewDynamicCargo( cargo ) + else if Event.IniObjectCategory == Object.Category.UNIT then @@ -1762,6 +1831,15 @@ function DATABASE:_EventOnPlayerEnterUnit( Event ) end end +--- Handles the OnDynamicCargoRemoved event to clean the active dynamic cargo table. +-- @param #DATABASE self +-- @param Core.Event#EVENTDATA Event +function DATABASE:_EventOnDynamicCargoRemoved( Event ) + self:T( { Event } ) + if Event.IniDynamicCargoName then + self:DeleteDynamicCargo(Event.IniDynamicCargoName) + end +end --- Handles the OnPlayerLeaveUnit event to clean the active players table. -- @param #DATABASE self diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 1d713607c..bd4cd039e 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -194,6 +194,11 @@ world.event.S_EVENT_NEW_ZONE_GOAL = world.event.S_EVENT_MAX + 1004 world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005 world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006 world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007 +-- dynamic cargo +world.event.S_EVENT_NEW_DYNAMIC_CARGO = world.event.S_EVENT_MAX + 1008 +world.event.S_EVENT_DYNAMIC_CARGO_LOADED = world.event.S_EVENT_MAX + 1009 +world.event.S_EVENT_DYNAMIC_CARGO_UNLOADED = world.event.S_EVENT_MAX + 1010 +world.event.S_EVENT_DYNAMIC_CARGO_REMOVED = world.event.S_EVENT_MAX + 1011 --- The different types of events supported by MOOSE. @@ -275,7 +280,12 @@ EVENTS = { SimulationFreeze = world.event.S_EVENT_SIMULATION_FREEZE or -1, SimulationUnfreeze = world.event.S_EVENT_SIMULATION_UNFREEZE or -1, HumanAircraftRepairStart = world.event.S_EVENT_HUMAN_AIRCRAFT_REPAIR_START or -1, - HumanAircraftRepairFinish = world.event.S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH or -1, + HumanAircraftRepairFinish = world.event.S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH or -1, + -- dynamic cargo + NewDynamicCargo = world.event.S_EVENT_NEW_DYNAMIC_CARGO or -1, + DynamicCargoLoaded = world.event.S_EVENT_DYNAMIC_CARGO_LOADED or -1, + DynamicCargoUnloaded = world.event.S_EVENT_DYNAMIC_CARGO_UNLOADED or -1, + DynamicCargoRemoved = world.event.S_EVENT_DYNAMIC_CARGO_REMOVED or -1, } @@ -334,6 +344,9 @@ EVENTS = { -- -- @field Core.Zone#ZONE Zone The zone object. -- @field #string ZoneName The name of the zone. +-- +-- @field Wrapper.DynamicCargo#DYNAMICCARGO IniDynamicCargo The dynamic cargo object. +-- @field #string IniDynamicCargoName The dynamic cargo unit name. @@ -730,6 +743,31 @@ local _EVENTMETA = { Side = "I", Event = "OnEventHumanAircraftRepairFinish", Text = "S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH" + }, + -- dynamic cargo + [EVENTS.NewDynamicCargo] = { + Order = 1, + Side = "I", + Event = "OnEventNewDynamicCargo", + Text = "S_EVENT_NEW_DYNAMIC_CARGO" + }, + [EVENTS.DynamicCargoLoaded] = { + Order = 1, + Side = "I", + Event = "OnEventDynamicCargoLoaded", + Text = "S_EVENT_DYNAMIC_CARGO_LOADED" + }, + [EVENTS.DynamicCargoUnloaded] = { + Order = 1, + Side = "I", + Event = "OnEventDynamicCargoUnloaded", + Text = "S_EVENT_DYNAMIC_CARGO_UNLOADED" + }, + [EVENTS.DynamicCargoRemoved] = { + Order = 1, + Side = "I", + Event = "OnEventDynamicCargoRemoved", + Text = "S_EVENT_DYNAMIC_CARGO_REMOVED" }, } @@ -1146,7 +1184,63 @@ do -- Event Creation world.onEvent( Event ) end - + + --- Creation of a S_EVENT_NEW_DYNAMIC_CARGO event. + -- @param #EVENT self + -- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object + function EVENT:CreateEventNewDynamicCargo(DynamicCargo) + self:F({DynamicCargo}) + local Event = { + id = EVENTS.NewDynamicCargo, + time = timer.getTime(), + dynamiccargo = DynamicCargo, + initiator = DynamicCargo:GetDCSObject(), + } + world.onEvent( Event ) + end + + --- Creation of a S_EVENT_DYNAMIC_CARGO_LOADED event. + -- @param #EVENT self + -- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object + function EVENT:CreateEventDynamicCargoLoaded(DynamicCargo) + self:F({DynamicCargo}) + local Event = { + id = EVENTS.DynamicCargoLoaded, + time = timer.getTime(), + dynamiccargo = DynamicCargo, + initiator = DynamicCargo:GetDCSObject(), + } + world.onEvent( Event ) + end + + --- Creation of a S_EVENT_DYNAMIC_CARGO_UNLOADED event. + -- @param #EVENT self + -- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object + function EVENT:CreateEventDynamicCargoUnloaded(DynamicCargo) + self:F({DynamicCargo}) + local Event = { + id = EVENTS.DynamicCargoUnloaded, + time = timer.getTime(), + dynamiccargo = DynamicCargo, + initiator = DynamicCargo:GetDCSObject(), + } + world.onEvent( Event ) + end + + --- Creation of a S_EVENT_DYNAMIC_CARGO_REMOVED event. + -- @param #EVENT self + -- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object + function EVENT:CreateEventDynamicCargoRemoved(DynamicCargo) + self:F({DynamicCargo}) + local Event = { + id = EVENTS.DynamicCargoRemoved, + time = timer.getTime(), + dynamiccargo = DynamicCargo, + initiator = DynamicCargo:GetDCSObject(), + } + world.onEvent( Event ) + end + end --- Main event function. @@ -1261,7 +1355,13 @@ function EVENT:onEvent( Event ) Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniUnitName = Event.IniDCSUnitName - Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName ) + if string.match(Event.IniUnitName,".+|%d%d:%d%d|PKG%d+") then + Event.IniDynamicCargo = DYNAMICCARGO:FindByName(Event.IniUnitName) + Event.IniDynamicCargoName = Event.IniUnitName + Event.IniPlayerName = string.match(Event.IniUnitName,"^(.+)|%d%d:%d%d|PKG%d+") + else + Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName ) + end Event.IniCoalition = Event.IniDCSUnit:getCoalition() Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniTypeName = Event.IniDCSUnit:getTypeName() @@ -1382,7 +1482,7 @@ function EVENT:onEvent( Event ) end -- Weapon. - if Event.weapon and type(Event.weapon) == "table" then + if Event.weapon and type(Event.weapon) == "table" and Event.weapon.isExist and Event.weapon:isExist() then Event.Weapon = Event.weapon Event.WeaponName = Event.weapon:isExist() and Event.weapon:getTypeName() or "Unknown Weapon" Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit! @@ -1425,6 +1525,13 @@ function EVENT:onEvent( Event ) Event.Cargo = Event.cargo Event.CargoName = Event.cargo.Name end + + -- Dynamic cargo Object + if Event.dynamiccargo then + Event.IniDynamicCargo = Event.dynamiccargo + Event.IniDynamicCargoName = Event.IniDynamicCargo.StaticName + Event.IniPlayerName = Event.IniDynamicCargo.Owner or string.match(Event.IniUnitName,"^(.+)|%d%d:%d%d|PKG%d+") + end -- Zone object. if Event.zone then diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 769598b9c..2a4fb2611 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1811,7 +1811,7 @@ function RANGE:OnEventBirth( EventData ) if not EventData.IniPlayerName then return end local _unitName = EventData.IniUnitName - local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) + local _unit, _playername = self:_GetPlayerUnitAndName( _unitName, EventData.IniPlayerName ) self:T3( self.lid .. "BIRTH: unit = " .. tostring( EventData.IniUnitName ) ) self:T3( self.lid .. "BIRTH: group = " .. tostring( EventData.IniGroupName ) ) @@ -1967,7 +1967,9 @@ end -- @param #number attackAlt Attack altitude. -- @param #number attackVel Attack velocity. function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackVel) - + + if not playerData then return end + -- Get closet target to last position. local _closetTarget = nil -- #RANGE.BombTarget local _distance = nil @@ -1984,13 +1986,13 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV -- Coordinate of impact point. local impactcoord = weapon:GetImpactCoordinate() - -- Check if impact happened in range zone. + -- Check if impact happened in range zone.+ local insidezone = self.rangezone:IsCoordinateInZone( impactcoord ) -- Smoke impact point of bomb. - if playerData.smokebombimpact and insidezone then - if playerData.delaysmoke then + if playerData and playerData.smokebombimpact and insidezone then + if playerData and playerData.delaysmoke then timer.scheduleFunction( self._DelayedSmoke, { coord = impactcoord, color = playerData.smokecolor }, timer.getTime() + self.TdelaySmoke ) else impactcoord:Smoke( playerData.smokecolor ) @@ -2115,7 +2117,7 @@ function RANGE:OnEventShot( EventData ) local _unitName = EventData.IniUnitName -- Get player unit and name. - local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) + local _unit, _playername = self:_GetPlayerUnitAndName( _unitName, EventData.IniPlayerName ) -- Distance Player-to-Range. Set this to larger value than the threshold. local dPR = self.BombtrackThreshold * 2 @@ -2127,11 +2129,13 @@ function RANGE:OnEventShot( EventData ) end -- Only track if distance player to range is < 25 km. Also check that a player shot. No need to track AI weapons. - if _track and dPR <= self.BombtrackThreshold and _unit and _playername then + if _track and dPR <= self.BombtrackThreshold and _unit and _playername and self.PlayerSettings[_playername] then -- Player data. local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData - + + if not playerData then return end + -- Attack parameters. local attackHdg=_unit:GetHeading() local attackAlt=_unit:GetHeight() @@ -4099,8 +4103,8 @@ end -- @return Wrapper.Unit#UNIT Unit of player. -- @return #string Name of the player. -- @return #boolean If true, group has > 1 player in it -function RANGE:_GetPlayerUnitAndName( _unitName ) - self:F2( _unitName ) +function RANGE:_GetPlayerUnitAndName( _unitName, PlayerName ) + self:I( _unitName ) if _unitName ~= nil then @@ -4111,7 +4115,7 @@ function RANGE:_GetPlayerUnitAndName( _unitName ) if DCSunit and DCSunit.getPlayerName then - local playername = DCSunit:getPlayerName() + local playername = DCSunit:getPlayerName() or PlayerName or "None" local unit = UNIT:Find( DCSunit ) self:T2( { DCSunit = DCSunit, unit = unit, playername = playername } ) diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index c08b4c890..58c779ca7 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -48,6 +48,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Marker.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Weapon.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Net.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Storage.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/DynamicCargo.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/Cargo.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoUnit.lua' ) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index d1b89307f..2458911be 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -78,6 +78,7 @@ CTLD_CARGO = { -- @field #string REPAIR -- @field #string ENGINEERS -- @field #string STATIC + -- @field #string GCLOADABLE CTLD_CARGO.Enum = { VEHICLE = "Vehicle", -- #string vehicles TROOPS = "Troops", -- #string troops @@ -86,6 +87,7 @@ CTLD_CARGO = { REPAIR = "Repair", -- #string repair ENGINEERS = "Engineers", -- #string engineers STATIC = "Static", -- #string statics + GCLOADABLE = "GC_Loadable", -- #string dynamiccargo } --- Function to create new CTLD_CARGO object. @@ -770,7 +772,7 @@ do -- my_ctld.nobuildmenu = false -- if set to true effectively enforces to have engineers build/repair stuff for you. -- my_ctld.RadioSound = "beacon.ogg" -- -- this sound will be hearable if you tune in the beacon frequency. Add the sound file to your miz. -- my_ctld.RadioSoundFC3 = "beacon.ogg" -- this sound will be hearable by FC3 users (actually all UHF radios); change to something like "beaconsilent.ogg" and add the sound file to your miz if you don't want to annoy FC3 pilots. --- my_ctld.enableChinookGCLoading = true -- this will effectively suppress the crate load and drop menus for CTLD for the Chinook +-- my_ctld.enableChinookGCLoading = true -- this will effectively suppress the crate load and drop for CTLD_CARGO.Enum.STATIc types for CTLD for the Chinook -- -- ## 2.1 CH-47 Chinook support -- @@ -780,7 +782,7 @@ do -- -- ## 2.1.1 Moose CTLD created crate cargo -- --- Given the correct shape, Moose created cargo can be either loaded with the ground crew or via the F10 CTLD menu. **It is strongly recommend to either use the ground crew or CTLD to load/unload cargo**. Mix and match will not work here. +-- Given the correct shape, Moose created cargo can be either loaded with the ground crew or via the F10 CTLD menu. **It is strongly recommend to either use the ground crew or CTLD to load/unload Moose created cargo**. Mix and match will not work here. -- Static shapes loadable *into* the Chinook are at the time of writing: -- -- * Ammo crate (type "ammo_cargo") @@ -1203,6 +1205,7 @@ CTLD = { wpZones = {}, dropOffZones = {}, pickupZones = {}, + DynamicCargo = {}, } ------------------------------ @@ -1307,7 +1310,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.58" +CTLD.version="1.1.12" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1590,7 +1593,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param #string To State. -- @param Wrapper.Group#GROUP Group Group Object. -- @param Wrapper.Unit#UNIT Unit Unit Object. - -- @param #CTLD_CARGO Cargo Cargo crate. + -- @param #CTLD_CARGO Cargo Cargo crate. Can be a Wrapper.DynamicCargo#DYNAMICCARGO object, if ground crew loaded! -- @return #CTLD self --- FSM Function OnBeforeTroopsDeployed. @@ -1612,7 +1615,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param #string To State. -- @param Wrapper.Group#GROUP Group Group Object. -- @param Wrapper.Unit#UNIT Unit Unit Object. - -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. + -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. Can be a Wrapper.DynamicCargo#DYNAMICCARGO object, if ground crew unloaded! -- @return #CTLD self --- FSM Function OnBeforeCratesBuild. @@ -1678,7 +1681,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param #string To State. -- @param Wrapper.Group#GROUP Group Group Object. -- @param Wrapper.Unit#UNIT Unit Unit Object. - -- @param #CTLD_CARGO Cargo Cargo crate. + -- @param #CTLD_CARGO Cargo Cargo crate. Can be a Wrapper.DynamicCargo#DYNAMICCARGO object, if ground crew loaded! -- @return #CTLD self --- FSM Function OnAfterTroopsDeployed. @@ -1700,7 +1703,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param #string To State. -- @param Wrapper.Group#GROUP Group Group Object. -- @param Wrapper.Unit#UNIT Unit Unit Object. - -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. + -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. Can be a Wrapper.DynamicCargo#DYNAMICCARGO object, if ground crew unloaded! -- @return #CTLD self --- FSM Function OnAfterCratesBuild. @@ -1879,7 +1882,7 @@ end -- @param #CTLD self -- @param Core.Event#EVENTDATA EventData function CTLD:_EventHandler(EventData) - self:I(string.format("%s Event = %d",self.lid, EventData.id)) + self:T(string.format("%s Event = %d",self.lid, EventData.id)) local event = EventData -- Core.Event#EVENTDATA if event.id == EVENTS.PlayerEnterAircraft or event.id == EVENTS.PlayerEnterUnit then local _coalition = event.IniCoalition @@ -1909,28 +1912,98 @@ function CTLD:_EventHandler(EventData) self.CtldUnits[unitname] = nil self.Loaded_Cargo[unitname] = nil self.MenusDone[unitname] = nil - elseif event.id == EVENTS.Birth and event.IniObjectCategory == 6 and string.match(event.IniUnitName,".+|%d%d:%d%d|PKG%d+") then - --UTILS.PrintTableToLog(event) + --elseif event.id == EVENTS.NewDynamicCargo and event.IniObjectCategory == 6 and string.match(event.IniUnitName,".+|%d%d:%d%d|PKG%d+") then + elseif event.id == EVENTS.NewDynamicCargo then + self:T(self.lid.."GC New Event "..event.IniDynamicCargoName) --------------- - -- New dynamic cargo system Handling + -- New dynamic cargo system Handling NEW -------------- - local function RegisterDynamicCargo() - local static = _DATABASE:AddStatic(event.IniUnitName) - if static then - static.DCSCargoObject = event.IniDCSUnit - local Mass = event.IniDCSUnit:getCargoWeight() - local country = event.IniDCSUnit:getCountry() - local template = _DATABASE:_GetGenericStaticCargoGroupTemplate(event.IniUnitName,event.IniTypeName,Mass,event.IniCoalition,country) - _DATABASE:_RegisterStaticTemplate(template,event.IniCoalition,"static",country) - self:I("**** Ground crew created static cargo added: "..event.IniUnitName .." | Weight in kgs: "..Mass) - local cargotype = self:AddStaticsCargo(event.IniUnitName,Mass,1,nil,true) - self.CrateCounter = self.CrateCounter + 1 - self.Spawned_Crates[self.CrateCounter] = static - cargotype.Positionable = static - table.insert(self.Spawned_Cargo, cargotype) + self.DynamicCargo[event.IniDynamicCargoName] = event.IniDynamicCargo + --------------- + -- End new dynamic cargo system Handling + -------------- + elseif event.id == EVENTS.DynamicCargoLoaded then + self:T(self.lid.."GC Loaded Event "..event.IniDynamicCargoName) + --------------- + -- New dynamic cargo system Handling LOADING + -------------- + local dcargo = event.IniDynamicCargo -- Wrapper.DynamicCargo#DYNAMICCARGO + -- get client/unit object + local client = CLIENT:FindByPlayerName(dcargo.Owner) + if client and client:IsAlive() then + -- add to unit load list + local unitname = client:GetName() or "none" + local loaded = {} + if self.Loaded_Cargo[unitname] then + loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo + else + loaded = {} -- #CTLD.LoadedCargo + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} end + loaded.Cratesloaded = loaded.Cratesloaded+1 + table.insert(loaded.Cargo,dcargo) + self.Loaded_Cargo[unitname] = nil + self.Loaded_Cargo[unitname] = loaded + local Group = client:GetGroup() + self:_SendMessage(string.format("Crate %s loaded by ground crew!",event.IniDynamicCargoName), 10, false, Group) + self:__CratesPickedUp(1, Group, client, dcargo) end - self:ScheduleOnce(0.5,RegisterDynamicCargo) + --------------- + -- End new dynamic cargo system Handling + -------------- + elseif event.id == EVENTS.DynamicCargoUnloaded then + self:T(self.lid.."GC Unload Event "..event.IniDynamicCargoName) + --------------- + -- New dynamic cargo system Handling UNLOADING + -------------- + local dcargo = event.IniDynamicCargo -- Wrapper.DynamicCargo#DYNAMICCARGO + -- get client/unit object + local client = CLIENT:FindByPlayerName(dcargo.Owner) + if client and client:IsAlive() then + -- add to unit load list + local unitname = client:GetName() or "none" + local loaded = {} + if self.Loaded_Cargo[unitname] then + loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo + loaded.Cratesloaded = loaded.Cratesloaded - 1 + if loaded.Cratesloaded < 0 then loaded.Cratesloaded = 0 end + -- TODO zap cargo from list + local Loaded = {} + for _,_item in pairs (loaded.Cargo or {}) do + self:T(self.lid.."UNLOAD checking: ".._item:GetName()) + self:T(self.lid.."UNLOAD state: ".. tostring(_item:WasDropped())) + if _item and _item:GetType() == CTLD_CARGO.Enum.GCLOADABLE and event.IniDynamicCargoName and event.IniDynamicCargoName ~= _item:GetName() and not _item:WasDropped() then + table.insert(Loaded,_item) + else + table.insert(Loaded,_item) + end + end + loaded.Cargo = nil + loaded.Cargo = Loaded + self.Loaded_Cargo[unitname] = nil + self.Loaded_Cargo[unitname] = loaded + else + loaded = {} -- #CTLD.LoadedCargo + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} + self.Loaded_Cargo[unitname] = loaded + end + local Group = client:GetGroup() + self:_SendMessage(string.format("Crate %s unloaded by ground crew!",event.IniDynamicCargoName), 10, false, Group) + self:__CratesDropped(1,Group,client,{dcargo}) + end + --------------- + -- End new dynamic cargo system Handling + -------------- + elseif event.id == EVENTS.DynamicCargoRemoved then + self:T(self.lid.."GC Remove Event "..event.IniDynamicCargoName) + --------------- + -- New dynamic cargo system Handling REMOVE + -------------- + self.DynamicCargo[event.IniDynamicCargoName] = nil --------------- -- End new dynamic cargo system Handling -------------- @@ -2173,7 +2246,7 @@ end function CTLD:_FindRepairNearby(Group, Unit, Repairtype) self:T(self.lid .. " _FindRepairNearby") - --self:I({Group:GetName(),Unit:GetName(),Repairtype}) + --self:T({Group:GetName(),Unit:GetName(),Repairtype}) local unitcoord = Unit:GetCoordinate() -- find nearest group of deployed groups @@ -2191,7 +2264,7 @@ function CTLD:_FindRepairNearby(Group, Unit, Repairtype) end end - --self:I("Distance: ".. nearestDistance) + --self:T("Distance: ".. nearestDistance) -- found one and matching distance? if nearestGroup == nil or nearestDistance > self.EngineerSearch then @@ -2225,7 +2298,7 @@ function CTLD:_FindRepairNearby(Group, Unit, Repairtype) -- walk through generics and find matching type local Cargotype = nil for k,v in pairs(self.Cargo_Crates) do - --self:I({groupname,v.Templates,Repairtype}) + --self:T({groupname,v.Templates,Repairtype}) if matchstring(groupname,v.Templates) and matchstring(groupname,Repairtype) then Cargotype = v -- #CTLD_CARGO break @@ -2235,7 +2308,7 @@ function CTLD:_FindRepairNearby(Group, Unit, Repairtype) if Cargotype == nil then return nil, nil else - --self:I({groupname,Cargotype}) + --self:T({groupname,Cargotype}) return nearestGroup, Cargotype end @@ -2713,7 +2786,7 @@ function CTLD:_ListCratesNearby( _group, _unit) end text:Add("------------------------------------------------------------") if indexgc > 0 then - text:Add("Probably ground crew loaded (F8)") + text:Add("Probably ground crew loadable (F8)") for _,_entry in pairs (loadedbygc) do local entry = _entry -- #CTLD_CARGO local name = entry:GetName() --#string @@ -2817,7 +2890,7 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist, _ignoreweight) local capabilities = {} local maxmass = 2000 local maxloadable = 2000 - local IsNoHook = not self:IsHook(_unit) + local IsHook = self:IsHook(_unit) if not _ignoreweight then maxloadable = self:_GetMaxLoadableMass(_unit) end @@ -2828,9 +2901,10 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist, _ignoreweight) local weight = cargo:GetMass() -- weight in kgs of this cargo local staticid = cargo:GetID() self:T(self.lid .. " Found cargo mass: " .. weight) - local cargoalive = false -- TODO dyn cargo spawn workaround - local dcsunit = nil - local dcsunitpos = nil + --local cargoalive = false -- TODO dyn cargo spawn workaround + --local dcsunit = nil + --local dcsunitpos = nil + --[[ if static and static.DCSCargoObject then dcsunit = Unit.getByName(static.StaticName) if dcsunit then @@ -2844,26 +2918,37 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist, _ignoreweight) end end end - if static and (static:IsAlive() or cargoalive) then - local staticpos = static:GetCoordinate() or dcsunitpos + --]] + if static and static:IsAlive() then --or cargoalive) then + local restricthooktononstatics = self.enableChinookGCLoading and IsHook + self:T(self.lid .. " restricthooktononstatics: " .. tostring(restricthooktononstatics)) + local cargoisstatic = cargo:GetType() == CTLD_CARGO.Enum.STATIC and true or false + self:T(self.lid .. " Cargo is static: " .. tostring(cargoisstatic)) + local restricted = cargoisstatic and restricthooktononstatics + self:T(self.lid .. " Loading restricted: " .. tostring(restricted)) + local staticpos = static:GetCoordinate() --or dcsunitpos + --[[ --- Testing local landheight = staticpos:GetLandHeight() local agl = staticpos.y-landheight agl = UTILS.Round(agl,2) local GCloaded = agl > 0 and true or false - if IsNoHook == true then GCloaded = false end + if IsNoHook == true then GCloaded = false end + --]] --- Testing local distance = self:_GetDistance(location,staticpos) - self:T({name=static:GetName(),IsNoHook=IsNoHook,agl=agl,GCloaded=GCloaded,distance=string.format("%.2f",distance or 0)}) - if (not GCloaded) and distance <= finddist and static and (weight <= maxloadable or _ignoreweight) then + --self:T({name=static:GetName(),IsHook=IsHook,agl=agl,GCloaded=GCloaded,distance=string.format("%.2f",distance or 0)}) + --if (not restricthooktononstatics) and distance <= finddist and static and (weight <= maxloadable or _ignoreweight) then + self:T(self.lid .. string.format("Dist %dm/%dm | weight %dkg | maxloadable %dkg",distance,finddist,weight,maxloadable)) + if distance <= finddist and (weight <= maxloadable or _ignoreweight) and restricted == false then index = index + 1 table.insert(found, staticid, cargo) maxloadable = maxloadable - weight + --elseif restricthooktononstatics and distance < 10 and static then + --indexg = indexg + 1 + --table.insert(LoadedbyGC,staticid, cargo) end - if GCloaded == true and distance < 10 and static then - indexg = indexg + 1 - table.insert(LoadedbyGC,staticid, cargo) - end + end end return found, index, LoadedbyGC, indexg @@ -2929,10 +3014,10 @@ function CTLD:_LoadCratesNearby(Group, Unit) if number == 0 and self.hoverautoloading then return self -- exit elseif number == 0 then - self:_SendMessage("Sorry no loadable crates nearby or max cargo weight reached!", 10, false, Group) + self:_SendMessage("Sorry, no loadable crates nearby or max cargo weight reached!", 10, false, Group) return self -- exit elseif numberonboard == cratelimit then - self:_SendMessage("Sorry no fully loaded!", 10, false, Group) + self:_SendMessage("Sorry, we are fully loaded!", 10, false, Group) return self -- exit else -- go through crates and load @@ -3024,9 +3109,13 @@ function CTLD:_GetUnitCargoMass(Unit) if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) and not cargo:WasDropped() then loadedmass = loadedmass + (cargo.PerCrateMass * cargo:GetCratesNeeded()) end - if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and not cargo:WasDropped() then + if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and type ~= CTLD_CARGO.Enum.GCLOADABLE and not cargo:WasDropped() then loadedmass = loadedmass + cargo.PerCrateMass end + if type == CTLD_CARGO.Enum.GCLOADABLE then + local mass = cargo:GetCargoWeight() + loadedmass = loadedmass+mass + end end end return loadedmass @@ -3073,8 +3162,8 @@ function CTLD:_ListCargo(Group, Unit) local loadedmass = self:_GetUnitCargoMass(Unit) -- #number local maxloadable = self:_GetMaxLoadableMass(Unit) local finddist = self.CrateDistance or 35 - local _,_,loadedgc,loadedno = self:_FindCratesNearby(Group,Unit,finddist,true) - if self.Loaded_Cargo[unitname] or loadedno > 0 then + --local _,_,loadedgc,loadedno = self:_FindCratesNearby(Group,Unit,finddist,true) + if self.Loaded_Cargo[unitname] then local no_troops = loadedcargo.Troopsloaded or 0 local no_crates = loadedcargo.Cratesloaded or 0 local cargotable = loadedcargo.Cargo or {} -- #table @@ -3099,17 +3188,22 @@ function CTLD:_ListCargo(Group, Unit) for _,_cargo in pairs(cargotable or {}) do local cargo = _cargo -- #CTLD_CARGO local type = cargo:GetType() -- #CTLD_CARGO.Enum - if (type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS) and (not cargo:WasDropped() or self.allowcratepickupagain) then + if (type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and type ~= CTLD_CARGO.Enum.GCLOADABLE) and (not cargo:WasDropped() or self.allowcratepickupagain) then report:Add(string.format("Crate: %s size 1",cargo:GetName())) cratecount = cratecount + 1 end + if type == CTLD_CARGO.Enum.GCLOADABLE and not cargo:WasDropped() then + report:Add(string.format("GC loaded Crate: %s size 1",cargo:GetName())) + cratecount = cratecount + 1 + end end if cratecount == 0 then report:Add(" N O N E") end + --[[ if loadedno > 0 then report:Add("------------------------------------------------------------") - report:Add(" -- CRATES loaded via F8 --") + report:Add(" -- CRATES loaded via Ground Crew --") for _,_cargo in pairs(loadedgc or {}) do local cargo = _cargo -- #CTLD_CARGO local type = cargo:GetType() -- #CTLD_CARGO.Enum @@ -3119,6 +3213,7 @@ function CTLD:_ListCargo(Group, Unit) end end end + --]] report:Add("------------------------------------------------------------") report:Add("Total Mass: ".. loadedmass .. " kg. Loadable: "..maxloadable.." kg.") local text = report:Text() @@ -3453,7 +3548,7 @@ function CTLD:_UnloadCrates(Group, Unit) for _,_cargo in pairs (cargotable) do local cargo = _cargo -- #CTLD_CARGO local type = cargo:GetType() -- #CTLD_CARGO.Enum - if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and (not cargo:WasDropped() or self.allowcratepickupagain) then + if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and type ~= CTLD_CARGO.Enum.GCLOADABLE and (not cargo:WasDropped() or self.allowcratepickupagain) then -- unload crates self:_GetCrates(Group, Unit, cargo, 1, true) cargo:SetWasDropped(true) @@ -3474,6 +3569,10 @@ function CTLD:_UnloadCrates(Group, Unit) table.insert(loaded.Cargo,_cargo) loaded.Troopsloaded = loaded.Troopsloaded + size end + if type == CTLD_CARGO.Enum.GCLOADABLE and not cargo:WasDropped() then + table.insert(loaded.Cargo,_cargo) + loaded.Cratesloaded = loaded.Cratesloaded + size + end end self.Loaded_Cargo[unitname] = nil self.Loaded_Cargo[unitname] = loaded @@ -3906,7 +4005,8 @@ function CTLD:_RefreshF10Menus() local cantroops = capabilities.troops local cancrates = capabilities.crates local isHook = self:IsHook(_unit) - local nohookswitch = not (isHook and self.enableChinookGCLoading) + --local nohookswitch = not (isHook and self.enableChinookGCLoading) + local nohookswitch = true -- top menu local topmenu = MENU_GROUP:New(_group,"CTLD",nil) local toptroops = nil @@ -5537,7 +5637,11 @@ end self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler) self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) self:HandleEvent(EVENTS.UnitLost, self._EventHandler) - self:HandleEvent(EVENTS.Birth, self._EventHandler) + --self:HandleEvent(EVENTS.Birth, self._EventHandler) + self:HandleEvent(EVENTS.NewDynamicCargo, self._EventHandler) + self:HandleEvent(EVENTS.DynamicCargoLoaded, self._EventHandler) + self:HandleEvent(EVENTS.DynamicCargoUnloaded, self._EventHandler) + self:HandleEvent(EVENTS.DynamicCargoRemoved, self._EventHandler) self:__Status(-5) -- AutoSave @@ -5625,9 +5729,11 @@ end -- @return #CTLD self function CTLD:onafterStop(From, Event, To) self:T({From, Event, To}) - self:UnhandleEvent(EVENTS.PlayerEnterAircraft) - self:UnhandleEvent(EVENTS.PlayerEnterUnit) - self:UnhandleEvent(EVENTS.PlayerLeaveUnit) + self:UnHandleEvent(EVENTS.PlayerEnterAircraft) + self:UnHandleEvent(EVENTS.PlayerEnterUnit) + self:UnHandleEvent(EVENTS.PlayerLeaveUnit) + self:UnHandleEvent(EVENTS.UnitLost) + self:UnHandleEvent(EVENTS.Shot) return self end @@ -5652,7 +5758,7 @@ end -- @param #string To State. -- @param Wrapper.Group#GROUP Group Group Object. -- @param Wrapper.Unit#UNIT Unit Unit Object. - -- @param #CTLD_CARGO Cargo Cargo crate. + -- @param #CTLD_CARGO Cargo Cargo crate. Can be a Wrapper.DynamicCargo#DYNAMICCARGO object, if ground crew loaded! -- @return #CTLD self function CTLD:onbeforeCratesPickedUp(From, Event, To, Group, Unit, Cargo) self:T({From, Event, To}) @@ -5734,7 +5840,7 @@ end -- @param #string To State. -- @param Wrapper.Group#GROUP Group Group Object. -- @param Wrapper.Unit#UNIT Unit Unit Object. - -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. + -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. Can be a Wrapper.DynamicCargo#DYNAMICCARGO object, if ground crew unloaded! -- @return #CTLD self function CTLD:onbeforeCratesDropped(From, Event, To, Group, Unit, Cargotable) self:T({From, Event, To}) diff --git a/Moose Development/Moose/Wrapper/DynamicCargo.lua b/Moose Development/Moose/Wrapper/DynamicCargo.lua new file mode 100644 index 000000000..335687f9a --- /dev/null +++ b/Moose Development/Moose/Wrapper/DynamicCargo.lua @@ -0,0 +1,457 @@ +--- **Wrapper** - Dynamic Cargo create from the F8 menu. +-- +-- ## Main Features: +-- +-- * Convenient access to DCS API functions +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Wrapper/Storage). +-- +-- === +-- +-- ### Author: **Applevangelist** +-- +-- === +-- @module Wrapper.DynamicCargo +-- @image Wrapper_Storage.png + + +--- DYNAMICCARGO class. +-- @type DYNAMICCARGO +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity level. +-- @field #string lid Class id string for output to DCS log file. +-- @field Wrapper.Storage#STORAGE warehouse The STORAGE object. +-- @field #string version. +-- @field #string CargoState. +-- @field #table DCS#Vec3 LastPosition. +-- @field #number Interval Check Interval. 20 secs default. +-- @field #boolean testing +-- @field Core.Timer#TIMER timer Timmer to run intervals +-- @field #string Owner The playername who has created, loaded or unloaded this cargo. Depends on state. +-- @extends Wrapper.Positionable#POSITIONABLE + +--- *The capitalist cannot store labour-power in warehouses after he has bought it, as he may do with the raw material.* -- Karl Marx +-- +-- === +-- +-- # The DYNAMICCARGO Concept +-- +-- The DYNAMICCARGO class offers an easy-to-use wrapper interface to all DCS API functions of DCS dynamically spawned cargo crates. +-- We named the class DYNAMICCARGO, because the name WAREHOUSE is already taken by another MOOSE class.. +-- +-- # Constructor +-- +-- @field #DYNAMICCARGO +DYNAMICCARGO = { + ClassName = "DYNAMICCARGO", + verbose = 0, + testing = false, + Interval = 10, + +} + +--- Liquid types. +-- @type DYNAMICCARGO.Liquid +-- @field #number JETFUEL Jet fuel (0). +-- @field #number GASOLINE Aviation gasoline (1). +-- @field #number MW50 MW50 (2). +-- @field #number DIESEL Diesel (3). +DYNAMICCARGO.Liquid = { + JETFUEL = 0, + GASOLINE = 1, + MW50 = 2, + DIESEL = 3, +} + +--- Liquid Names for the static cargo resource table. +-- @type DYNAMICCARGO.LiquidName +-- @field #number JETFUEL "jet_fuel". +-- @field #number GASOLINE "gasoline". +-- @field #number MW50 "methanol_mixture". +-- @field #number DIESEL "diesel". +DYNAMICCARGO.LiquidName = { + GASOLINE = "gasoline", + DIESEL = "diesel", + MW50 = "methanol_mixture", + JETFUEL = "jet_fuel", +} + +--- Storage types. +-- @type DYNAMICCARGO.Type +-- @field #number WEAPONS weapons. +-- @field #number LIQUIDS liquids. Also see #list<#DYNAMICCARGO.Liquid> for types of liquids. +-- @field #number AIRCRAFT aircraft. +DYNAMICCARGO.Type = { + WEAPONS = "weapons", + LIQUIDS = "liquids", + AIRCRAFT = "aircrafts", +} + +--- State types +-- @type DYNAMICCARGO.State +-- @field #string NEW +-- @field #string LOADED +-- @field #string UNLOADED +-- @field #string REMOVED +DYNAMICCARGO.State = { + NEW = "NEW", + LOADED = "LOADED", + UNLOADED = "UNLOADED", + REMOVED = "REMOVED", +} + +--- Helo types possible. +-- @type DYNAMICCARGO.AircraftTypes +DYNAMICCARGO.AircraftTypes = { + ["CH-47Fbl1"] = "CH-47Fbl1", +} + +--- Helo types possible. +-- @type DYNAMICCARGO.AircraftDimensions +DYNAMICCARGO.AircraftDimensions = { + -- CH-47 model start coordinate is quite exactly in the middle of the model, so half values here + ["CH-47Fbl1"] = { + ["width"] = 4, + ["height"] = 6, + ["length"] = 11, + ["ropelength"] = 30, + }, +} + +--- DYNAMICCARGO class version. +-- @field #string version +DYNAMICCARGO.version="0.0.4" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new DYNAMICCARGO object from the DCS static cargo object. +-- @param #DYNAMICCARGO self +-- @param #string CargoName Name of the Cargo. +-- @return #DYNAMICCARGO self +function DYNAMICCARGO:Register(CargoName) + + -- Inherit everything from a BASE class. + local self=BASE:Inherit(self, POSITIONABLE:New(CargoName)) -- #DYNAMICCARGO + + self.StaticName = CargoName + + self.LastPosition = self:GetCoordinate() + + self.CargoState = DYNAMICCARGO.State.NEW + + self.Interval = DYNAMICCARGO.Interval or 10 + + local DCSObject = self:GetDCSObject() + + if DCSObject then + local warehouse = STORAGE:NewFromDynamicCargo(CargoName) + self.warehouse = warehouse + end + + self.lid = string.format("DYNAMICCARGO %s", CargoName) + + self.Owner = string.match(CargoName,"^(.+)|%d%d:%d%d|PKG%d+") or "None" + + self.timer = TIMER:New(DYNAMICCARGO._UpdatePosition,self) + self.timer:Start(self.Interval,self.Interval) + + if not _DYNAMICCARGO_HELOS then + _DYNAMICCARGO_HELOS = SET_CLIENT:New():FilterAlive():FilterFunction(DYNAMICCARGO._FilterHeloTypes):FilterStart() + end + + if self.testing then + BASE:TraceOn() + BASE:TraceClass("DYNAMICCARGO") + end + + return self +end + +--- Get DCS object. +-- @param #DYNAMICCARGO self +-- @return DCS static object +function DYNAMICCARGO:GetDCSObject() + local DCSStatic = Unit.getByName( self.StaticName ) + if DCSStatic then + return DCSStatic + end + return nil +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User API Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get last know owner (player) name of this DYNAMICCARGO +-- @param #DYNAMICCARGO self +-- @return DCS#Vec3 Position in 3D space +function DYNAMICCARGO:GetLastPosition() + return self.Owner +end + +--- [CTLD] Get number of crates this DYNAMICCARGO consists of. Always one. +-- @param #DYNAMICCARGO self +-- @return #number crate number, always one +function DYNAMICCARGO:GetCratesNeeded() + return 1 +end + +--- [CTLD] Get this DYNAMICCARGO drop state. True if DYNAMICCARGO.State.UNLOADED +-- @param #DYNAMICCARGO self +-- @return #boolean Dropped +function DYNAMICCARGO:WasDropped() + return self.CargoState == DYNAMICCARGO.State.UNLOADED and true or false +end + +--- [CTLD] Get CTLD_CARGO.Enum type of this DYNAMICCARGO +-- @param #DYNAMICCARGO self +-- @return #string Type, only one at the moment is CTLD_CARGO.Enum.GCLOADABLE +function DYNAMICCARGO:GetType() + return CTLD_CARGO.Enum.GCLOADABLE +end + + +--- Find last known position of this DYNAMICCARGO +-- @param #DYNAMICCARGO self +-- @return DCS#Vec3 Position in 3D space +function DYNAMICCARGO:GetLastPosition() + return self.LastPosition +end + +--- Find current state of this DYNAMICCARGO +-- @param #DYNAMICCARGO self +-- @return string The current state +function DYNAMICCARGO:GetState() + return self.CargoState +end + +--- Find a DYNAMICCARGO in the **_DATABASE** using the name associated with it. +-- @param #DYNAMICCARGO self +-- @param #string Name The dynamic cargo name +-- @return #DYNAMICCARGO self +function DYNAMICCARGO:FindByName( Name ) + local storage = _DATABASE:FindDynamicCargo( Name ) + return storage +end + +--- Find the first(!) DYNAMICCARGO matching using patterns. Note that this is **a lot** slower than `:FindByName()`! +-- @param #DYNAMICCARGO self +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA. +-- @return #DYNAMICCARGO The DYNAMICCARGO. +-- @usage +-- -- Find a dynamic cargo with a partial dynamic cargo name +-- local grp = DYNAMICCARGO:FindByMatching( "Apple" ) +-- -- will return e.g. a dynamic cargo named "Apple|08:00|PKG08" +-- +-- -- using a pattern +-- local grp = DYNAMICCARGO:FindByMatching( ".%d.%d$" ) +-- -- will return the first dynamic cargo found ending in "-1-1" to "-9-9", but not e.g. "-10-1" +function DYNAMICCARGO:FindByMatching( Pattern ) + local GroupFound = nil + + for name,static in pairs(_DATABASE.DYNAMICCARGO) do + if string.match(name, Pattern ) then + GroupFound = static + break + end + end + + return GroupFound +end + +--- Find all DYNAMICCARGO objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`! +-- @param #DYNAMICCARGO self +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA. +-- @return #table Groups Table of matching #DYNAMICCARGO objects found +-- @usage +-- -- Find all dynamic cargo with a partial dynamic cargo name +-- local grptable = DYNAMICCARGO:FindAllByMatching( "Apple" ) +-- -- will return all dynamic cargos with "Apple" in the name +-- +-- -- using a pattern +-- local grp = DYNAMICCARGO:FindAllByMatching( ".%d.%d$" ) +-- -- will return the all dynamic cargos found ending in "-1-1" to "-9-9", but not e.g. "-10-1" or "-1-10" +function DYNAMICCARGO:FindAllByMatching( Pattern ) + local GroupsFound = {} + + for name,static in pairs(_DATABASE.DYNAMICCARGO) do + if string.match(name, Pattern ) then + GroupsFound[#GroupsFound+1] = static + end + end + + return GroupsFound +end + +--- Get the #STORAGE object from this dynamic cargo. +-- @param #DYNAMICCARGO self +-- @return Wrapper.Storage#STORAGE Storage The #STORAGE object +function DYNAMICCARGO:GetStorageObject() + return self.warehouse +end + +--- Get the weight in kgs from this dynamic cargo. +-- @param #DYNAMICCARGO self +-- @return #number Weight in kgs. +function DYNAMICCARGO:GetCargoWeight() + local DCSObject = self:GetDCSObject() + if DCSObject then + local weight = DCSObject:getCargoWeight() + return weight + else + return 0 + end +end + +--- Get the cargo display name from this dynamic cargo. +-- @param #DYNAMICCARGO self +-- @return #string The display name +function DYNAMICCARGO:GetCargoDisplayName() + local DCSObject = self:GetDCSObject() + if DCSObject then + local weight = DCSObject:getCargoDisplayName() + return weight + else + return self.StaticName + end +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- [Internal] _Get Possible Player Helo Nearby +-- @param #DYNAMICCARGO self +-- @param Core.Point#COORDINATE pos +-- @param #boolean loading If true measure distance for loading else for unloading +-- @return #boolean Success +-- @return Wrapper.Client#CLIENT Helo +-- @return #string PlayerName +function DYNAMICCARGO:_GetPossibleHeloNearby(pos,loading) + local set = _DYNAMICCARGO_HELOS:GetAliveSet() + local success = false + local Helo = nil + local Playername = nil + for _,_helo in pairs (set or {}) do + local helo = _helo -- Wrapper.Client#CLIENT + local name = helo:GetPlayerName() or _DATABASE:_FindPlayerNameByUnitName(helo:GetName()) or "None" + self:T(self.lid.." Checking: "..name) + local hpos = helo:GetCoordinate() + -- TODO Unloading via sling load? + --local inair = hpos.y-hpos:GetLandHeight() > 4.5 and true or false -- Standard FARP is 4.5m + local inair = helo:InAir() + self:T(self.lid.." InAir: AGL/InAir: "..hpos.y-hpos:GetLandHeight().."/"..tostring(inair)) + local typename = helo:GetTypeName() + if hpos and typename and inair == false then + local dimensions = DYNAMICCARGO.AircraftDimensions[typename] + if dimensions then + local delta2D = hpos:Get2DDistance(pos) + local delta3D = hpos:Get3DDistance(pos) + if self.testing then + self:T(string.format("Cargo relative position: 2D %dm | 3D %dm",delta2D,delta3D)) + self:T(string.format("Helo dimension: length %dm | width %dm | rope %dm",dimensions.length,dimensions.width,dimensions.ropelength)) + end + if loading~=true and delta2D > dimensions.length or delta2D > dimensions.width or delta3D > dimensions.ropelength then + success = true + Helo = helo + Playername = name + end + if loading == true and delta2D < dimensions.length or delta2D < dimensions.width or delta3D < dimensions.ropelength then + success = true + Helo = helo + Playername = name + end + end + end + end + return success,Helo,Playername +end + +--- [Internal] Update internal states. +-- @param #DYNAMICCARGO self +-- @return #DYNAMICCARGO self +function DYNAMICCARGO:_UpdatePosition() + self:T(self.lid.." _UpdatePositionAndState") + if self:IsAlive() then + local pos = self:GetCoordinate() + if self.testing then + self:T(string.format("Cargo position: x=%d, y=%d, z=%d",pos.x,pos.y,pos.z)) + self:T(string.format("Last position: x=%d, y=%d, z=%d",self.LastPosition.x,self.LastPosition.y,self.LastPosition.z)) + end + if UTILS.Round(UTILS.VecDist3D(pos,self.LastPosition),2) > 0.5 then + --------------- + -- LOAD Cargo + --------------- + if self.CargoState == DYNAMICCARGO.State.NEW then + local isloaded, client, playername = self:_GetPossibleHeloNearby(pos,true) + self:T(self.lid.." moved! NEW -> LOADED by "..tostring(playername)) + self.CargoState = DYNAMICCARGO.State.LOADED + self.Owner = playername + _DATABASE:CreateEventDynamicCargoLoaded(self) + --------------- + -- UNLOAD Cargo + --------------- + elseif self.CargoState == DYNAMICCARGO.State.LOADED then + -- TODO add checker if we are in flight somehow + -- ensure not just the helo is moving + local count = _DYNAMICCARGO_HELOS:CountAlive() + -- Testing + local landheight = pos:GetLandHeight() + local agl = pos.y-landheight + agl = UTILS.Round(agl,2) + self:T(self.lid.." AGL: "..agl or -1) + local isunloaded = true + local client + local playername + if count > 0 and (agl > 0 or self.testing) then + self:T(self.lid.." Possible alive helos: "..count or -1) + if agl ~= 0 or self.testing then + isunloaded, client, playername = self:_GetPossibleHeloNearby(pos,false) + end + if isunloaded then + self:T(self.lid.." moved! LOADED -> UNLOADED by "..tostring(playername)) + self.CargoState = DYNAMICCARGO.State.UNLOADED + self.Owner = playername + _DATABASE:CreateEventDynamicCargoUnloaded(self) + end + end + end + self.LastPosition = pos + end + else + --------------- + -- REMOVED Cargo + --------------- + if self.timer and self.timer:IsRunning() then self.timer:Stop() end + self:T(self.lid.." dead! " ..self.CargoState.."-> REMOVED") + self.CargoState = DYNAMICCARGO.State.REMOVED + _DATABASE:CreateEventDynamicCargoRemoved(self) + end + return self +end + +--- [Internal] Track helos for loaded/unloaded decision making. +-- @param Wrapper.Client#CLIENT client +-- @return #boolean IsIn +function DYNAMICCARGO._FilterHeloTypes(client) + if not client then return false end + local typename = client:GetTypeName() + local isinclude = DYNAMICCARGO.AircraftTypes[typename] ~= nil and true or false + return isinclude +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua index af3863fa8..a2b553019 100644 --- a/Moose Development/Moose/Wrapper/Net.lua +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -528,6 +528,7 @@ function NET:SendChatToPlayer(Message, ToPlayer, FromPlayer) return self end +--[[ not in 2.97 MSE any longer --- Load a specific mission. -- @param #NET self -- @param #string Path and Mission @@ -550,6 +551,7 @@ function NET:LoadNextMission() outcome = net.load_next_mission() return outcome end +--]] --- Return a table of players currently connected to the server. -- @param #NET self diff --git a/Moose Development/Moose/Wrapper/Storage.lua b/Moose Development/Moose/Wrapper/Storage.lua index 9e750d779..15cbf995d 100644 --- a/Moose Development/Moose/Wrapper/Storage.lua +++ b/Moose Development/Moose/Wrapper/Storage.lua @@ -226,6 +226,26 @@ function STORAGE:NewFromStaticCargo(StaticCargoName) return self end +--- Create a new STORAGE object from an DCS static cargo object. +-- @param #STORAGE self +-- @param #string DynamicCargoName Unit name of the dynamic cargo. +-- @return #STORAGE self +function STORAGE:NewFromDynamicCargo(DynamicCargoName) + + -- Inherit everything from BASE class. + local self=BASE:Inherit(self, BASE:New()) -- #STORAGE + + self.airbase=Unit.getByName(DynamicCargoName) + + if Airbase.getWarehouse then + self.warehouse=Warehouse.getCargoAsWarehouse(self.airbase) + end + + self.lid = string.format("STORAGE %s", DynamicCargoName) + + return self +end + --- Airbases only - Find a STORAGE in the **_DATABASE** using the name associated airbase. -- @param #STORAGE self diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 863f03e0f..e75f1f874 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -1500,7 +1500,7 @@ function UNIT:InAir(NoHeloCheck) local UnitCategory = DCSUnit:getDesc().category -- If DCS says that it is in air, check if this is really the case, since we might have landed on a building where inAir()=true but actually is not. - -- This is a workaround since DCS currently does not acknoledge that helos land on buildings. + -- This is a workaround since DCS currently does not acknowledge that helos land on buildings. -- Note however, that the velocity check will fail if the ground is moving, e.g. on an aircraft carrier! if UnitInAir==true and UnitCategory == Unit.Category.HELICOPTER and (not NoHeloCheck) then local VelocityVec3 = DCSUnit:getVelocity() diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index aeebb3634..1f41c41a6 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -49,6 +49,7 @@ Wrapper/Marker.lua Wrapper/Weapon.lua Wrapper/Net.lua Wrapper/Storage.lua +Wrapper/DynamicCargo.lua Cargo/Cargo.lua Cargo/CargoUnit.lua