From 76ce28cdccb26af19cdfb79760b4a7e5af65e7d2 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 18 Aug 2018 01:29:19 +0200 Subject: [PATCH] Warehouse 0.20 Capture update --- Moose Development/Moose/Core/Point.lua | 6 +- .../Moose/Functional/Warehouse.lua | 322 +++++++++++------- 2 files changed, 195 insertions(+), 133 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index d2d9884a6..77aedec5c 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -397,7 +397,7 @@ do -- COORDINATE local gotunits=false local gotscenery=false - local function EvaluateZone( ZoneObject ) + local function EvaluateZone(ZoneObject) if ZoneObject then @@ -408,7 +408,7 @@ do -- COORDINATE --if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive()) then if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist()) then - table.insert(Units, ZoneObject) + table.insert(Units, UNIT:Find(ZoneObject)) gotunits=true elseif (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then @@ -432,7 +432,7 @@ do -- COORDINATE world.searchObjects(scanobjects, SphereSearch, EvaluateZone) for _,unit in pairs(Units) do - self:T(string.format("Scan found unit %s", unit:getName())) + self:T(string.format("Scan found unit %s", unit:GetName())) end for _,static in pairs(Statics) do self:T(string.format("Scan found static %s", static:getName())) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d9eea2a1f..ac3aa3cff 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -34,10 +34,10 @@ -- @field Core.Point#COORDINATE road Closest point to warehouse on road. -- @field Core.Point#COORDINATE rail Closest point to warehouse on rail. -- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. --- @field Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION capturezone Zone capture object handling the capturing of the warehouse spawn zone. -- @field #string wid Identifier of the warehouse printed before other output to DCS.log file. -- @field #number uid Unit identifier of the warehouse. Derived from the associated airbase. -- @field #number markerid ID of the warehouse marker at the airbase. +-- @field #number dTstatus Time interval in seconds of updating the warehouse status and processing new events. Default 30 seconds. -- @field #number assetid Unique id of asset items in stock. Essentially a running number starting at one and incremented when a new asset is added. -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stockitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. @@ -88,28 +88,28 @@ WAREHOUSE = { ClassName = "WAREHOUSE", Debug = false, - Report = true, - warehouse = nil, - coalition = nil, - country = nil, - alias = nil, - zone = nil, - airbase = nil, - airbasename = nil, - category = -1, - coordinate = nil, - road = nil, - rail = nil, - spawnzone = nil, - capturezone = nil, - wid = nil, - uid = nil, - markerid = nil, - assetid = 0, - queueid = 0, - stock = {}, - queue = {}, - pending = {}, + Report = true, + warehouse = nil, + coalition = nil, + country = nil, + alias = nil, + zone = nil, + airbase = nil, + airbasename = nil, + category = -1, + coordinate = nil, + road = nil, + rail = nil, + spawnzone = nil, + wid = nil, + uid = nil, + markerid = nil, + dTstatus = 30, + assetid = 0, + queueid = 0, + stock = {}, + queue = {}, + pending = {}, } --- Item of the warehouse stock table. @@ -196,7 +196,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.9w" +WAREHOUSE.version="0.2.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -230,8 +230,6 @@ WAREHOUSE.version="0.1.9w" -- @param #WAREHOUSE self -- @param Wrapper.Static#STATIC warehouse The physical structure of the warehouse. -- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static --- @param Core.Zone#ZONE spawnzone (Optional) The zone in which units are spawned and despawned when they leave or arrive the warehouse. Default is a zone of 200 meters around the warehouse. --- @param Wrapper.Airbase#AIRBASE airbase (Optional) The airbase belonging to the warehouse. Default is the closest airbase to the warehouse structure as long as it within a range of 3 km. -- @return #WAREHOUSE self function WAREHOUSE:New(warehouse, alias) BASE:E({warehouse=warehouse:GetName()}) @@ -279,15 +277,16 @@ function WAREHOUSE:New(warehouse, alias) -- Add FSM transitions. self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. - self:AddTransition("Running", "Status", "*") -- Status update in running mode. Requests are processed. - self:AddTransition("Paused", "Status", "*") -- TODO Status update in paused mode. Requests are not processed. + self:AddTransition("*", "Status", "*") -- Status update. self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. + self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. @@ -428,6 +427,15 @@ end -- User functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Set interval of status updates +-- @param #WAREHOUSE self +-- @param #number timeinterval Time interval in seconds. +-- @return #WAREHOUSE self +function WAREHOUSE:SetSpawnZone(timeinterval) + self.dTstatus=timeinterval + return self +end + --- Set a zone where the (ground) assets of the warehouse are spawned once requested. -- @param #WAREHOUSE self -- @param Core.Zone#ZONE zone The spawn zone. @@ -572,32 +580,6 @@ function WAREHOUSE:onafterStart(From, Event, To) if self.rail then self.rail:MarkToAll(string.format("%s rail connection.", self.alias), true) end - - -- Create a zone capture object. - self.capturezone=ZONE_CAPTURE_COALITION:New(self.zone, self.coalition) - - -- Add warehouse to zone capture object. Does this work? - self.capturezone.warehouse=self - - -- Start capturing monitoring. - self.capturezone:Start(10, 60) - - -- Handle attack. - function self.capturezone:OnEnterAttacked() - local coalition = self:GetCoalition() - self:E(string.format("Warehouse %s is under attack!", tostring(self.warehouse.alias))) - -- Trigger FSM Attacked event. - self.warehouse:Attacked() - end - - -- Handle capturing. - function self.capturezone:OnEnterCaptured() - local coalition = self:GetCoalition() - self:E(string.format("Warehouse %s was captured by coalition %d!", tostring(self.warehouse.alias), coalition)) - self.warehouse.coalition=coalition --:SetCoalition(coalition) - self.warehouse:Captured(coalition) - self:Guard() - end -- Handle events: self:HandleEvent(EVENTS.Birth, self._OnEventBirth) @@ -616,7 +598,7 @@ function WAREHOUSE:onafterStart(From, Event, To) self:HandleEvent(EVENTS.EngineShutdown, self._OnEventArrived) -- Start the status monitoring. - self:__Status(5) + self:__Status(1) end --- On after "Stop" event. Stops the warehouse, unhandles all events. @@ -637,8 +619,6 @@ function WAREHOUSE:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.BaseCaptured) - -- Stop capture zone FSM. - self.capturezone:Stop() end --- On after "Pause" event. Pauses the warehouse, i.e. no requests are processed. However, new requests and new assets can be added in this state. @@ -669,32 +649,11 @@ end function WAREHOUSE:onafterStatus(From, Event, To) self:E(self.wid..string.format("Checking warehouse status of %s", self.alias)) - -- Print queue. - self:_PrintQueue(self.queue, "Queue0:") - self:_PrintQueue(self.pending, "Pending0:") - - -- Create a mark with the current assets in stock. - if self.markerid~=nil then - trigger.action.removeMark(self.markerid) - end - local marktext="Warehouse stock:\n" - local text="Warehouse stock:\n" - - local _data=self:GetStockInfo(self.stock) - for _attribute,_count in pairs(_data) do - marktext=marktext..string.format("%s=%d, ", _attribute,_count) -- Dont use \n because too many make DCS crash! - text=text..string.format("%s = %d\n", _attribute,_count) - end - self.markerid=self.coordinate:MarkToCoalition(marktext, self.coalition, true) - - -- Debug output. - self:E(self.wid..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) - - -- Display complete list of stock itmes. - if self.Debug then - --self:_DisplayStockItems(self.stock) - end + -- Print status. + self:_DisplayStatus() + + -- Check if warehouse is being attacked or has even been captured. + self:_CheckConquered() -- Print queue. self:_PrintQueue(self.queue, "Queue:") @@ -702,14 +661,9 @@ function WAREHOUSE:onafterStatus(From, Event, To) -- Check if requests are valid and remove invalid one. self:_CheckRequestConsistancy(self.queue) - - -- Print queue. - self:_PrintQueue(self.queue, "Queue after consitancy:") - self:_PrintQueue(self.pending, "Pending after consistancy:") - - + -- If warehouse is running than requests can be processed. - if self:IsRunning() then + if self:IsRunning() or self:IsAttacked() then -- Check queue and handle requests if possible. local request=self:_CheckQueue() @@ -719,12 +673,16 @@ function WAREHOUSE:onafterStatus(From, Event, To) end end - -- Print queue. - self:_PrintQueue(self.queue, "Queue after request:") - self:_PrintQueue(self.pending, "Pending after request:") + -- Update warhouse marker on F10 map. + self:_UpdateWarehouseMarkText() + + -- Display complete list of stock itmes. + if self.Debug then + --self:_DisplayStockItems(self.stock) + end - -- Call status again in 30 sec. - self:__Status(30) + -- Call status again in ~30 sec (user choice). + self:__Status(self.dTstatus) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -874,7 +832,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) local distance=self.coordinate:Get2DDistance(Request.warehouse.coordinate) -- Filter the requested assets. - local _assets=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + local _assets,_nasset,_enough=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) -- Check if destination is in range for all requested assets. for _,_asset in pairs(_assets) do @@ -896,8 +854,8 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) end -- Asset is not in stock ==> request denied. - if #_assets < Request.nasset then - local text=string.format("Request denied! Not enough assets currently in stock. Requested %d < %d in stock.", Request.nasset, #_assets) + if not _enough then + local text=string.format("Request denied! Not enough assets currently in stock. Requested %s < %d in stock.", tostring(Request.nasset), _nasset) MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) return false @@ -967,8 +925,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Self request! Assets are only spawned but not routed or transported anywhere. if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then - env.info("FF selfrequest!") - self:__SelfRequest(_spawngroups) + self:E(self.wid..string.format("Selfrequest! Current status %s", self:GetState())) + self:__SelfRequest(1,_spawngroups, Pending) return end @@ -1254,10 +1212,10 @@ end function WAREHOUSE:_SpawnAssetRequest(Request) -- Filter the requested cargo assets. - local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + local _assetstock,_nasset,_enough=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) -- No assets in stock :( - if #_assetstock==0 then + if not _enough then return nil,nil,nil end @@ -1287,7 +1245,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) local _assets={} -- Loop over cargo requests. - for i=1,Request.nasset do + for i=1,#_assetstock do -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem @@ -1405,7 +1363,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) local ncargo=request.cargogroupset:Count() -- Info - self:E(self.wid..string.format("Cargo %d of %d arrived at warehouse %s. Assets still to deliver %d.",request.ndelivered, request.nasset, request.warehouse.alias, ncargo)) + self:E(self.wid..string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.",request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo)) -- Move asset into new warehouse. -- TODO: need to figure out which template group name I best take. @@ -1508,8 +1466,9 @@ end -- @param DCS#coalition.side Coalition which is attacking the warehouse. -- @param DCS#country.id Country which is attacking the warehouse. function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) - self:E(self.wid..string.format("Out warehouse is under attack!")) + self:E(self.wid..string.format("Our warehouse is under attack!")) --TODO: Spawn all ground units in the spawnzone? + self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) end --- On after "Defeated" event. Warehouse defeated an attack by another coalition. @@ -1911,6 +1870,7 @@ end --- Checks if the warehouse zone was conquered by antoher coalition. -- @param #WAREHOUSE self function WAREHOUSE:_CheckConquered() + env.info("FF checking conq") local coord=self.zone:GetCoordinate() local radius=self.zone:GetRadius() @@ -1949,6 +1909,9 @@ function WAREHOUSE:_CheckConquered() end end + + -- Debug info. + self:E(self.wid..string.format("Troops at warehouse: blue=%d, red=%d, neutral=%d", Nblue, Nred, Nneutral)) -- Figure out the new coalition if any. @@ -1979,7 +1942,7 @@ function WAREHOUSE:_CheckConquered() if self.coalition==coalition.side.BLUE then -- Blue warehouse is running and we have red units in the zone. if self:IsRunning() and Nred>0 then - self:__Attacked(coalition.side.RED, CountryRed) + self:Attacked(coalition.side.RED, CountryRed) end -- Blue warehouse was under attack by blue but no more blue units in zone. if self:IsAttacked() and Nred==0 then @@ -1988,7 +1951,7 @@ function WAREHOUSE:_CheckConquered() elseif self.coalition==coalition.side.RED then -- Red Warehouse is running and we have blue units in the zone. if self:IsRunning() and Nblue>0 then - self:__Attacked(coalition.side.BLUE, CountryBlue) + self:Attacked(coalition.side.BLUE, CountryBlue) end -- Red warehouse was under attack by blue but no more blue units in zone. if self:IsAttacked() and Nblue==0 then @@ -2248,10 +2211,10 @@ function WAREHOUSE:_CheckRequestNow(request) local okay=true -- Check if number of requested assets is in stock. - local _assets=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) + local _assets,_nassets,_enough=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) -- Nothing in stock. - if #_assets==0 then + if _nassets==0 then local text=string.format("Request denied! No assets for this request currently available.") MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) @@ -2263,7 +2226,7 @@ function WAREHOUSE:_CheckRequestNow(request) local _assetcategory=_assets[1].category -- Check if enough assets are in stock. - if request.nasset > #_assets then + if not _enough then local text=string.format("Request denied! Not enough assets currently available.") MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) @@ -2566,40 +2529,55 @@ end -- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. -- @param #string item Descriptor -- @param value Value of the descriptor. --- @param #number nmax (Optional) Maximum number of items that will be returned. Default is all matching items are returned. +-- @param #number nmax (Optional) Maximum number of items that will be returned. Default nmax=nil is all matching items are returned. -- @return #table Filtered stock items table. +-- @return #number Total number of (requested) assets available. +-- @return #boolean Enough assets are available. function WAREHOUSE:_FilterStock(stock, item, value, nmax) + -- Default all. + nmax=nmax or "all" + -- Filtered array. local filtered={} + -- Count total number in stock. + local ntot=0 + for _,_stock in ipairs(stock) do + if _stock[item]==value then + ntot=ntot+1 + end + end + + -- Handle string input for nmax. + if type(nmax)=="string" then + if nmax:lower()=="all" then + nmax=ntot + elseif nmax:lower()=="half" then + nmax=ntot/2 + elseif nmax:lower()=="third" then + nmax=ntot/3 + elseif namx:lower()=="quarter" then + nmax=ntot/4 + elseif namx:lower()=="fivth" then + nmax=ntot/5 + else + nmax=math.min(1,ntot) + end + end + -- Loop over stock items. for _i,_stock in ipairs(stock) do if _stock[item]==value then _stock.pos=_i table.insert(filtered, _stock) if nmax~=nil and #filtered>=nmax then - return filtered + return filtered, ntot, true end end end - return filtered -end - ---- Filter stock assets by table entry. --- @param #WAREHOUSE self --- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. -function WAREHOUSE:_DisplayStockItems(stock) - - local text=self.wid..string.format("Warehouse %s stock assets:\n", self.airbase:GetName()) - for _,_stock in pairs(stock) do - local mystock=_stock --#WAREHOUSE.Stockitem - text=text..string.format("template = %s, category = %d, unittype = %s, attribute = %s\n", mystock.templatename, mystock.category, mystock.unittype, mystock.attribute) - end - - env.info(text) - MESSAGE:New(text, 10):ToAll() + return filtered, ntot, nmax>=ntot end --- Check if a group has a generalized attribute. @@ -2768,13 +2746,97 @@ function WAREHOUSE:_PrintQueue(queue, name) self:E(self.wid..name) for _,_qitem in ipairs(queue) do local qitem=_qitem --#WAREHOUSE.Queueitem - local text=self.wid..string.format("UID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%d, Transport=%s, Ntransport=%d", - qitem.uid, qitem.prio, qitem.warehouse.alias, qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval),qitem.nasset,qitem.transporttype,qitem.ntransport) + local text=self.wid..string.format("UID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%s, Transport=%s, Ntransport=%d", + qitem.uid, qitem.prio, qitem.warehouse.alias, qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval), tostring(qitem.nasset),qitem.transporttype,qitem.ntransport) self:E(text) end end +--- Display status of warehouse. +-- @param #WAREHOUSE self +function WAREHOUSE:_DisplayStatus() + -- Set airbase name. + local airbasename="none" + if self.airbase then + airbasename=self.airbase:GetName() + end + + local text=string.format("\n------------------------------------------------------\n") + text=text..string.format("Warehouse %s status:\n", self.alias) + text=text..string.format("------------------------------------------------------\n") + text=text..string.format("Current status = %s\n", self:GetState()) + text=text..string.format("Coalition side = %d\n", self.coalition) + text=text..string.format("Country name = %d\n", self.country) + text=text..string.format("Airbase name = %s\n", airbasename) + text=text..string.format("Queued requests = %d\n", #self.queue) + text=text..string.format("Pending requests = %d\n", #self.pending) + text=text..string.format("------------------------------------------------------\n") + text=text..self:_GetStockAssetsText() + env.info(text) + --TODO: number of ground, air, naval assets. +end + +--- Get text about warehouse stock. +-- @param #WAREHOUSE self +-- @param #boolean messagetoall If true, send message to all. +-- @return #string Text about warehouse stock +function WAREHOUSE:_GetStockAssetsText(messagetoall) + + -- Get assets in stock. + local _data=self:GetStockInfo(self.stock) + + -- Text. + local text="Stock:\n" + for _attribute,_count in pairs(_data) do + text=text..string.format("%s = %d\n", _attribute,_count) + end + text=text..string.format("------------------------------------------------------\n") + + -- Send message? + MESSAGE:New(text, 10):ToAllIf(messagetoall) + + return text +end + +--- Create or update mark text at warehouse, which is displayed in F10 map. +-- Only the coaliton of the warehouse owner is able to see it. +-- @param #WAREHOUSE self +-- @return #string Text about warehouse stock +function WAREHOUSE:_UpdateWarehouseMarkText() + + -- Create a mark with the current assets in stock. + if self.markerid~=nil then + trigger.action.removeMark(self.markerid) + end + + -- Get assets in stock. + local _data=self:GetStockInfo(self.stock) + + -- Create mark text. + local marktext="Warehouse stock:\n" + for _attribute,_count in pairs(_data) do + marktext=marktext..string.format("%s=%d, ", _attribute,_count) -- Dont use \n because too many make DCS crash! + end + + -- Create/update marker at warehouse in F10 map. + self.markerid=self.coordinate:MarkToCoalition(marktext, self.coalition, true) +end + +--- Display stock items of warehouse. +-- @param #WAREHOUSE self +-- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. +function WAREHOUSE:_DisplayStockItems(stock) + + local text=self.wid..string.format("Warehouse %s stock assets:\n", self.airbase:GetName()) + for _,_stock in pairs(stock) do + local mystock=_stock --#WAREHOUSE.Stockitem + text=text..string.format("template = %s, category = %d, unittype = %s, attribute = %s\n", mystock.templatename, mystock.category, mystock.unittype, mystock.attribute) + end + + env.info(text) + MESSAGE:New(text, 10):ToAll() +end --- Make a flight plan from a departure to a destination airport. -- @param #WAREHOUSE self