Buildable (#36)
- Buildable missions now working. - FARPS and BLUESAM zones are now buildable. - Secondary Logistics missions are created as buildables become available. - Logistic missions can be "marked" with a MAP marker. - Supplies can only be picked up in a SUPPLYHUB zone. - SUPPLYHUB zones are active once their parents are active. A supplyhub parent can be oneof: FARP, AIRBASE, STAGE. - Required supplies for a zone can be configured in kilos. - Crates can be picked up with 1000 or 2000 kilo per crate. - Max load for a helicopter is set based on public data. - TO COME: Sling load options
This commit is contained in:
+8
-5
@@ -25,9 +25,12 @@
|
||||
---@field x number directed to the north
|
||||
---@field y number directed to the east
|
||||
---@field z number directed up (away from ground)
|
||||
|
||||
---@class Position : Vec3
|
||||
|
||||
---@class Position
|
||||
---@field p Vec3 Point on map
|
||||
---@field x Vec3 Direction vector
|
||||
---@field y Vec3 Direction vector
|
||||
---@field z Vec3 Direction vector
|
||||
|
||||
---@class Array<T>: { [number]: T }
|
||||
|
||||
@@ -216,14 +219,14 @@ do -- world
|
||||
---@field min Vec3
|
||||
---@field max Vec3
|
||||
|
||||
---@class Sphere
|
||||
---@class Sphere : Volume
|
||||
---@field params SphereParams
|
||||
|
||||
---@class SphereParams
|
||||
---@field point Vec3
|
||||
---@field radius number
|
||||
|
||||
---@class VolumePyramid
|
||||
---@class VolumePyramid : Volume
|
||||
---@field params VolumePyramidParams
|
||||
|
||||
---@class VolumePyramidParams
|
||||
@@ -348,7 +351,7 @@ do -- trigger
|
||||
---@class MarkCommands
|
||||
---@field markToAll fun(id: number, text:string, point:Vec3, readOnly: boolean?, message: string?) Adds a mark point to all on the F10 map with attached text.
|
||||
---@field markToCoalition fun(id: number, text: string, point: Vec3, coalitionID: CoalitionSide, readOnly: boolean?, message: string) Adds a mark point to a coalition on the F10 map with attached text.
|
||||
---@field markToGroup fun(id: number, text: string, point: Vec3, groupID: number, readOnly: boolean?, message: string) Adds a mark point to a group on the F10 map with attached text.
|
||||
---@field markToGroup fun(id: number, text: string, point: Vec3, groupID: number, readOnly: boolean?, message: string?) Adds a mark point to a group on the F10 map with attached text.
|
||||
---@field removeMark fun(id: number) Removes a mark panel from the f10 map
|
||||
---@field markupToAll fun(shapeID: ShapeId, coalition: DrawCoalition, id: number, ... : any) Complex parameters. <br/> See: https://wiki.hoggitworld.com/view/DCS_func_markupToAll
|
||||
---@field lineToAll fun(coalition: DrawCoalition, id: number, startPoint:Vec3, endPoint: Vec3, color:table, lineType: LineType, readonly: boolean?, message: string?) Creates a line on the F10 map from one point to another.
|
||||
|
||||
@@ -78,6 +78,8 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#randomization" class="side-nav-h2">Randomization</a></li>
|
||||
<li><a href="#in-stage-dependencies" class="side-nav-h2">In stage dependencies</a></li>
|
||||
<li><a href="#buildable" class="side-nav-h2">Buildables</a></li>
|
||||
<li><a href="#runway-bombing" class="side-nav-h2">Runway Bombing</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -164,6 +166,22 @@
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
<h3 id="supply-hub">Supply Hub</h3>
|
||||
<p>
|
||||
<strong>Format:</strong> <span class="inline-lua"><span class="lua-variable">SUPPLYHUB_[Name]</span></span> <br />
|
||||
<strong>Format:</strong> <span class="inline-lua"><span class="lua-variable">SUPPLYHUB_A_[Name]</span></span> <br />
|
||||
<strong>Example:</strong> <span class="inline-lua"><span class="lua-variable">SUPPLYHUB_HOUSTON</span></span> <br/> <br/>
|
||||
|
||||
Supply hubs are used as zones to pick up logistics for logistic missions. <br/>
|
||||
They are activated when their parent is activated. <br/>
|
||||
Activated depends on what it's parent is. <br/>
|
||||
If it's a stage. It can be SUPPLYHUB_A_[name] when you want it to be active when the stage is initiated. (red state) or SUPPLYHUB_B_ it you want it activated when it's BLUE only. <br/>
|
||||
If you place the Supply Hub trigger zone inside of a FARP zone the supply hub will activate when the FARP zone is activated. <br/>
|
||||
If the FARP needs to be built first, then that means the supply hub also is activated later. <br/>
|
||||
|
||||
NOTE: The supply hub does not spawn anything, but merely give options to pick up logistics.
|
||||
</p>
|
||||
|
||||
<h2 id="mission-types">Mission Types</h2>
|
||||
<p>
|
||||
@@ -258,6 +276,28 @@
|
||||
The mission will be "pre-activated" (spawned) but won't have a f10 mission menu until the mission(s) it depends on is completed. <br/>
|
||||
</p>
|
||||
|
||||
<h2 id="buildable">Buildables</h2>
|
||||
<p>
|
||||
Current buildables: <br/>
|
||||
<span class="inline-lua"><span class="lua-variable">FARP_</span></span> <br/>
|
||||
<span class="inline-lua"><span class="lua-variable">BLUESAM_</span></span> <br/>
|
||||
<br/>
|
||||
|
||||
Buildables are zones or objects that first require logistics. <br/>
|
||||
Eg. A forward SAM site or FARP requires logistic crates before it can be built. <br/>
|
||||
|
||||
To make an item buildable add a text box inside of the trigger zone and name it: <span class="inline-lua"><span class="lua-variable">buildable_[freeform]</span></span> <br/>
|
||||
Then in the text box type amount of kilo's you want to be transfered before the logistisc mission is complete. <br/>
|
||||
The base will slowly build up with each crate giving both a nice view of it happening AND it's good for performance as there's not a big addition of objects at once. <br/>
|
||||
Right now a crate takes 15 seconds to unpack per 500kg. Meaning 2 crates of 1000kg will be faster than 1 crate of 2000kg. <br/>
|
||||
|
||||
All helicopters have a max limit set based on quick google searches and cannot be changed but the ME at this point: <br/>
|
||||
UH-1H : 2000kg <br/>
|
||||
Mi-8 : 4000kg <br/>
|
||||
CH-47 : 10000kg <br/>
|
||||
MI-24 : 2000kg <br/>
|
||||
</p>
|
||||
|
||||
<h2>Runway Bombing</h2>
|
||||
<p>
|
||||
Runway bombing can be a very effective OCA tactic. With Spearhead we've tried adding as much logic and detail to it so it will feel as engaging as possible. <br/>
|
||||
|
||||
Binary file not shown.
@@ -32,7 +32,11 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw
|
||||
CapBase.__index = CapBase
|
||||
local self = setmetatable({}, { __index = CapBase }) --[[@as CapBase]]
|
||||
|
||||
self.groupNames = database:getCapGroupsAtAirbase(airbaseName)
|
||||
local baseData = database:getAirbaseDataForZone(airbaseName)
|
||||
local groupNames = {}
|
||||
if baseData then groupNames = baseData.CapGroups or {} end
|
||||
|
||||
self.groupNames = groupNames
|
||||
self.database = database
|
||||
self.runwayBombingTracker = runwayBombingTracker
|
||||
self.runwayStrikeMissions = {}
|
||||
|
||||
@@ -12,10 +12,5 @@ do -- mission aliases
|
||||
|
||||
---@class Array<T>: { [integer]: T }
|
||||
|
||||
--- @class Position
|
||||
--- @field x number x position (Top-Down on Map)
|
||||
--- @field y number y altitude
|
||||
--- @field z number z position (Left-Right on Map)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ do
|
||||
|
||||
---@class DeathState
|
||||
---@field isDead boolean
|
||||
---@field pos Position
|
||||
---@field pos Vec3
|
||||
---@field heading number
|
||||
---@field type string
|
||||
---@field country_id integer
|
||||
@@ -204,7 +204,7 @@ do
|
||||
|
||||
---Pass the unit to be saved as "dead"
|
||||
---@param name string
|
||||
---@param position Position { x, y ,z }
|
||||
---@param position Vec3 { x, y ,z }
|
||||
---@param heading number
|
||||
---@param type string
|
||||
---@param country_id number
|
||||
|
||||
@@ -206,6 +206,22 @@ do -- INIT UTIL
|
||||
return false
|
||||
end
|
||||
|
||||
---@param point Vec2
|
||||
---@param zone SpearheadTriggerZone
|
||||
function UTIL.is2dPointInZone(point, zone)
|
||||
if zone.zone_type == "Polygon" and zone.verts then
|
||||
if UTIL.IsPointInPolygon(zone.verts, point.x, point.y) == true then
|
||||
return true
|
||||
end
|
||||
else
|
||||
if (((point.x - zone.location.x) ^ 2 + (point.y - zone.location.y) ^ 2) ^ 0.5 <= zone.radius) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param points Array<Vec2> points
|
||||
---@return Array<Vec2> hullPoints
|
||||
@@ -974,6 +990,27 @@ do -- INIT DCS_UTIL
|
||||
return drawID
|
||||
end
|
||||
|
||||
|
||||
local _markID = 200
|
||||
|
||||
---@param groupID number
|
||||
---@param text string
|
||||
---@param location Vec3
|
||||
---@return number markID
|
||||
function DCS_UTIL.AddMarkToGroup(groupID, text, location)
|
||||
|
||||
_markID = _markID + 1
|
||||
trigger.action.markToGroup(_markID, text, location, groupID, true, nil)
|
||||
return _markID
|
||||
end
|
||||
|
||||
---@param markId number
|
||||
function DCS_UTIL.RemoveMark(markId)
|
||||
if markId ~= nil then
|
||||
trigger.action.removeMark(markId)
|
||||
end
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param drawID number
|
||||
---@param lineColor DrawColor
|
||||
@@ -1092,6 +1129,19 @@ do -- INIT DCS_UTIL
|
||||
end
|
||||
end
|
||||
|
||||
---@param unitID number
|
||||
---@return Unit?
|
||||
function DCS_UTIL.GetPLayerUnitByID(unitID)
|
||||
for i = 0, 2 do
|
||||
local players = coalition.getPlayers(i)
|
||||
for key, unit in pairs(players) do
|
||||
if unit and unit:getID() == unitID then
|
||||
return unit
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DCS_UTIL.__INIT();
|
||||
end
|
||||
Spearhead.DcsUtil = DCS_UTIL
|
||||
|
||||
+183
-68
@@ -11,6 +11,7 @@
|
||||
---@field CapDataPerStageNumber table<integer, CapData> table<StageNumber, table>
|
||||
---@field CarrierRouteZones Array<string> All Carrier routes zones
|
||||
---@field BlueSams Array<string> All blue sam zones
|
||||
---@field SupplyHubZones Array<string> All supply hub zones
|
||||
---@field MissionAnnotations table<string, MissionAnnotations> table<ZoneName, MissionAnnotations>
|
||||
---@field AirbaseDataPerAirfield table<string, AirbaseData>
|
||||
---@field BlueSamDataPerZone table<string, BlueSamData>
|
||||
@@ -39,15 +40,19 @@
|
||||
---@field RandomMissionZones Array<string>
|
||||
---@field StageIndex string
|
||||
---@field BlueSamZones Array<string>
|
||||
---@field SupplyHubZones Array<string>
|
||||
---@field SupplyHubZonesInFarp table<string, string>
|
||||
---@field MiscGroups Array<string>
|
||||
|
||||
---@class AirbaseData
|
||||
---@field CapGroups Array<string>
|
||||
---@field RedGroups Array<string>
|
||||
---@field BlueGroups Array<string>
|
||||
---@field supplyHubNames Array<string>
|
||||
|
||||
---@class BlueSamData
|
||||
---@field Groups Array<string>
|
||||
---@field groups Array<string>
|
||||
---@field buildingKilos number?
|
||||
|
||||
---@class MissionZoneData
|
||||
---@field Groups Array<string>
|
||||
@@ -58,6 +63,8 @@
|
||||
---@class FarpZoneData
|
||||
---@field groups Array<string>
|
||||
---@field padNames Array<string>
|
||||
---@field buildingKilos number?
|
||||
---@field supplyHubNames Array<string>
|
||||
|
||||
---@class Database
|
||||
---@field private _tables DatabaseTables
|
||||
@@ -87,7 +94,8 @@ function Database.New(Logger)
|
||||
MissionZoneData = {},
|
||||
FarpZoneData = {},
|
||||
missionCodes = {},
|
||||
MissionAnnotations = {}
|
||||
MissionAnnotations = {},
|
||||
SupplyHubZones = {}
|
||||
}
|
||||
|
||||
Database.__index = Database
|
||||
@@ -126,7 +134,9 @@ function Database.New(Logger)
|
||||
FarpZones = {},
|
||||
MissionZones = {},
|
||||
RandomMissionZones = {},
|
||||
MiscGroups = {}
|
||||
MiscGroups = {},
|
||||
SupplyHubZones = {},
|
||||
SupplyHubZonesInFarp = {}
|
||||
}
|
||||
self._tables.StageZones[zone_name] = stageData
|
||||
end
|
||||
@@ -161,25 +171,37 @@ function Database.New(Logger)
|
||||
if string.lower(split_string[1]) == "bluesam" then
|
||||
table.insert(self._tables.BlueSams, zone_name)
|
||||
end
|
||||
|
||||
if string.lower(split_string[1]) == "supplyhub" then
|
||||
table.insert(self._tables.SupplyHubZones, zone_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self._logger:debug("initiated zone tables, continuing with descriptions")
|
||||
do --load markers
|
||||
|
||||
if env.mission.drawings and env.mission.drawings.layers then
|
||||
for i, layer in pairs(env.mission.drawings.layers) do
|
||||
if string.lower(layer.name) == "author" then
|
||||
for key, layer_object in pairs(layer.objects) do
|
||||
if Spearhead.Util.startswith(string.lower(layer_object.name), "buildable", true) == true then
|
||||
local blueSamData = self:getBlueSamDataForDrawLayer(layer_object)
|
||||
if blueSamData then
|
||||
local number = tonumber(layer_object.text)
|
||||
blueSamData.buildingKilos = number
|
||||
end
|
||||
|
||||
if Spearhead.Util.startswith(string.lower(layer_object.name), "completeat", true) == true then
|
||||
|
||||
local farpData = self:getFarpDataForDrawLayer(layer_object)
|
||||
if farpData then
|
||||
local number = tonumber(layer_object.text)
|
||||
farpData.buildingKilos = number
|
||||
end
|
||||
elseif Spearhead.Util.startswith(string.lower(layer_object.name), "completeat", true) == true then
|
||||
local annotationData = self:getMissionMetaDataForDrawLayer(layer_object)
|
||||
if annotationData then
|
||||
local number = tonumber(layer_object.text)
|
||||
if number and number > 1 then
|
||||
number = number / 100
|
||||
|
||||
end
|
||||
annotationData.completeAt = number
|
||||
end
|
||||
@@ -204,6 +226,12 @@ function Database.New(Logger)
|
||||
end
|
||||
end
|
||||
|
||||
---@type table<string, boolean>
|
||||
local availableSupplyHubs = {}
|
||||
for _, supplyHubZoneName in pairs(self._tables.SupplyHubZones) do
|
||||
availableSupplyHubs[supplyHubZoneName] = true
|
||||
end
|
||||
|
||||
for _, stageZoneName in pairs(self._tables.StageZoneNames) do
|
||||
local stageData = self._tables.StageZones[stageZoneName]
|
||||
if stageData then
|
||||
@@ -214,6 +242,63 @@ function Database.New(Logger)
|
||||
end
|
||||
end
|
||||
|
||||
--- fill farp zones
|
||||
for _, farpZoneName in pairs(self._tables.AllFarpZones) do
|
||||
if Spearhead.DcsUtil.isZoneInZone(farpZoneName, stageZoneName) then
|
||||
table.insert(stageData.FarpZones, farpZoneName)
|
||||
|
||||
for hubZoneName, available in pairs(availableSupplyHubs) do
|
||||
if available == true and Spearhead.DcsUtil.isZoneInZone(hubZoneName, farpZoneName) == true then
|
||||
local farpZoneData = self:getOrCreateFarpDataForZone(farpZoneName)
|
||||
if farpZoneData then
|
||||
table.insert(farpZoneData.supplyHubNames, hubZoneName)
|
||||
availableSupplyHubs[hubZoneName] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- fill airbases
|
||||
for _, airbase in pairs(world.getAirbases()) do
|
||||
local point = airbase:getPoint()
|
||||
|
||||
if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, stageZoneName) == true then
|
||||
if airbase:getDesc().category == 0 then
|
||||
table.insert(stageData.AirbaseNames, airbase:getName())
|
||||
|
||||
local airbaseZone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName())
|
||||
for hubZoneName, available in pairs(availableSupplyHubs) do
|
||||
local zone = Spearhead.DcsUtil.getZoneByName(hubZoneName)
|
||||
if zone and airbaseZone then
|
||||
if available == true and Spearhead.Util.is2dPointInZone(zone.location, airbaseZone) == true then
|
||||
local airbaseData = self:getOrCreateAirbaseData(airbase:getName())
|
||||
if airbaseData then
|
||||
table.insert(airbaseData.supplyHubNames, hubZoneName)
|
||||
availableSupplyHubs[hubZoneName] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- fill supply hubs
|
||||
for supplyHubZone, available in pairs(availableSupplyHubs) do
|
||||
if available == true and Spearhead.DcsUtil.isZoneInZone(supplyHubZone, stageZoneName) == true then
|
||||
table.insert(stageData.SupplyHubZones, supplyHubZone)
|
||||
end
|
||||
end
|
||||
|
||||
for _, farpZoneName in pairs(stageData.FarpZones) do
|
||||
for _, supplyHubZone in pairs(self._tables.SupplyHubZones) do
|
||||
if Spearhead.DcsUtil.isZoneInZone(supplyHubZone, farpZoneName) == true then
|
||||
stageData.SupplyHubZonesInFarp[supplyHubZone] = farpZoneName
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- fill missions
|
||||
for key, missionZone in pairs(self._tables.MissionZones) do
|
||||
if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then
|
||||
@@ -227,24 +312,6 @@ function Database.New(Logger)
|
||||
table.insert(stageData.RandomMissionZones, missionZone)
|
||||
end
|
||||
end
|
||||
|
||||
--- fill farp zones
|
||||
for _, farpZoneName in pairs(self._tables.AllFarpZones) do
|
||||
if Spearhead.DcsUtil.isZoneInZone(farpZoneName, stageZoneName) then
|
||||
table.insert(stageData.FarpZones, farpZoneName)
|
||||
end
|
||||
end
|
||||
|
||||
-- fill airbases
|
||||
for _, airbase in pairs(world.getAirbases()) do
|
||||
local point = airbase:getPoint()
|
||||
|
||||
if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, stageZoneName) == true then
|
||||
if airbase:getDesc().category == 0 then
|
||||
table.insert(stageData.AirbaseNames, airbase:getName())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -268,7 +335,8 @@ function Database.New(Logger)
|
||||
if self._tables.FarpZoneData[farpZoneName] == nil then
|
||||
self._tables.FarpZoneData[farpZoneName] = {
|
||||
groups = {},
|
||||
padNames = {}
|
||||
padNames = {},
|
||||
supplyHubNames = {}
|
||||
}
|
||||
end
|
||||
local position = airbase:getPoint()
|
||||
@@ -288,7 +356,8 @@ function Database.New(Logger)
|
||||
baseData = {
|
||||
CapGroups = {},
|
||||
RedGroups = {},
|
||||
BlueGroups = {}
|
||||
BlueGroups = {},
|
||||
supplyHubNames = {}
|
||||
}
|
||||
self._tables.AirbaseDataPerAirfield[baseName] = baseData
|
||||
end
|
||||
@@ -417,13 +486,65 @@ function Database:initAvailableUnits()
|
||||
end
|
||||
end
|
||||
|
||||
---comment
|
||||
---@private
|
||||
---@param layer_object table
|
||||
---@return BlueSamData?
|
||||
function Database:getBlueSamDataForDrawLayer(layer_object)
|
||||
for _, zonename in pairs(self._tables.BlueSams) do
|
||||
if Spearhead.DcsUtil.isPositionInZone(layer_object.mapX, layer_object.mapY, zonename) == true then
|
||||
return self:getOrCreateBlueSamDataForZone(zonename)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@private
|
||||
---@param layer_object table
|
||||
---@return FarpZoneData?
|
||||
function Database:getFarpDataForDrawLayer(layer_object)
|
||||
for _, zonename in pairs(self._tables.AllFarpZones) do
|
||||
if Spearhead.DcsUtil.isPositionInZone(layer_object.mapX, layer_object.mapY, zonename) == true then
|
||||
return self:getOrCreateFarpDataForZone(zonename)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@private
|
||||
function Database:getOrCreateBlueSamDataForZone(zoneName)
|
||||
local blueSamData = self._tables.BlueSamDataPerZone[zoneName]
|
||||
if blueSamData == nil then
|
||||
blueSamData = {
|
||||
groups = {},
|
||||
buildingCrates = nil
|
||||
}
|
||||
self._tables.BlueSamDataPerZone[zoneName] = blueSamData
|
||||
end
|
||||
return blueSamData
|
||||
end
|
||||
|
||||
---@private
|
||||
function Database:getOrCreateFarpDataForZone(zoneName)
|
||||
local farpData = self._tables.FarpZoneData[zoneName]
|
||||
if farpData == nil then
|
||||
farpData = {
|
||||
padNames = {},
|
||||
groups = {},
|
||||
buildingCrates = nil,
|
||||
supplyHubNames = {}
|
||||
}
|
||||
self._tables.FarpZoneData[zoneName] = farpData
|
||||
end
|
||||
return farpData
|
||||
end
|
||||
|
||||
---@private
|
||||
---@param layer_object table
|
||||
---@return MissionAnnotations?
|
||||
function Database:getMissionMetaDataForDrawLayer(layer_object)
|
||||
for _, zonename in pairs(self._tables.MissionZones) do
|
||||
if Spearhead.DcsUtil.isPositionInZone(layer_object.mapX, layer_object.mapY, zonename) == true then
|
||||
|
||||
if self._tables.MissionAnnotations[zonename] == nil then
|
||||
self._tables.MissionAnnotations[zonename] = {
|
||||
description = nil,
|
||||
@@ -437,7 +558,6 @@ function Database:getMissionMetaDataForDrawLayer(layer_object)
|
||||
|
||||
for _, zonename in pairs(self._tables.RandomMissionZones) do
|
||||
if Spearhead.DcsUtil.isPositionInZone(layer_object.mapX, layer_object.mapY, zonename) == true then
|
||||
|
||||
if self._tables.MissionAnnotations[zonename] == nil then
|
||||
self._tables.MissionAnnotations[zonename] = {
|
||||
description = nil,
|
||||
@@ -487,13 +607,11 @@ end
|
||||
function Database:loadBlueSamUnits()
|
||||
local all_groups = Spearhead.DcsUtil.getAllGroupNames()
|
||||
for _, blueSamZone in pairs(self._tables.BlueSams) do
|
||||
self._tables.BlueSamDataPerZone[blueSamZone] = {
|
||||
Groups = {}
|
||||
}
|
||||
local samData = self:getOrCreateBlueSamDataForZone(blueSamZone)
|
||||
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, blueSamZone)
|
||||
for _, groupName in pairs(groups) do
|
||||
is_group_taken[groupName] = true
|
||||
table.insert(self._tables.BlueSamDataPerZone[blueSamZone].Groups, groupName)
|
||||
table.insert(samData.groups, groupName)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -533,17 +651,12 @@ end
|
||||
function Database:loadFarpGroups()
|
||||
local all_groups = getAvailableGroups()
|
||||
for _, farpZone in pairs(self._tables.AllFarpZones) do
|
||||
if self._tables.FarpZoneData[farpZone] == nil then
|
||||
self._tables.FarpZoneData[farpZone] = {
|
||||
groups = {},
|
||||
padNames = {}
|
||||
}
|
||||
end
|
||||
local farpzoneData = self:getOrCreateFarpDataForZone(farpZone)
|
||||
|
||||
local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, farpZone)
|
||||
for _, groupName in pairs(groups) do
|
||||
is_group_taken[groupName] = true
|
||||
table.insert(self._tables.FarpZoneData[farpZone].groups, groupName)
|
||||
table.insert(farpzoneData.groups, groupName)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -628,7 +741,6 @@ function Database:loadMiscGroupsInStages()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---@return string?
|
||||
function Database:getMissionBriefingForMissionZone(missionZoneName)
|
||||
if not self._tables.MissionAnnotations[missionZoneName] then
|
||||
@@ -638,7 +750,6 @@ function Database:getMissionBriefingForMissionZone(missionZoneName)
|
||||
return self._tables.MissionAnnotations[missionZoneName].description
|
||||
end
|
||||
|
||||
|
||||
---@param missionZoneName string
|
||||
---@return Array<string>
|
||||
function Database:getMissionDependencies(missionZoneName)
|
||||
@@ -757,7 +868,6 @@ function Database:getGroupsForMissionZone(missionZoneName)
|
||||
return missionZoneData.Groups
|
||||
end
|
||||
|
||||
|
||||
---@param stageName string
|
||||
---@return table result airbase Names
|
||||
function Database:getAirbaseNamesInStage(stageName)
|
||||
@@ -780,14 +890,15 @@ function Database:getFarpDataForZone(farpZoneName)
|
||||
return farpData
|
||||
end
|
||||
|
||||
---@param airbaseName string
|
||||
---@return Array<string>
|
||||
function Database:getCapGroupsAtAirbase(airbaseName)
|
||||
local airbaseData = self._tables.AirbaseDataPerAirfield[airbaseName]
|
||||
if not airbaseData then return {} end
|
||||
return airbaseData.CapGroups
|
||||
---@return AirbaseData?
|
||||
---@param baseName string
|
||||
function Database:getAirbaseDataForZone(baseName)
|
||||
local baseData = self._tables.AirbaseDataPerAirfield[baseName]
|
||||
if not baseData then return nil end
|
||||
return baseData
|
||||
end
|
||||
|
||||
|
||||
---@param stageName string
|
||||
---@return Array<string>
|
||||
function Database:getBlueSamsInStage(stageName)
|
||||
@@ -796,29 +907,33 @@ function Database:getBlueSamsInStage(stageName)
|
||||
return stageData.BlueSamZones
|
||||
end
|
||||
|
||||
---@param stageName string
|
||||
---@return Array<string>
|
||||
function Database:getSupplyHubsInStage(stageName)
|
||||
local stageData = self._tables.StageZones[stageName]
|
||||
if not stageData then return {} end
|
||||
return stageData.SupplyHubZones
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param stageName any
|
||||
---@param supplyZoneName any
|
||||
---@return nil
|
||||
function Database:getFarpDependencyForSupplyHub(stageName, supplyZoneName)
|
||||
local stageData = self._tables.StageZones[stageName]
|
||||
if not stageData then return nil end
|
||||
return stageData.SupplyHubZonesInFarp[supplyZoneName]
|
||||
end
|
||||
|
||||
---@param samZone string
|
||||
---@return Array<string>
|
||||
function Database:getBlueSamGroupsInZone(samZone)
|
||||
local blueSamData = self._tables.BlueSamDataPerZone[samZone]
|
||||
if not blueSamData then return {} end
|
||||
return blueSamData.Groups
|
||||
---@return BlueSamData?
|
||||
function Database:getBlueSamDataForZone(samZone)
|
||||
return self._tables.BlueSamDataPerZone[samZone]
|
||||
end
|
||||
|
||||
---@param airbaseName string
|
||||
---@return Array<string>
|
||||
function Database:getRedGroupsAtAirbase(airbaseName)
|
||||
local airbaseData = self._tables.AirbaseDataPerAirfield[airbaseName]
|
||||
if not airbaseData then return {} end
|
||||
return airbaseData.RedGroups
|
||||
end
|
||||
|
||||
---@param airbaseName string
|
||||
---@return Array<string>
|
||||
function Database:getBlueGroupsAtAirbase(airbaseName)
|
||||
local airbaseData = self._tables.AirbaseDataPerAirfield[airbaseName]
|
||||
if not airbaseData then return {} end
|
||||
return airbaseData.BlueGroups
|
||||
end
|
||||
|
||||
|
||||
|
||||
function Database:getMiscGroupsAtStage(stageName)
|
||||
local stageZone = self._tables.StageZones[stageName]
|
||||
|
||||
@@ -21,6 +21,10 @@ function SpearheadGroup.New(groupName)
|
||||
return self
|
||||
end
|
||||
|
||||
function SpearheadGroup:IsSpawned()
|
||||
return self._isSpawned
|
||||
end
|
||||
|
||||
function SpearheadGroup:SpawnCorpsesOnly()
|
||||
|
||||
if self._isSpawned == true then return end
|
||||
@@ -146,6 +150,26 @@ function SpearheadGroup:GetUnits()
|
||||
return result
|
||||
end
|
||||
|
||||
---@return Array<Vec3>
|
||||
function SpearheadGroup:GetAllUnitPositions()
|
||||
|
||||
local result = {}
|
||||
if self._isStatic == true then
|
||||
local staticObject = StaticObject.getByName(self.groupName)
|
||||
if staticObject then
|
||||
table.insert(result, staticObject:getPoint())
|
||||
end
|
||||
else
|
||||
local group = Group.getByName(self.groupName)
|
||||
if not group then return {} end
|
||||
for _, unit in pairs(group:getUnits()) do
|
||||
table.insert(result, unit:getPoint())
|
||||
end
|
||||
end
|
||||
return result
|
||||
|
||||
end
|
||||
|
||||
if not Spearhead.classes then Spearhead.classes = {} end
|
||||
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
|
||||
if not Spearhead.classes.stageClasses.Groups then Spearhead.classes.stageClasses.Groups = {} end
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
|
||||
---@class BlueSam
|
||||
---@class BlueSam : OnCrateDroppedListener, MissionCompleteListener
|
||||
---@field Activate fun(self: BlueSam)
|
||||
---@field private _database Database
|
||||
---@field private _logger Logger
|
||||
---@field private _zoneName string
|
||||
---@field private _blueGroups Array<SpearheadGroup>
|
||||
---@field private _cleanupUnits table<string, boolean>
|
||||
---@field private _buildableCrateKilos number?
|
||||
---@field private _receivedKilos number?
|
||||
---@field private _unitsPerCrate number?
|
||||
---@field private _buildableMission BuildableMission?
|
||||
local BlueSam = {}
|
||||
|
||||
---@param database Database
|
||||
---@param logger Logger
|
||||
---@param zoneName string
|
||||
---@return BlueSam?
|
||||
function BlueSam.New(database, logger, zoneName)
|
||||
BlueSam.__index = BlueSam
|
||||
local self = setmetatable({}, BlueSam)
|
||||
@@ -19,56 +27,114 @@ function BlueSam.New(database, logger, zoneName)
|
||||
self._blueGroups = {}
|
||||
self._cleanupUnits = {}
|
||||
|
||||
do
|
||||
local groups = database:getBlueSamGroupsInZone(zoneName)
|
||||
local blueSamData = database:getBlueSamDataForZone(zoneName)
|
||||
|
||||
---@type table<string, Vec3>
|
||||
local blueUnitsPos = {}
|
||||
if blueSamData == nil then
|
||||
logger:error("Blue SAM data not found for zone: " .. zoneName)
|
||||
return nil
|
||||
end
|
||||
|
||||
---@type table<string, Vec3>
|
||||
local redUnitsPos = {}
|
||||
self._buildableCrateKilos = blueSamData.buildingKilos
|
||||
self._receivedKilos = 0
|
||||
|
||||
for _, groupName in pairs(groups) do
|
||||
local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||
if SpearheadGroup then
|
||||
|
||||
if SpearheadGroup:GetCoalition() == 2 then
|
||||
table.insert(self._blueGroups, SpearheadGroup)
|
||||
end
|
||||
---@type table<string, Vec3>
|
||||
local blueUnitsPos = {}
|
||||
|
||||
---@type table<string, Vec3>
|
||||
local redUnitsPos = {}
|
||||
|
||||
for _, unit in pairs(SpearheadGroup:GetUnits()) do
|
||||
if SpearheadGroup:GetCoalition() == 1 then
|
||||
table.insert(blueUnitsPos, unit:getPoint())
|
||||
elseif SpearheadGroup:GetCoalition() == 2 then
|
||||
table.insert(redUnitsPos, unit:getPoint())
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for _, groupName in pairs(blueSamData.groups) do
|
||||
local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||
if SpearheadGroup then
|
||||
|
||||
if SpearheadGroup:GetCoalition() == 2 then
|
||||
table.insert(self._blueGroups, SpearheadGroup)
|
||||
end
|
||||
SpearheadGroup:Destroy()
|
||||
end
|
||||
|
||||
do -- check cleanup requirements
|
||||
-- Checks is any of the units are withing range (5m) of another unit.
|
||||
-- If so, make sure to add them to the cleanup list.
|
||||
|
||||
local cleanup_distance = 5
|
||||
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
|
||||
for redUnitName, redUnitPos in pairs(redUnitsPos) do
|
||||
local distance = Spearhead.Util.VectorDistance3d(blueUnitPos, redUnitPos)
|
||||
if distance <= cleanup_distance then
|
||||
self._cleanupUnits[redUnitName] = true
|
||||
end
|
||||
|
||||
for _, unit in pairs(SpearheadGroup:GetUnits()) do
|
||||
if SpearheadGroup:GetCoalition() == 1 then
|
||||
table.insert(blueUnitsPos, unit:getPoint())
|
||||
elseif SpearheadGroup:GetCoalition() == 2 then
|
||||
table.insert(redUnitsPos, unit:getPoint())
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
SpearheadGroup:Destroy()
|
||||
end
|
||||
|
||||
--Cleanup units
|
||||
local cleanup_distance = 5
|
||||
for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do
|
||||
for redUnitName, redUnitPos in pairs(redUnitsPos) do
|
||||
local distance = Spearhead.Util.VectorDistance3d(blueUnitPos, redUnitPos)
|
||||
if distance <= cleanup_distance then
|
||||
self._cleanupUnits[redUnitName] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self._buildableCrateKilos ~= nil and self._buildableCrateKilos ~= 0 then
|
||||
self._logger:debug("Buildable mission creating for zone: " .. zoneName .. " with crates: " .. self._buildableCrateKilos)
|
||||
|
||||
local noLandingZone = self:GetNoLandingZone()
|
||||
|
||||
self._buildableMission = Spearhead.classes.stageClasses.missions.BuildableMission.new(database, logger, zoneName, noLandingZone, self._buildableCrateKilos, "SAM_CRATE")
|
||||
self._buildableMission:AddOnCrateDroppedOfListener(self)
|
||||
self._buildableMission:AddMissionCompleteListener(self)
|
||||
end
|
||||
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
---@private
|
||||
---@return SpearheadTriggerZone?
|
||||
function BlueSam:GetNoLandingZone()
|
||||
|
||||
---@type Array<Vec2>
|
||||
local points = {}
|
||||
|
||||
for _, group in pairs(self._blueGroups) do
|
||||
for _, unitPos in pairs(group:GetAllUnitPositions()) do
|
||||
table.insert(points, { x = unitPos.x, y = unitPos.z })
|
||||
end
|
||||
end
|
||||
|
||||
local vecs = Spearhead.Util.getConvexHull(points)
|
||||
|
||||
local zone = Spearhead.DcsUtil.getZoneByName(self._zoneName)
|
||||
if zone == nil then
|
||||
self._logger:error("Zone not found: " .. self._zoneName)
|
||||
return nil
|
||||
end
|
||||
|
||||
---@type SpearheadTriggerZone
|
||||
local spearheadZone = {
|
||||
name = self._zoneName .. "_noland",
|
||||
location = zone.location,
|
||||
verts = vecs,
|
||||
radius = 0,
|
||||
zone_type = "Polygon"
|
||||
}
|
||||
|
||||
return spearheadZone
|
||||
end
|
||||
|
||||
function BlueSam:Activate()
|
||||
|
||||
if self._buildableMission == nil then
|
||||
self:SpawnGroups()
|
||||
else
|
||||
self._buildableMission:SpawnActive()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function BlueSam:SpawnGroups()
|
||||
for unitName, needsCleanup in pairs(self._cleanupUnits) do
|
||||
if needsCleanup == true then
|
||||
Spearhead.DcsUtil.DestroyUnit(unitName)
|
||||
@@ -86,6 +152,24 @@ function BlueSam:Activate()
|
||||
end
|
||||
|
||||
|
||||
---@param buildableMission BuildableMission
|
||||
---@param kilos number
|
||||
function BlueSam:OnCrateDroppedOff(buildableMission, kilos)
|
||||
|
||||
self._logger:debug("Crate dropped off in zone: " .. self._zoneName)
|
||||
self._receivedKilos = self._receivedKilos + kilos
|
||||
|
||||
end
|
||||
|
||||
---@param buildableMission Mission
|
||||
function BlueSam:OnMissionComplete(buildableMission)
|
||||
self._logger:debug("Buildable mission complete for zone: " .. self._zoneName)
|
||||
|
||||
self:SpawnGroups()
|
||||
|
||||
end
|
||||
|
||||
|
||||
if Spearhead == nil then Spearhead = {} end
|
||||
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
|
||||
|
||||
---@class FarpZone
|
||||
---@class FarpZone : OnCrateDroppedListener, MissionCompleteListener
|
||||
---@field private _startingFarp boolean
|
||||
---@field private _groups Array<SpearheadGroup>
|
||||
---@field private _padNames Array<string>
|
||||
---@field private _database Database
|
||||
---@field private _logger Logger
|
||||
---@field private _zoneName string
|
||||
---@field private _requiredBuildingKilos number
|
||||
---@field private _receivedBuildingKilos number
|
||||
---@field private _groupsPerKilo number
|
||||
---@field private _buildableMission BuildableMission?
|
||||
---@field private _supplyHubs Array<SupplyHub>
|
||||
local FarpZone = {}
|
||||
FarpZone.__index = FarpZone
|
||||
|
||||
@@ -36,22 +41,50 @@ function FarpZone.New(database, logger, zoneName)
|
||||
local farpData = database:getFarpDataForZone(zoneName)
|
||||
self._groups = {}
|
||||
self._padNames = {}
|
||||
self._supplyHubs = {}
|
||||
|
||||
|
||||
if farpData then
|
||||
self._padNames = farpData.padNames
|
||||
|
||||
for _, supplyHubName in pairs(farpData.supplyHubNames) do
|
||||
local supplyHub = Spearhead.classes.stageClasses.SpecialZones.SupplyHub.new(database, logger, supplyHubName)
|
||||
if supplyHub then
|
||||
table.insert(self._supplyHubs, supplyHub)
|
||||
end
|
||||
end
|
||||
|
||||
for _, groupName in pairs(farpData.groups) do
|
||||
local group = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||
table.insert(self._groups, group)
|
||||
group:Destroy()
|
||||
end
|
||||
end
|
||||
|
||||
self._requiredBuildingKilos = farpData.buildingKilos
|
||||
self._receivedBuildingKilos = 0
|
||||
|
||||
if self._requiredBuildingKilos ~= nil and self._requiredBuildingKilos > 0 then
|
||||
self._logger:debug("FARP zone " .. zoneName .. " requires " .. self._requiredBuildingKilos .. " crates to be dropped off")
|
||||
local noLandingZone = self:GetNoLandingZone()
|
||||
self._buildableMission = Spearhead.classes.stageClasses.missions.BuildableMission.new(database, logger, zoneName, noLandingZone, self._requiredBuildingKilos, "FARP_CRATE")
|
||||
if self._buildableMission then
|
||||
self._buildableMission:AddOnCrateDroppedOfListener(self)
|
||||
self._buildableMission:AddMissionCompleteListener(self)
|
||||
end
|
||||
|
||||
local totalGroups = Spearhead.Util.tableLength(self._groups)
|
||||
self._groupsPerKilo = totalGroups / self._requiredBuildingKilos
|
||||
end
|
||||
end
|
||||
if self._buildableMission == nil then
|
||||
self._logger:debug("No buildable mission for zone: " .. zoneName)
|
||||
end
|
||||
self:Deactivate()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@return boolean
|
||||
function FarpZone:IsStartingFarp()
|
||||
return self._startingFarp
|
||||
@@ -59,14 +92,112 @@ end
|
||||
|
||||
function FarpZone:Activate()
|
||||
self._logger:info("Activating FARP zone: " .. self._zoneName)
|
||||
self:BuildUp()
|
||||
self:SetPadsBlue()
|
||||
|
||||
if self._buildableMission == nil then
|
||||
self:BuildUp()
|
||||
self:SetPadsBlue()
|
||||
self:ActivateSupplyHubs()
|
||||
else
|
||||
self._buildableMission:SpawnActive()
|
||||
end
|
||||
end
|
||||
|
||||
function FarpZone:Deactivate()
|
||||
|
||||
self:NeutralisePads()
|
||||
end
|
||||
|
||||
---@class UnpackCrateParam
|
||||
---@field self FarpZone
|
||||
---@field kilos number
|
||||
---@field groupsPerKilo number
|
||||
---@field kilosPerSecond number
|
||||
---@field unpackedItems number
|
||||
---@field unpackedKilos number
|
||||
|
||||
---@param params UnpackCrateParam
|
||||
---@param time number
|
||||
local startUnpackingCrate = function(params, time)
|
||||
local unpacked = params.unpackedKilos + (params.kilosPerSecond * 2)
|
||||
local alreadySpawned = params.unpackedItems / params.groupsPerKilo
|
||||
local diff = unpacked - alreadySpawned
|
||||
|
||||
local amount = math.floor(diff * params.groupsPerKilo)
|
||||
local spawned = params.self:SpawnAmount(amount)
|
||||
|
||||
params.unpackedItems = params.unpackedItems + amount
|
||||
params.unpackedKilos = unpacked
|
||||
if params.unpackedKilos >= params.kilos or spawned == false then
|
||||
params.self:FinaliseCrate(params.kilos)
|
||||
return
|
||||
end
|
||||
|
||||
return time + 2
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param amount number
|
||||
---@return boolean
|
||||
function FarpZone:SpawnAmount(amount)
|
||||
|
||||
local function spawnOne()
|
||||
for _, group in pairs(self._groups) do
|
||||
if group:IsSpawned() == false then
|
||||
group:Spawn()
|
||||
return true
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
for i = 1, amount do
|
||||
local spawned = spawnOne()
|
||||
if spawned ~= true then
|
||||
self._logger:debug("No more groups to spawn in zone: " .. self._zoneName)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
---@param buildableMission BuildableMission
|
||||
function FarpZone:OnCrateDroppedOff(buildableMission, kilos)
|
||||
|
||||
self._logger:debug("Crate dropped off in zone: " .. self._zoneName)
|
||||
|
||||
local timeToUnpack = (kilos / 500) * 15
|
||||
|
||||
---@type UnpackCrateParam
|
||||
local params = {
|
||||
self = self,
|
||||
groupsPerKilo = self._groupsPerKilo,
|
||||
unpackedItems = 0,
|
||||
kilosPerSecond = kilos/timeToUnpack,
|
||||
unpackedKilos = 0,
|
||||
kilos = kilos
|
||||
}
|
||||
|
||||
timer.scheduleFunction(startUnpackingCrate, params, timer.getTime() + 2)
|
||||
|
||||
end
|
||||
|
||||
---@param kilos number
|
||||
function FarpZone:FinaliseCrate(kilos)
|
||||
self._receivedBuildingKilos = self._receivedBuildingKilos + kilos
|
||||
if self._receivedBuildingKilos >= self._requiredBuildingKilos then
|
||||
self:BuildUp()
|
||||
self:SetPadsBlue()
|
||||
self:ActivateSupplyHubs()
|
||||
end
|
||||
end
|
||||
|
||||
---@param buildableMission Mission
|
||||
function FarpZone:OnMissionComplete(buildableMission)
|
||||
self._logger:debug("Buildable mission complete for zone: " .. self._zoneName)
|
||||
|
||||
-- self:BuildUp()
|
||||
-- self:SetPadsBlue()
|
||||
-- self:ActivateSupplyHubs()
|
||||
|
||||
end
|
||||
|
||||
@@ -76,6 +207,13 @@ function FarpZone:BuildUp()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function FarpZone:ActivateSupplyHubs()
|
||||
for _, supplyHub in pairs(self._supplyHubs) do
|
||||
supplyHub:Activate()
|
||||
end
|
||||
end
|
||||
|
||||
---@private
|
||||
function FarpZone:NeutralisePads()
|
||||
for _, name in pairs(self._padNames) do
|
||||
@@ -99,6 +237,39 @@ function FarpZone:SetPadsBlue()
|
||||
end
|
||||
|
||||
|
||||
---@private
|
||||
---@return SpearheadTriggerZone?
|
||||
function FarpZone:GetNoLandingZone()
|
||||
|
||||
---@type Array<Vec2>
|
||||
local points = {}
|
||||
|
||||
for _, group in pairs(self._groups) do
|
||||
for _, unitPos in pairs(group:GetAllUnitPositions()) do
|
||||
table.insert(points, { x = unitPos.x, y = unitPos.z })
|
||||
end
|
||||
end
|
||||
|
||||
local vecs = Spearhead.Util.getConvexHull(points)
|
||||
|
||||
local zone = Spearhead.DcsUtil.getZoneByName(self._zoneName)
|
||||
if zone == nil then
|
||||
self._logger:error("Zone not found: " .. self._zoneName)
|
||||
return nil
|
||||
end
|
||||
|
||||
---@type SpearheadTriggerZone
|
||||
local spearheadZone = {
|
||||
name = self._zoneName .. "_noland",
|
||||
location = zone.location,
|
||||
verts = vecs,
|
||||
radius = 0,
|
||||
zone_type = "Polygon"
|
||||
}
|
||||
|
||||
return spearheadZone
|
||||
end
|
||||
|
||||
if Spearhead == nil then Spearhead = {} end
|
||||
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
---@field private _cleanup_units table<string, boolean>
|
||||
---@field private _airbase Airbase?
|
||||
---@field private _initialSide number?
|
||||
---@field private _supplyHubs Array<SupplyHub>
|
||||
local StageBase = {}
|
||||
|
||||
---comment
|
||||
---@param databaseManager Database
|
||||
---@param logger table
|
||||
---@param airbaseName string
|
||||
---@return StageBase
|
||||
---@return StageBase?
|
||||
function StageBase.New(databaseManager, logger, airbaseName)
|
||||
|
||||
StageBase.__index = StageBase
|
||||
@@ -26,45 +27,51 @@ function StageBase.New(databaseManager, logger, airbaseName)
|
||||
self._red_groups = {}
|
||||
self._blue_groups = {}
|
||||
self._cleanup_units = {}
|
||||
self._supplyHubs = {}
|
||||
|
||||
self._airbase = Airbase.getByName(airbaseName)
|
||||
self._initialSide = Spearhead.DcsUtil.getStartingCoalition(self._airbase)
|
||||
|
||||
do --init
|
||||
|
||||
local airbaseData = databaseManager:getAirbaseDataForZone(airbaseName)
|
||||
if airbaseData == nil then
|
||||
logger:error("Airbase data not found for airbase: " .. airbaseName)
|
||||
return nil
|
||||
end
|
||||
|
||||
---@type table<string, Vec3>
|
||||
local redUnitsPos = {}
|
||||
|
||||
---@type table<string, Vec3>
|
||||
local blueUnitsPos = {}
|
||||
|
||||
do -- fill tables
|
||||
local redGroups = databaseManager:getRedGroupsAtAirbase(airbaseName)
|
||||
if redGroups then
|
||||
for _, groupName in pairs(redGroups) do
|
||||
local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||
table.insert(self._red_groups, shGroup)
|
||||
for _, groupName in pairs(airbaseData.RedGroups) do
|
||||
local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||
table.insert(self._red_groups, shGroup)
|
||||
|
||||
for _, unit in pairs(shGroup:GetUnits()) do
|
||||
redUnitsPos[unit:getName()] = unit:getPoint()
|
||||
end
|
||||
|
||||
shGroup:Destroy()
|
||||
end
|
||||
for _, unit in pairs(shGroup:GetUnits()) do
|
||||
redUnitsPos[unit:getName()] = unit:getPoint()
|
||||
end
|
||||
|
||||
local blueGroups = databaseManager:getBlueGroupsAtAirbase(airbaseName)
|
||||
if blueGroups then
|
||||
for _, groupName in pairs(blueGroups) do
|
||||
local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||
table.insert(self._blue_groups, shGroup)
|
||||
shGroup:Destroy()
|
||||
end
|
||||
|
||||
for _, unit in pairs(shGroup:GetUnits()) do
|
||||
blueUnitsPos[unit:getName()] = unit:getPoint()
|
||||
end
|
||||
for _, groupName in pairs(airbaseData.BlueGroups) do
|
||||
local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName)
|
||||
table.insert(self._blue_groups, shGroup)
|
||||
|
||||
shGroup:Destroy()
|
||||
for _, unit in pairs(shGroup:GetUnits()) do
|
||||
blueUnitsPos[unit:getName()] = unit:getPoint()
|
||||
end
|
||||
|
||||
shGroup:Destroy()
|
||||
end
|
||||
|
||||
for _, supplyHubName in pairs(airbaseData.supplyHubNames) do
|
||||
local supplyHub = Spearhead.classes.stageClasses.SpecialZones.SupplyHub.new(databaseManager, logger, supplyHubName)
|
||||
if supplyHub then
|
||||
table.insert(self._supplyHubs, supplyHub)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -151,6 +158,10 @@ function StageBase:ActivateBlueStage()
|
||||
|
||||
self:CleanRedUnits()
|
||||
self:SpawnBlueUnits()
|
||||
|
||||
for _, hub in pairs(self._supplyHubs) do
|
||||
hub:Activate()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
---@class SupplyHub
|
||||
---@field private _database Database
|
||||
---@field private _logger Logger
|
||||
---@field private _zoneName string
|
||||
---@field private _zone SpearheadTriggerZone?
|
||||
---@field private _supplyUnitsTracker SupplyUnitsTracker
|
||||
---@field private _isCommmandAdded table<string, boolean>
|
||||
---@field private _missionCommandsHelper MissionCommandsHelper
|
||||
---@field private _inZone table<string, boolean>
|
||||
---@field private _drawID number
|
||||
---@field private _cargoInUnits table<table<string, number>>
|
||||
---@field private _activeAtStart boolean
|
||||
---@field private _active boolean
|
||||
local SupplyHub = {}
|
||||
|
||||
---@param database Database
|
||||
---@param logger Logger
|
||||
---@param zoneName string
|
||||
---@return SupplyHub?
|
||||
function SupplyHub.new(database, logger, zoneName)
|
||||
|
||||
SupplyHub.__index = SupplyHub
|
||||
local self = setmetatable({}, SupplyHub)
|
||||
|
||||
self._database = database
|
||||
self._logger = logger
|
||||
self._zoneName = zoneName
|
||||
|
||||
local split = Spearhead.Util.split_string(zoneName, "_")
|
||||
if string.lower(split[2]) == "a" then
|
||||
self._activeAtStart = true
|
||||
else
|
||||
self._activeAtStart = false
|
||||
end
|
||||
|
||||
self._zone = Spearhead.DcsUtil.getZoneByName(zoneName)
|
||||
|
||||
self._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logger.LogLevel)
|
||||
self._inZone = {}
|
||||
self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(logger.LogLevel)
|
||||
|
||||
self._logger:debug("Creating Supply Hub zone: " .. self._zoneName)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function SupplyHub:IsActiveFromStart()
|
||||
return self._activeAtStart
|
||||
end
|
||||
|
||||
function SupplyHub:GetZoneName()
|
||||
return self._zoneName
|
||||
end
|
||||
|
||||
---@return SpearheadTriggerZone?
|
||||
function SupplyHub:GetZone()
|
||||
return self._zone
|
||||
end
|
||||
|
||||
function SupplyHub:Activate()
|
||||
if self._active == true then
|
||||
return
|
||||
end
|
||||
|
||||
self._active = true
|
||||
|
||||
self._logger:debug("Activating Supply Hub zone: " .. self._zoneName)
|
||||
|
||||
local zone = Spearhead.DcsUtil.getZoneByName(self._zoneName)
|
||||
if zone and self._drawID == nil then
|
||||
---@type DrawColor
|
||||
local fillColor = { r=0, g=1, b=0, a=0.2 }
|
||||
---@type DrawColor
|
||||
local lineColor = { r=0, g=1, b=0, a=1}
|
||||
local lineStyle = 1
|
||||
self._drawID = Spearhead.DcsUtil.DrawZone(zone, lineColor, fillColor, lineStyle)
|
||||
end
|
||||
|
||||
self._supplyUnitsTracker:RegisterHub(self)
|
||||
|
||||
end
|
||||
|
||||
|
||||
if not Spearhead then Spearhead = {} end
|
||||
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
|
||||
if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end
|
||||
Spearhead.classes.stageClasses.SpecialZones.SupplyHub = SupplyHub
|
||||
@@ -14,6 +14,7 @@
|
||||
--- @field miscGroups Array<SpearheadGroup>
|
||||
--- @field maxMissions integer
|
||||
--- @field farps Array<FarpZone>
|
||||
--- @field supplyHubs Array<SupplyHub>
|
||||
|
||||
--- @class StageInitData
|
||||
--- @field stageZoneName string
|
||||
@@ -90,7 +91,9 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
|
||||
maxMissions = stageConfig.maxMissionsPerStage,
|
||||
farps = {},
|
||||
missionsByName = {},
|
||||
supplyHubs = {}
|
||||
}
|
||||
|
||||
self._activeStage = -99
|
||||
self._preActivated = false
|
||||
self._stageConfig = stageConfig or {}
|
||||
@@ -110,6 +113,11 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority
|
||||
table.insert(self._db.farps, farp)
|
||||
end
|
||||
|
||||
local supplyHubNames = database:getSupplyHubsInStage(self.zoneName)
|
||||
for _, supplyHubName in pairs(supplyHubNames) do
|
||||
local supplyHub = Spearhead.classes.stageClasses.SpecialZones.SupplyHub.new(database, logger, supplyHubName)
|
||||
table.insert(self._db.supplyHubs, supplyHub)
|
||||
end
|
||||
|
||||
self._logger:info("Initiating new Stage with name: " .. self.zoneName)
|
||||
|
||||
@@ -404,6 +412,12 @@ function Stage:ActivateStage()
|
||||
end
|
||||
end
|
||||
|
||||
for _, supplyHub in pairs(self._db.supplyHubs) do
|
||||
if supplyHub:IsActiveFromStart() == true then
|
||||
supplyHub:Activate()
|
||||
end
|
||||
end
|
||||
|
||||
timer.scheduleFunction(self.CheckContinuousAsync, self, timer.getTime() + 3)
|
||||
end
|
||||
|
||||
@@ -476,6 +490,10 @@ function Stage:ActivateBlueGroups()
|
||||
farp:Activate()
|
||||
end
|
||||
end
|
||||
|
||||
for _, supplyHub in pairs(self._db.supplyHubs) do
|
||||
supplyHub:Activate()
|
||||
end
|
||||
end
|
||||
|
||||
---@return number strike
|
||||
|
||||
@@ -18,7 +18,7 @@ function PrimaryStage.New(database, stageConfig, logger, initData)
|
||||
local self = setmetatable({}, { __index = PrimaryStage }) --[[@as PrimaryStage]]
|
||||
self:superNew(database, stageConfig, logger, initData, "primary")
|
||||
return self
|
||||
|
||||
|
||||
end
|
||||
|
||||
if not Spearhead.classes then Spearhead.classes = {} end
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
---@field lastUpdate number @last update time
|
||||
---@field updateContinuous fun(self: MissionCommandsHelper, time: number): number @function to update commands continuously
|
||||
---@field pinnedByGroup table<string, Mission> @table of pinned missions by group ID
|
||||
---@field private _supplyHubGroups table<string, boolean> @table of supply hub groups by their ID
|
||||
---@field private _logger Logger @logger instance for logging
|
||||
---@field private _supplyUnitsTracker SupplyUnitsTracker @supply units tracker instance
|
||||
local MissionCommandsHelper = {}
|
||||
MissionCommandsHelper.__index = MissionCommandsHelper
|
||||
|
||||
local id = 0
|
||||
|
||||
local instance = nil
|
||||
|
||||
@@ -16,21 +19,25 @@ local instance = nil
|
||||
---@param logLevel string @log level for the logger
|
||||
function MissionCommandsHelper.getOrCreate(logLevel)
|
||||
if instance == nil then
|
||||
local self = setmetatable({}, MissionCommandsHelper)
|
||||
instance = setmetatable({}, MissionCommandsHelper)
|
||||
|
||||
self._logger = Spearhead.LoggerTemplate.new("MissionCommandsHelper", logLevel)
|
||||
instance._logger = Spearhead.LoggerTemplate.new("MissionCommandsHelper", logLevel)
|
||||
|
||||
self.missionsByCode = {}
|
||||
self.enabledByCode = {}
|
||||
self.updateNeeded = false
|
||||
self.pinnedByGroup = {}
|
||||
self.lastUpdate = 0
|
||||
instance._logger:info("Creating MissionCommandsHelper instance")
|
||||
|
||||
instance.missionsByCode = {}
|
||||
instance.enabledByCode = {}
|
||||
instance.updateNeeded = false
|
||||
instance.pinnedByGroup = {}
|
||||
instance.lastUpdate = 0
|
||||
instance._supplyHubGroups = {}
|
||||
instance._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logLevel)
|
||||
|
||||
---comment
|
||||
---@param selfA MissionCommandsHelper
|
||||
---@param time number
|
||||
---@return number
|
||||
self.updateContinuous = function(selfA, time)
|
||||
instance.updateContinuous = function(selfA, time)
|
||||
if selfA.updateNeeded == false then
|
||||
return time + 10
|
||||
end
|
||||
@@ -49,14 +56,63 @@ function MissionCommandsHelper.getOrCreate(logLevel)
|
||||
return time + 10
|
||||
end
|
||||
|
||||
timer.scheduleFunction(self.updateContinuous, self, timer.getTime() + 5)
|
||||
Spearhead.Events.AddOnPlayerEnterUnitListener(self)
|
||||
instance = self
|
||||
timer.scheduleFunction(instance.updateContinuous, instance, timer.getTime() + 5)
|
||||
Spearhead.Events.AddOnPlayerEnterUnitListener(instance)
|
||||
|
||||
end
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
---@param mission Mission
|
||||
function MissionCommandsHelper:AddMissionToCommands(mission)
|
||||
self._logger:debug("Adding mission to commands: [" .. mission.code .. "]" .. mission.name)
|
||||
self.missionsByCode[tostring(mission.code)] = mission
|
||||
self.enabledByCode[tostring(mission.code)] = true
|
||||
self.updateNeeded = true
|
||||
end
|
||||
|
||||
---Removes a mission from the F10 commands menu
|
||||
---@param mission Mission
|
||||
function MissionCommandsHelper:RemoveMissionToCommands(mission)
|
||||
self.enabledByCode[tostring(mission.code)] = false
|
||||
self.updateNeeded = true
|
||||
end
|
||||
|
||||
---@param groupID number
|
||||
function MissionCommandsHelper:MarkUnitInSupplyHub(groupID)
|
||||
self._logger:debug("Marking unit in supply hub: " .. tostring(groupID))
|
||||
local updateNeeded = false
|
||||
if self._supplyHubGroups[tostring(groupID)] ~= true then
|
||||
updateNeeded = true
|
||||
end
|
||||
|
||||
self._supplyHubGroups[tostring(groupID)] = true
|
||||
if updateNeeded == true then self:updateCommandsForGroup(groupID) end
|
||||
end
|
||||
|
||||
|
||||
---@param groupID number
|
||||
function MissionCommandsHelper:MarkUnitOutsideSupplyHub(groupID)
|
||||
self._logger:debug("Marking unit outide supply hub: " .. tostring(groupID))
|
||||
local updateNeeded = false
|
||||
if self._supplyHubGroups[tostring(groupID)] == true then
|
||||
updateNeeded = true
|
||||
end
|
||||
|
||||
self._supplyHubGroups[tostring(groupID)] = false
|
||||
if updateNeeded == true then self:updateCommandsForGroup(groupID) end
|
||||
end
|
||||
|
||||
|
||||
|
||||
---@param unit Unit
|
||||
function MissionCommandsHelper:OnPlayerEntersUnit(unit)
|
||||
if unit then
|
||||
local group = unit:getGroup()
|
||||
if group then self:updateCommandsForGroup(group:getID()) end
|
||||
end
|
||||
end
|
||||
|
||||
---@class MissionBriefingRequestedArgs
|
||||
---@field mission Mission @the mission object
|
||||
@@ -72,6 +128,22 @@ local missionBriefingRequested = function(args)
|
||||
mission:ShowBriefing(groupID)
|
||||
end
|
||||
|
||||
---@class MarkRequestedArgs
|
||||
---@field mission Mission @the mission object
|
||||
---@field groupId integer @the group ID of the player requesting the briefing
|
||||
|
||||
---@param args MarkRequestedArgs
|
||||
local markRequested = function(args)
|
||||
|
||||
local mission = args.mission
|
||||
if not mission then return end
|
||||
|
||||
if mission.missionType == "LOGISTICS" then
|
||||
mission:MarkMissionAreaToGroup(args.groupId)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
---@class PinMissionCommandArgs
|
||||
---@field self MissionCommandsHelper @the MissionCommandsHelper instance
|
||||
@@ -114,7 +186,7 @@ function MissionCommandsHelper:AddOverviewCommand(groupID)
|
||||
end
|
||||
end
|
||||
|
||||
return string.format("[%s] %-15s %-20s %10s nM\n", mission.code, mission.missionTypeDisplay, mission.name, distanceText)
|
||||
return string.format("[%s]\t%s \t%s \t%s nM\n", mission.code, mission.missionTypeDisplay, mission.name, distanceText)
|
||||
end
|
||||
|
||||
---Primary missions
|
||||
@@ -161,11 +233,11 @@ function MissionCommandsHelper:AddPinnedMission(groupID)
|
||||
|
||||
end
|
||||
|
||||
|
||||
---comment
|
||||
---@param groupID number
|
||||
function MissionCommandsHelper:updateCommandsForGroup(groupID)
|
||||
|
||||
self._logger:debug("Updating commands for group: " .. tostring(groupID))
|
||||
|
||||
self:AddPinnedMission(groupID)
|
||||
self:AddOverviewCommand(groupID)
|
||||
|
||||
@@ -180,6 +252,9 @@ function MissionCommandsHelper:updateCommandsForGroup(groupID)
|
||||
end
|
||||
end
|
||||
|
||||
self:AddSupplyHubCommandsIfApplicable(groupID)
|
||||
self:AddCargoCommands(groupID)
|
||||
|
||||
---@param id number
|
||||
local clearView = function(id)
|
||||
trigger.action.outTextForGroup(id, "clearing...", 1, true)
|
||||
@@ -192,7 +267,9 @@ end
|
||||
|
||||
local folderNames = {
|
||||
primary = "Primary Missions",
|
||||
secondary = "Secondary Missions"
|
||||
secondary = "Secondary Missions",
|
||||
supplyHub = "Supply Hub",
|
||||
cargo = "Cargo"
|
||||
}
|
||||
|
||||
|
||||
@@ -221,31 +298,135 @@ function MissionCommandsHelper:addMissionCommands(groupId, mission)
|
||||
local missionFolderName = "[" .. mission.code .. "]" .. mission.name
|
||||
missionCommands.addSubMenuForGroup(groupId, missionFolderName, path)
|
||||
table.insert(path, missionFolderName)
|
||||
missionCommands.addCommandForGroup(groupId, "Briefing", path, missionBriefingRequested,{ groupId = groupId, mission = mission })
|
||||
missionCommands.addCommandForGroup(groupId, "Pin", path, pinMissionCommand, { self = self, groupId = groupId, mission = mission })
|
||||
|
||||
---@type MissionBriefingRequestedArgs
|
||||
local missionBriefingRequestedArgs = { groupId = groupId, mission = mission }
|
||||
missionCommands.addCommandForGroup(groupId, "Briefing", path, missionBriefingRequested,missionBriefingRequestedArgs)
|
||||
|
||||
---@type PinMissionCommandArgs
|
||||
local pinMissionCommandArgs = { self = self, groupId = groupId, mission = mission }
|
||||
missionCommands.addCommandForGroup(groupId, "Pin", path, pinMissionCommand, pinMissionCommandArgs)
|
||||
|
||||
if mission.missionType == "LOGISTICS" then
|
||||
---@type MarkRequestedArgs
|
||||
local markRequestArgs = { groupId = groupId, mission = mission }
|
||||
missionCommands.addCommandForGroup(groupId, "Mark", path, markRequested, markRequestArgs)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
---@param mission Mission
|
||||
function MissionCommandsHelper:AddMissionToCommands(mission)
|
||||
self._logger:debug("Adding mission to commands: [" .. mission.code .. "]" .. mission.name)
|
||||
self.missionsByCode[tostring(mission.code)] = mission
|
||||
self.enabledByCode[tostring(mission.code)] = true
|
||||
self.updateNeeded = true
|
||||
---@private
|
||||
---@param groupID integer
|
||||
function MissionCommandsHelper:AddSupplyHubCommandsIfApplicable(groupID)
|
||||
|
||||
if self._supplyHubGroups[tostring(groupID)] ~= true then return end
|
||||
|
||||
self._logger:debug("Adding supply hub commands for group: " .. tostring(groupID))
|
||||
|
||||
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
|
||||
if group == nil then return end
|
||||
|
||||
local unit = group:getUnit(1)
|
||||
if unit == nil then return end
|
||||
|
||||
---@class LoadCargoCommandParams
|
||||
---@field unitID number
|
||||
---@field groupID number
|
||||
---@field crateType CrateType
|
||||
---@field supplyUnitsTracker SupplyUnitsTracker
|
||||
|
||||
---comment
|
||||
---@param params LoadCargoCommandParams
|
||||
local loadCargoCommand = function(params)
|
||||
local crateType = params.crateType
|
||||
local supplyUnitsTracker = params.supplyUnitsTracker
|
||||
if supplyUnitsTracker then
|
||||
supplyUnitsTracker:UnitRequestCrateLoading(params.groupID, crateType)
|
||||
end
|
||||
end
|
||||
|
||||
local path = { [1] = folderNames.supplyHub }
|
||||
|
||||
|
||||
---@type LoadCargoCommandParams
|
||||
local farpParams1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_1000", supplyUnitsTracker = self._supplyUnitsTracker }
|
||||
missionCommands.addCommandForGroup(groupID, "Load FARP Crate (1000)", path, loadCargoCommand, farpParams1000)
|
||||
|
||||
---@type LoadCargoCommandParams
|
||||
local farpParams2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker }
|
||||
missionCommands.addCommandForGroup(groupID, "Load FARP Crate (2000)", path, loadCargoCommand, farpParams2000)
|
||||
|
||||
---@type LoadCargoCommandParams
|
||||
local samParms1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_1000", supplyUnitsTracker = self._supplyUnitsTracker }
|
||||
missionCommands.addCommandForGroup(groupID, "Load SAM Crate (1000)", path, loadCargoCommand, samParms1000)
|
||||
|
||||
---@type LoadCargoCommandParams
|
||||
local samParms2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker }
|
||||
missionCommands.addCommandForGroup(groupID, "Load SAM Crate (2000)", path, loadCargoCommand, samParms2000)
|
||||
end
|
||||
|
||||
---Removes a mission from the F10 commands menu
|
||||
---@param mission Mission
|
||||
function MissionCommandsHelper:RemoveMissionToCommands(mission)
|
||||
self.enabledByCode[tostring(mission.code)] = false
|
||||
self.updateNeeded = true
|
||||
function MissionCommandsHelper:AddCargoCommands(groupID)
|
||||
|
||||
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
|
||||
if group == nil then return end
|
||||
|
||||
local unit = group:getUnit(1)
|
||||
if unit == nil then return end
|
||||
|
||||
---@class UnloadCargoCommandParams
|
||||
---@field unitID number
|
||||
---@field crateType CrateType
|
||||
---@field supplyUnitsTracker SupplyUnitsTracker
|
||||
|
||||
---comment
|
||||
---@param params UnloadCargoCommandParams
|
||||
local unloadCargoCommand = function(params)
|
||||
local unitID = params.unitID
|
||||
local crateType = params.crateType
|
||||
params.supplyUnitsTracker:UnloadRequested(unitID, crateType)
|
||||
end
|
||||
|
||||
local cargo = self._supplyUnitsTracker:GetCargoInUnit(unit:getID())
|
||||
if cargo then
|
||||
for cargoType, amount in pairs(cargo) do
|
||||
local cargoConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(cargoType)
|
||||
if cargoConfig then
|
||||
for i = 1, amount do
|
||||
local path = { [1] = folderNames.cargo }
|
||||
---@type UnloadCargoCommandParams
|
||||
local params = { unitID = unit:getID(), crateType = cargoType, supplyUnitsTracker = self._supplyUnitsTracker }
|
||||
missionCommands.addCommandForGroup(groupID, "Unload " .. cargoConfig.displayName, path, unloadCargoCommand, params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
---@private
|
||||
---@param groupId integer
|
||||
function MissionCommandsHelper:addMissionFolders(groupId)
|
||||
|
||||
missionCommands.addSubMenuForGroup(groupId, folderNames.primary)
|
||||
missionCommands.addSubMenuForGroup(groupId, folderNames.secondary)
|
||||
|
||||
if self._supplyHubGroups[tostring(groupId)] == true then
|
||||
self._logger:debug("Adding supply hub commands folder for group: " .. tostring(groupId))
|
||||
missionCommands.addSubMenuForGroup(groupId, folderNames.supplyHub)
|
||||
end
|
||||
|
||||
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId)
|
||||
if group == nil then return end
|
||||
|
||||
local unit = group:getUnit(1)
|
||||
if unit == nil then return end
|
||||
|
||||
local cargo = self._supplyUnitsTracker:GetCargoInUnit(unit:getID())
|
||||
if cargo ~= nil then
|
||||
missionCommands.addSubMenuForGroup(groupId, folderNames.cargo)
|
||||
end
|
||||
end
|
||||
|
||||
---@private
|
||||
@@ -253,6 +434,8 @@ end
|
||||
function MissionCommandsHelper:removeMissionFolders(groupId)
|
||||
missionCommands.removeItemForGroup(groupId, { folderNames.primary })
|
||||
missionCommands.removeItemForGroup(groupId, { folderNames.secondary })
|
||||
missionCommands.removeItemForGroup(groupId, { folderNames.supplyHub })
|
||||
missionCommands.removeItemForGroup(groupId, { folderNames.cargo })
|
||||
end
|
||||
|
||||
---@private
|
||||
@@ -264,15 +447,6 @@ function MissionCommandsHelper:ResetFolders(groupID)
|
||||
self:addMissionFolders(groupID)
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param unit Unit
|
||||
function MissionCommandsHelper:OnPlayerEntersUnit(unit)
|
||||
if unit then
|
||||
local group = unit:getGroup()
|
||||
if group then self:updateCommandsForGroup(group:getID()) end
|
||||
end
|
||||
end
|
||||
|
||||
if Spearhead == nil then Spearhead = {} end
|
||||
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
|
||||
---@alias SupplyType
|
||||
---| "FARP_CRATE"
|
||||
---| "SAM_CRATE"
|
||||
|
||||
---@alias CrateType
|
||||
---| "FARP_CRATE_500"
|
||||
---| "FARP_CRATE_1000"
|
||||
---| "FARP_CRATE_2000"
|
||||
---| "SAM_CRATE_500"
|
||||
---| "SAM_CRATE_1000"
|
||||
---| "SAM_CRATE_2000"
|
||||
|
||||
---@class SupplyConfig
|
||||
---@field type SupplyType
|
||||
---@field weight number
|
||||
---@field staticType string
|
||||
---@field displayName string
|
||||
|
||||
---@type table<CrateType, SupplyConfig>
|
||||
local SupplyConfig = {
|
||||
["FARP_CRATE_500"] = {
|
||||
type = "FARP_CRATE",
|
||||
weight = 500,
|
||||
displayName = "FARP Crate (500)",
|
||||
staticType = "container_cargo",
|
||||
},
|
||||
["FARP_CRATE_1000"] = {
|
||||
type = "FARP_CRATE",
|
||||
weight = 1000,
|
||||
displayName = "FARP Crate (1000)",
|
||||
staticType = "container_cargo",
|
||||
},
|
||||
["FARP_CRATE_2000"] = {
|
||||
type = "FARP_CRATE",
|
||||
weight = 2000,
|
||||
displayName = "FARP Crate (2000)",
|
||||
staticType = "container_cargo",
|
||||
},
|
||||
["SAM_CRATE_500"] = {
|
||||
type = "SAM_CRATE",
|
||||
weight = 1000,
|
||||
displayName = "SAM Crate (500)",
|
||||
staticType = "container_cargo",
|
||||
},
|
||||
["SAM_CRATE_1000"] = {
|
||||
type = "SAM_CRATE",
|
||||
weight = 1000,
|
||||
displayName = "SAM Crate (1000)",
|
||||
staticType = "container_cargo",
|
||||
},
|
||||
["SAM_CRATE_2000"] = {
|
||||
type = "SAM_CRATE",
|
||||
weight = 2000,
|
||||
displayName = "SAM Crate (2000)",
|
||||
staticType = "container_cargo",
|
||||
},
|
||||
}
|
||||
|
||||
---@class MaxLoadConfig
|
||||
---@field maxInternalLoad number
|
||||
|
||||
---@type table<string, MaxLoadConfig>
|
||||
MaxLoadConfig = {
|
||||
["Mi-8MT"] = {
|
||||
maxInternalLoad = 4000,
|
||||
},
|
||||
["CH-47Fbl1"] = {
|
||||
maxInternalLoad = 10000
|
||||
},
|
||||
["Mi-24P"] = {
|
||||
maxInternalLoad = 2000
|
||||
},
|
||||
["UH-1H"] = {
|
||||
maxInternalLoad = 2000
|
||||
}
|
||||
}
|
||||
|
||||
---@class SupplyConfigHelper
|
||||
local SupplyConfigHelper = {}
|
||||
|
||||
---comment
|
||||
---@param name string
|
||||
---@return SupplyConfig?
|
||||
function SupplyConfigHelper.fromObjectName(name)
|
||||
for configName, config in pairs(SupplyConfig) do
|
||||
if Spearhead.Util.startswith(name, configName, true) == true then
|
||||
return config
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param type CrateType
|
||||
function SupplyConfigHelper.getSupplyConfig(type)
|
||||
return SupplyConfig[type]
|
||||
end
|
||||
|
||||
if Spearhead == nil then Spearhead = {} end
|
||||
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
|
||||
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
|
||||
if Spearhead.classes.stageClasses.helpers.supplies == nil then Spearhead.classes.stageClasses.helpers.supplies = {} end
|
||||
Spearhead.classes.stageClasses.helpers.supplies.MaxLoadConfig = MaxLoadConfig
|
||||
Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper = SupplyConfigHelper
|
||||
|
||||
@@ -0,0 +1,407 @@
|
||||
---@class SupplyUnitsTracker
|
||||
---@field private _supplyUnitsByName table<string, Unit>
|
||||
---@field private _cargoInUnits table<string, table<CrateType, number>>
|
||||
---@field private _logger Logger
|
||||
---@field private _unitPositions table<string, Vec3>
|
||||
---@field private _commandsHelper MissionCommandsHelper
|
||||
---@field private _droppedCrates table<string, StaticObject>
|
||||
---@field private _registeredHubs table<SupplyHub, boolean>
|
||||
local SupplyUnitsTracker = {}
|
||||
SupplyUnitsTracker.__index = SupplyUnitsTracker
|
||||
|
||||
-- A single class tracking all units is more than enough.
|
||||
local singleton = nil
|
||||
|
||||
---comment
|
||||
---@param logLevel LogLevel
|
||||
---@return SupplyUnitsTracker
|
||||
function SupplyUnitsTracker.getOrCreate(logLevel)
|
||||
|
||||
if singleton == nil then
|
||||
singleton = setmetatable({}, SupplyUnitsTracker)
|
||||
singleton._logger = Spearhead.LoggerTemplate.new("SupplyUnitsTracker", logLevel)
|
||||
singleton._unitPositions = {}
|
||||
singleton._cargoInUnits = {}
|
||||
singleton._supplyUnitsByName = {}
|
||||
singleton._droppedCrates = {}
|
||||
singleton._registeredHubs = {}
|
||||
|
||||
singleton._commandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(singleton._logger.LogLevel)
|
||||
|
||||
Spearhead.Events.AddOnPlayerEnterUnitListener(singleton)
|
||||
|
||||
---@param selfA SupplyUnitsTracker
|
||||
local function updateTask(selfA, time)
|
||||
|
||||
selfA:Update()
|
||||
return time + 15
|
||||
end
|
||||
|
||||
timer.scheduleFunction(updateTask, singleton, timer.getTime() + 15)
|
||||
|
||||
---comment
|
||||
---@param selfA SupplyUnitsTracker
|
||||
---@param time number
|
||||
---@return number
|
||||
local function checkUnitsInZone(selfA, time)
|
||||
pcall(function()
|
||||
selfA:CheckUnitsInZones()
|
||||
end)
|
||||
return time + 5
|
||||
end
|
||||
|
||||
timer.scheduleFunction(checkUnitsInZone, singleton, timer.getTime() + 5)
|
||||
|
||||
end
|
||||
|
||||
return singleton
|
||||
|
||||
end
|
||||
|
||||
---@param unit Unit
|
||||
function SupplyUnitsTracker:OnPlayerEntersUnit(unit)
|
||||
if unit == nil then return end
|
||||
|
||||
if self:IsSupplyUnit(unit) == true then
|
||||
self._supplyUnitsByName[unit:getName()] = unit
|
||||
end
|
||||
end
|
||||
|
||||
function SupplyUnitsTracker:Update()
|
||||
local players = Spearhead.DcsUtil.getAllPlayerUnits()
|
||||
for _, player in pairs(players) do
|
||||
if player ~= nil and player:isExist() and self:IsSupplyUnit(player) == true then
|
||||
self._supplyUnitsByName[player:getName()] = player
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@private
|
||||
function SupplyUnitsTracker:IsSupplyUnit(unit)
|
||||
if unit == nil then return false end
|
||||
|
||||
if unit:hasAttribute("Transport helicopters") then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param unitID number
|
||||
---@param crateType CrateType
|
||||
function SupplyUnitsTracker:AddCargoToUnit(unitID, crateType)
|
||||
|
||||
if unitID == nil or crateType == nil then return end
|
||||
|
||||
local unit = Spearhead.DcsUtil.GetPLayerUnitByID(unitID)
|
||||
if unit == nil then return end
|
||||
|
||||
if self._cargoInUnits[unitID] == nil then
|
||||
self._cargoInUnits[unitID] = {}
|
||||
end
|
||||
|
||||
if self._cargoInUnits[unitID][crateType] == nil then
|
||||
self._cargoInUnits[unitID][crateType] = 0
|
||||
end
|
||||
|
||||
self._cargoInUnits[unitID][crateType] = self._cargoInUnits[unitID][crateType] + 1
|
||||
|
||||
end
|
||||
|
||||
---@param unitID number
|
||||
---@param crateType CrateType
|
||||
function SupplyUnitsTracker:RemoveCargoFromUnit(unitID, crateType)
|
||||
|
||||
if unitID == nil or crateType == nil then return end
|
||||
|
||||
local unitIDStr = tostring(unitID)
|
||||
if self._cargoInUnits[unitIDStr] == nil then return end
|
||||
|
||||
if self._cargoInUnits[unitIDStr][crateType] == nil then return end
|
||||
|
||||
self._cargoInUnits[unitIDStr][crateType] = self._cargoInUnits[unitIDStr][crateType] - 1
|
||||
|
||||
local hasCargo = false
|
||||
for type, count in pairs(self._cargoInUnits[unitIDStr]) do
|
||||
if count > 0 then
|
||||
hasCargo = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if hasCargo == false then
|
||||
self._cargoInUnits[unitIDStr] = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function SupplyUnitsTracker:CheckUnitsInZones()
|
||||
|
||||
for name, unit in pairs(self._supplyUnitsByName) do
|
||||
if unit ~= nil and unit:isExist() == true then
|
||||
self._logger:debug("Checking unit: " .. unit:getName())
|
||||
local pos = unit:getPoint()
|
||||
|
||||
local group = unit:getGroup()
|
||||
|
||||
for hub, enabled in pairs(self._registeredHubs) do
|
||||
if enabled == true then
|
||||
local zone = hub:GetZone()
|
||||
if zone ~= nil then
|
||||
if Spearhead.Util.is3dPointInZone(pos, zone) then
|
||||
self._commandsHelper:MarkUnitInSupplyHub(group:getID())
|
||||
else
|
||||
self._commandsHelper:MarkUnitOutsideSupplyHub(group:getID())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self._unitPositions[unit:getID()] = pos
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SupplyUnitsTracker:RegisterHub(hub)
|
||||
if hub == nil then return end
|
||||
|
||||
if self._registeredHubs[hub] == nil then
|
||||
self._registeredHubs[hub] = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---@param unitID number
|
||||
---@return table<CrateType, number>?
|
||||
function SupplyUnitsTracker:GetCargoInUnit(unitID)
|
||||
if unitID == nil then return end
|
||||
|
||||
local unitIDStr = tostring(unitID)
|
||||
if self._cargoInUnits[unitIDStr] == nil then return end
|
||||
|
||||
return self._cargoInUnits[unitIDStr]
|
||||
end
|
||||
|
||||
---@return table<string, Unit>
|
||||
function SupplyUnitsTracker:GetUnits()
|
||||
return self._supplyUnitsByName
|
||||
end
|
||||
|
||||
local cargoCount = 0
|
||||
function SupplyUnitsTracker:UnloadRequested(unitID, crateType)
|
||||
|
||||
self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType)
|
||||
|
||||
local unit = Spearhead.DcsUtil.GetPLayerUnitByID(unitID)
|
||||
if unit == nil or unit:isExist() == false then return end
|
||||
local group = unit:getGroup()
|
||||
if group == nil then
|
||||
return
|
||||
end
|
||||
|
||||
self:RemoveCargoFromUnit(unitID, crateType)
|
||||
|
||||
local cargoConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType)
|
||||
|
||||
if cargoConfig == nil then
|
||||
self._logger:error("Invalid crate type: " .. crateType)
|
||||
return
|
||||
end
|
||||
|
||||
local cargoPos = self:GetCargoPlacePosition(unit)
|
||||
|
||||
cargoCount = cargoCount + 1
|
||||
local cargoSpawnObject = {
|
||||
name = crateType .. "_" .. cargoCount,
|
||||
type = cargoConfig.staticType,
|
||||
x = cargoPos.x,
|
||||
y = cargoPos.z,
|
||||
}
|
||||
|
||||
local spawned = coalition.addStaticObject(unit:getCoalition(), cargoSpawnObject)
|
||||
|
||||
self._droppedCrates[cargoSpawnObject.name] = spawned
|
||||
self._commandsHelper:updateCommandsForGroup(group:getID())
|
||||
end
|
||||
|
||||
---@return table<string,StaticObject>
|
||||
function SupplyUnitsTracker:GetCargoCratesDropped()
|
||||
return self._droppedCrates
|
||||
end
|
||||
|
||||
---Loads a crate directly into the unit
|
||||
---@param groupID number
|
||||
---@param crateType CrateType
|
||||
function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType)
|
||||
|
||||
self._logger:debug("UnitRequestCrateLoading called with groupID: " .. groupID .. " and crateType: " .. crateType)
|
||||
|
||||
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
|
||||
if group ~= nil then
|
||||
|
||||
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType)
|
||||
if crateConfig == nil then
|
||||
self._logger:error("Invalid crate type: " .. crateType)
|
||||
return
|
||||
end
|
||||
|
||||
local unit = group:getUnit(1)
|
||||
|
||||
if unit == nil then return end
|
||||
if unit:isExist() == false then return end
|
||||
|
||||
|
||||
if unit:inAir() == true then
|
||||
trigger.action.outTextForUnit(unit:getID(), "Land first before crates can be loaded", 10)
|
||||
return
|
||||
end
|
||||
|
||||
trigger.action.outTextForUnit(unit:getID(), "Loading crate of type " .. crateType, 13)
|
||||
|
||||
---@class LoadCargoParams
|
||||
---@field self SupplyUnitsTracker
|
||||
---@field unit Unit
|
||||
---@field groupID number
|
||||
---@field crateType CrateType
|
||||
|
||||
---@param params LoadCargoParams
|
||||
local LoadCrateTask = function(params)
|
||||
|
||||
local loaded = params.self:TryLoadCrateInUnit(params.unit, params.crateType)
|
||||
if loaded ~= false then
|
||||
trigger.action.outTextForUnit(unit:getID(), "Loaded crate :" .. params.crateType, 10)
|
||||
end
|
||||
end
|
||||
|
||||
---@type LoadCargoParams
|
||||
local params = {
|
||||
self = self,
|
||||
unit = unit,
|
||||
crateType = crateType,
|
||||
groupID = groupID
|
||||
}
|
||||
|
||||
timer.scheduleFunction(LoadCrateTask, params, timer.getTime() + 15)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param unit Unit
|
||||
---@param crateType CrateType
|
||||
---@return boolean
|
||||
function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType)
|
||||
|
||||
local crateConfigA = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType)
|
||||
if crateConfigA == nil then
|
||||
trigger.action.outText("Invalid crate type: " .. crateType, 5)
|
||||
return false
|
||||
end
|
||||
|
||||
local currentWeight = 0
|
||||
for _, cargo in pairs(self._cargoInUnits) do
|
||||
if cargo[crateType] ~= nil then
|
||||
currentWeight = currentWeight + (cargo[crateType] * crateConfigA.weight)
|
||||
end
|
||||
end
|
||||
|
||||
local unitConfig = Spearhead.classes.stageClasses.helpers.supplies.MaxLoadConfig[unit:getTypeName()]
|
||||
if unitConfig == nil then
|
||||
trigger.action.outText("Your unit type is not configured for logistics: " .. crateType, 5)
|
||||
self._logger:error("Invalid unit type: " .. unit:getTypeName())
|
||||
return false
|
||||
end
|
||||
local maxWeight = unitConfig.maxInternalLoad
|
||||
|
||||
if currentWeight + crateConfigA.weight > maxWeight then
|
||||
trigger.action.outText("Failed to load crate due to it overloading your max weight of: " .. maxWeight .. "kg", 5)
|
||||
return false
|
||||
end
|
||||
|
||||
trigger.action.setUnitInternalCargo(unit:getName(), crateConfigA.weight)
|
||||
self:AddCargoToUnit(unit:getID(), crateType)
|
||||
|
||||
local group = unit:getGroup()
|
||||
if group == nil then return false end
|
||||
local groupID = group:getID()
|
||||
self._commandsHelper:updateCommandsForGroup(groupID)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
---Spawns a crate for sling loading
|
||||
---@param groupID number
|
||||
---@param crateType CrateType
|
||||
function SupplyUnitsTracker:UnitRequestCrateSpawn(groupID, crateType)
|
||||
|
||||
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
|
||||
if group == nil then
|
||||
|
||||
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType)
|
||||
if crateConfig == nil then
|
||||
self._logger:error("Invalid crate type: " .. crateType)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---@private
|
||||
---@param unit Unit
|
||||
---@return Vec3
|
||||
function SupplyUnitsTracker:GetCargoPlacePosition(unit)
|
||||
|
||||
local pos = unit:getPosition()
|
||||
local preferedPos = {
|
||||
x = pos.p.x - 10 * pos.x.x,
|
||||
y = pos.p.y - 10 * pos.x.y,
|
||||
z = pos.p.z - 10 * pos.x.z
|
||||
}
|
||||
|
||||
return preferedPos
|
||||
|
||||
|
||||
-- local volume = {
|
||||
-- id = world.VolumeType.SPHERE,
|
||||
-- params = {
|
||||
-- point = preferedPos,
|
||||
-- radius = 10
|
||||
-- }
|
||||
-- }
|
||||
|
||||
-- local occupiedPosX = {}
|
||||
-- local occupiedPosZ = {}
|
||||
|
||||
-- ---@param foundItem Object
|
||||
-- local found = function(foundItem, val)
|
||||
|
||||
-- local foundPos = foundItem:getPoint()
|
||||
|
||||
-- local z = math.floor(foundPos.z)
|
||||
-- for i = z - 3 , z + 3 do
|
||||
-- occupiedPosZ[i] = true
|
||||
-- end
|
||||
|
||||
-- local x = math.floor(foundPos.x)
|
||||
-- for i = x - 3 , x + 3 do
|
||||
-- occupiedPosX[i] = true
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- world.searchObjects(volume.id, volume.params, found)
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
if Spearhead == nil then Spearhead = {} end
|
||||
if Spearhead.classes == nil then Spearhead.classes = {} end
|
||||
if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end
|
||||
if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end
|
||||
Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker = SupplyUnitsTracker
|
||||
@@ -0,0 +1,229 @@
|
||||
|
||||
|
||||
---@class BuildableMission : Mission
|
||||
---@field private _requiredKilos number
|
||||
---@field private _droppedKilos number
|
||||
---@field private _crateType SupplyType
|
||||
---@field private _targetZoneName string
|
||||
---@field private _database Database
|
||||
---@field private _onCrateDroppedOfListeners Array<OnCrateDroppedListener>
|
||||
---@field private _markIDsPerGroup table<string, number>
|
||||
---@field private _supplyUnitsTracker SupplyUnitsTracker
|
||||
---@field private _noLandingZone SpearheadTriggerZone?
|
||||
---@field private _dropOffZone SpearheadTriggerZone?
|
||||
---@field private _noLandingZoneId number
|
||||
---@field private _dropOffZoneId number
|
||||
local BuildableMission = {}
|
||||
BuildableMission.__index = BuildableMission
|
||||
|
||||
---@class OnCrateDroppedListener
|
||||
---@field OnCrateDroppedOff fun(self:OnCrateDroppedListener, mission:BuildableMission, kilos:number)
|
||||
|
||||
---@param database Database
|
||||
---@param targetZoneName string
|
||||
---@param requiredKilos number
|
||||
---@param requiredCrateType SupplyType
|
||||
---@param noLandingZone SpearheadTriggerZone?
|
||||
---@param logger Logger
|
||||
function BuildableMission.new(database, logger, targetZoneName, noLandingZone, requiredKilos, requiredCrateType)
|
||||
|
||||
local Mission = Spearhead.classes.stageClasses.missions.baseMissions.Mission
|
||||
setmetatable(BuildableMission, Mission)
|
||||
|
||||
local self = setmetatable({}, { __index = BuildableMission })
|
||||
|
||||
self._targetZoneName = targetZoneName
|
||||
self._database = database
|
||||
self._requiredKilos = requiredKilos
|
||||
self._droppedKilos = 0
|
||||
|
||||
self._noLandingZone = noLandingZone
|
||||
|
||||
if noLandingZone then
|
||||
|
||||
local verts = noLandingZone.verts
|
||||
local enlarged = Spearhead.Util.enlargeConvexHull(verts, 300)
|
||||
|
||||
---@type SpearheadTriggerZone
|
||||
local dropOfZone = {
|
||||
name = targetZoneName .. "_dropZone",
|
||||
zone_type = "Polygon",
|
||||
radius = 0,
|
||||
verts = enlarged,
|
||||
location = noLandingZone.location,
|
||||
}
|
||||
|
||||
self._dropOffZone = dropOfZone
|
||||
end
|
||||
|
||||
self.code = tostring(database:GetNewMissionCode())
|
||||
self.name = "Resupply"
|
||||
|
||||
local type = "site"
|
||||
if requiredCrateType == "SAM_CRATE" then
|
||||
type = "SAM site"
|
||||
elseif requiredCrateType == "FARP_CRATE" then
|
||||
type = "FARP"
|
||||
end
|
||||
|
||||
self.zoneName = targetZoneName .. "_supply"
|
||||
self._logger = logger
|
||||
self._onCrateDroppedOfListeners = {}
|
||||
self._completeListeners = {}
|
||||
self._markIDsPerGroup = {}
|
||||
self._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logger.LogLevel)
|
||||
self._state = "NEW"
|
||||
|
||||
local spearheadZone = Spearhead.DcsUtil.getZoneByName(targetZoneName)
|
||||
if spearheadZone == nil then
|
||||
logger:error("Zone not found: " .. targetZoneName)
|
||||
return nil
|
||||
end
|
||||
|
||||
self.location = spearheadZone.location
|
||||
|
||||
self.missionType = "LOGISTICS"
|
||||
self.missionTypeDisplay = "LOGISTICS"
|
||||
|
||||
self.priority = "secondary"
|
||||
|
||||
self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(self._logger.LogLevel)
|
||||
|
||||
self._crateType = requiredCrateType
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
---@param listener OnCrateDroppedListener
|
||||
function BuildableMission:AddOnCrateDroppedOfListener(listener)
|
||||
table.insert(self._onCrateDroppedOfListeners, listener)
|
||||
end
|
||||
|
||||
function BuildableMission:ShowBriefing(groupID)
|
||||
|
||||
local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID)
|
||||
if group == nil then return end
|
||||
|
||||
local unitType = Spearhead.DcsUtil.getUnitTypeFromGroup(group)
|
||||
local coords = Spearhead.DcsUtil.convertVec2ToUnitUsableType(self.location, unitType)
|
||||
|
||||
local siteType = "FARP"
|
||||
if self._crateType == "SAM_CRATE" then
|
||||
siteType = "SAM site"
|
||||
end
|
||||
|
||||
local briefing = "Mission [" .. self.code .. "] " .. self.name ..
|
||||
"\n \n" ..
|
||||
"We've dispatched forward units to find a proper spot for a new " .. siteType .. "." ..
|
||||
"\nYou will need to drop off supplies so they can start building." ..
|
||||
"\nThe coords are: " .. coords ..
|
||||
"\n\n" ..
|
||||
"\nKilos still required: " .. self._requiredKilos - self._droppedKilos ..
|
||||
"\n\n" ..
|
||||
"NOTE: Do not land in the orange construction zone!"
|
||||
|
||||
trigger.action.outTextForGroup(groupID, briefing, 30)
|
||||
end
|
||||
|
||||
function BuildableMission:MarkMissionAreaToGroup(groupID)
|
||||
|
||||
local text = "[" .. self.code .. "] " .. self.name
|
||||
local location = { x= self.location.x, y=land.getHeight(self.location), z=self.location.y }
|
||||
local markID = Spearhead.DcsUtil.AddMarkToGroup(groupID, text, location)
|
||||
|
||||
self._markIDsPerGroup[groupID] = markID
|
||||
end
|
||||
|
||||
---@private
|
||||
---@param crate SupplyConfig
|
||||
function BuildableMission:NotifyCrateDroppedOf(crate)
|
||||
for _, listener in ipairs(self._onCrateDroppedOfListeners) do
|
||||
if listener.OnCrateDroppedOff then
|
||||
listener:OnCrateDroppedOff(self, crate.weight)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BuildableMission:SpawnActive()
|
||||
|
||||
self._logger:debug("Spawning buildable mission: " .. self.code)
|
||||
|
||||
if self._noLandingZone == nil then
|
||||
self._logger:error("No no landing zone found for mission: " .. self.code)
|
||||
return
|
||||
end
|
||||
|
||||
---@type DrawColor
|
||||
local lineColor = { r=230/255, g=93/255, b=49/255, a=1}
|
||||
---@type DrawColor
|
||||
local fillColor = { r=230/255, g=93/255, b=49/255, a=0.2}
|
||||
self._noLandingZoneId = Spearhead.DcsUtil.DrawZone(self._noLandingZone, lineColor, fillColor, 6)
|
||||
|
||||
if self._dropOffZone == nil then
|
||||
self._logger:error("No drop off zone found for mission: " .. self.code)
|
||||
return
|
||||
end
|
||||
|
||||
local lineColor2 = { r=0, g=0, b=1, a=1}
|
||||
local fillColor2 = { r=0, g=0, b=1, a=0}
|
||||
self._dropOffZoneId = Spearhead.DcsUtil.DrawZone(self._dropOffZone, lineColor2, fillColor2, 6)
|
||||
|
||||
---@param selfA BuildableMission
|
||||
---@param time number
|
||||
local checkForCrateTasks = function (selfA, time)
|
||||
selfA:CheckCratesInZone()
|
||||
return time + 10
|
||||
end
|
||||
|
||||
timer.scheduleFunction(checkForCrateTasks, self, timer.getTime() + 10)
|
||||
|
||||
self:SpawnForwardUnits()
|
||||
self._state = "ACTIVE"
|
||||
|
||||
self._missionCommandsHelper:AddMissionToCommands(self)
|
||||
|
||||
end
|
||||
|
||||
function BuildableMission:SpawnForwardUnits()
|
||||
|
||||
end
|
||||
|
||||
function BuildableMission:CheckCratesInZone()
|
||||
|
||||
---@type Array<Object>
|
||||
local foundCrates = {}
|
||||
|
||||
local crates = self._supplyUnitsTracker:GetCargoCratesDropped()
|
||||
for _, staticObject in pairs(crates) do
|
||||
if staticObject and staticObject:isExist() and Spearhead.Util.startswith(staticObject:getName(), self._crateType, true) then
|
||||
local pos = staticObject:getPoint()
|
||||
|
||||
if Spearhead.Util.is3dPointInZone(pos, self._dropOffZone) then
|
||||
table.insert(foundCrates, staticObject)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, foundCrate in pairs(foundCrates) do
|
||||
local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.fromObjectName(foundCrate:getName())
|
||||
if crateConfig then
|
||||
self._droppedKilos = self._droppedKilos + crateConfig.weight
|
||||
foundCrate:destroy()
|
||||
self:NotifyCrateDroppedOf(crateConfig)
|
||||
end
|
||||
end
|
||||
|
||||
if self._droppedKilos >= self._requiredKilos then
|
||||
Spearhead.DcsUtil.RemoveMark(self._noLandingZoneId)
|
||||
Spearhead.DcsUtil.RemoveMark(self._dropOffZoneId)
|
||||
self:NotifyMissionComplete()
|
||||
self._state = "COMPLETED"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if not Spearhead.classes then Spearhead.classes = {} end
|
||||
if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end
|
||||
if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end
|
||||
Spearhead.classes.stageClasses.missions.BuildableMission = BuildableMission
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
|
||||
|
||||
---@class Mission
|
||||
---@field name string
|
||||
---@field zoneName string
|
||||
@@ -120,6 +121,8 @@ function Mission:NotifyMissionComplete()
|
||||
|
||||
end
|
||||
|
||||
function Mission:MarkMissionAreaToGroup(groupId) end
|
||||
|
||||
---endregion
|
||||
|
||||
--region PROTECTED
|
||||
@@ -155,6 +158,7 @@ do --aliases
|
||||
--- | "DEAD"
|
||||
--- | "SAM"
|
||||
--- | "OCA"
|
||||
--- | "LOGISTICS"
|
||||
|
||||
--- @alias MissionState
|
||||
--- | "NEW"
|
||||
|
||||
@@ -23,6 +23,7 @@ assert(loadfile(classPath .. "stageClasses\\GlobalStageManager.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\missions\\baseMissions\\Mission.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\missions\\ZoneMission.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\missions\\RunwayStrikeMission.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\missions\\BuildableMission.lua"))()
|
||||
|
||||
|
||||
assert(loadfile(classPath .. "stageClasses\\Stages\\BaseStage\\Stage.lua"))()
|
||||
@@ -34,10 +35,13 @@ assert(loadfile(classPath .. "stageClasses\\Stages\\WaitingStage.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\Groups\\SpearheadGroup.lua"))()
|
||||
|
||||
assert(loadfile(classPath .. "stageClasses\\helpers\\MissionCommandsHelper.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\helpers\\SupplyConfig.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\helpers\\SupplyUnitsTracker.lua"))()
|
||||
|
||||
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\StageBase.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\BlueSam.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\FarpZone.lua"))()
|
||||
assert(loadfile(classPath .. "stageClasses\\SpecialZones\\SupplyHub.lua"))()
|
||||
|
||||
assert(loadfile(classPath .. "capClasses\\CapGroup.lua"))()
|
||||
assert(loadfile(classPath .. "capClasses\\GlobalCapManager.lua"))()
|
||||
|
||||
Reference in New Issue
Block a user