diff --git a/Moose/Moose.lua b/Loaders/Moose_Load_Dynamic.lua similarity index 59% rename from Moose/Moose.lua rename to Loaders/Moose_Load_Dynamic.lua index edacba13a..e12d51570 100644 --- a/Moose/Moose.lua +++ b/Loaders/Moose_Load_Dynamic.lua @@ -1,42 +1,43 @@ local base = _G - env.info("Loading MOOSE " .. base.timer.getAbsTime() ) -function script_path() - local str = debug.getinfo(2, "S").source - return str:match("(.*/)"):sub(1,-2) -end - - Include = {} -Include.MissionPath = script_path() .. "Mission\\" -Include.ProgramPath = "Scripts\\Moose\\Moose\\" - -env.info( "Include.MissionPath = " .. Include.MissionPath) -env.info( "Include.ProgramPath = " .. Include.ProgramPath) -Include.Files = {} +Include.Path = function() + local str = debug.getinfo(2, "S").source + return str:match("(.*/)"):sub(1,-2):gsub("\\","/") +end Include.File = function( IncludeFile ) if not Include.Files[ IncludeFile ] then Include.Files[IncludeFile] = IncludeFile - local f = base.loadfile( Include.ProgramPath .. IncludeFile .. ".lua" ) + env.info( "Include:" .. IncludeFile .. " from " .. Include.ProgramPath ) + local f = assert( base.loadfile( Include.ProgramPath .. IncludeFile .. ".lua" ) ) if f == nil then - local f = base.loadfile( Include.MissionPath .. IncludeFile .. ".lua" ) + env.info( "Include:" .. IncludeFile .. " from " .. Include.MissionPath ) + local f = assert( base.loadfile( Include.MissionPath .. IncludeFile .. ".lua" ) ) if f == nil then error ("Could not load MOOSE file " .. IncludeFile .. ".lua" ) else - env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.ProgramPath ) + env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.MissionPath ) return f() end else - env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.MissionPath ) + env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.ProgramPath ) return f() end end end +Include.ProgramPath = "Scripts/Moose/Moose/" +Include.MissionPath = Include.Path() + +env.info( "Include.ProgramPath = " .. Include.ProgramPath) +env.info( "Include.MissionPath = " .. Include.MissionPath) + +Include.Files = {} + Include.File( "Database" ) env.info("Loaded MOOSE Include Engine") \ No newline at end of file diff --git a/Loaders/Moose_Load_Embedded.lua b/Loaders/Moose_Load_Embedded.lua new file mode 100644 index 000000000..4314a929d --- /dev/null +++ b/Loaders/Moose_Load_Embedded.lua @@ -0,0 +1,23 @@ + +local base = _G +env.info("Loading MOOSE " .. base.timer.getAbsTime() ) + +Include = {} + +Include.Path = function() + local str = debug.getinfo(2, "S").source + return str:match("(.*/)"):sub(1,-2):gsub("\\","/") +end + +Include.File = function( IncludeFile ) +end + +Include.ProgramPath = "Scripts/Moose/Moose/" +Include.MissionPath = Include.Path() + +env.info( "Include.ProgramPath = " .. Include.ProgramPath) +env.info( "Include.MissionPath = " .. Include.MissionPath) + +Include.Files = {} + +env.info("Loaded MOOSE Include Engine") \ No newline at end of file diff --git a/Loaders/Moose_Test.lua b/Loaders/Moose_Test.lua new file mode 100644 index 000000000..fc6979ea9 --- /dev/null +++ b/Loaders/Moose_Test.lua @@ -0,0 +1,159 @@ + +local base = _G + +local MOOSE_Version = "0.1.1.1" + +env.info("Loading MOOSE " .. base.timer.getAbsTime() ) + +function script_path() + local str = debug.getinfo(2, "S").source + return str:match("(.*/)"):sub(1,-2):gsub("\\","/") +end + + +Include = {} + +Include.ProgramPath = "Scripts/Moose/Moose/" +Include.MissionPath = script_path() + +env.info( "Include.ProgramPath = " .. Include.ProgramPath) +env.info( "Include.MissionPath = " .. Include.MissionPath) +Include.Files = {} + +Include.FileIn = function(fileName, table) +-- env.info( fileName ) + local chunk, errMsg = base.loadfile(fileName) + if chunk ~= nil then + env.info( "chunk assigned " ) + env.info( Include.oneLineSerialize( chunk ) ) + base.setfenv(chunk, table) + chunk() + if table.MOOSE_Version then + env.info( table.MOOSE_Version ) + end + return chunk + else + return nil, errMsg + end +end + +Include.MisFiles = {} + +Include.FileName = function( num ) + local hexstr = '0123456789ABCDEF' + local s = '' + while num > 0 do + local mod = math.fmod(num, 16) + s = string.sub(hexstr, mod+1, mod+1) .. s + num = math.floor(num / 16) + end + if s == '' then s = '0' end +-- env.info( string.format( "~mis" .. "%8s", "00000000" .. s ) ) + return string.format( "~mis" .. "%s", string.sub( "00000000" .. s, -8 ) ) +end + +Include.ScanFiles = function() + + local i = 0 + while i <= 32767 do + local FileName = Include.FileName( i ) + local FileChunk = {} + local FileChunk = Include.FileIn( Include.MissionPath .. FileName, FileChunk ) + if FileChunk then + end + i = i + 1 + end +end + + +Include.File = function( IncludeFile ) + if not Include.Files[ IncludeFile ] then + Include.Files[IncludeFile] = IncludeFile + env.info( "Include:" .. IncludeFile .. " from " .. Include.ProgramPath ) + local f = base.loadfile( Include.ProgramPath .. IncludeFile .. ".lua" ) + if f == nil then + env.info( "Include:" .. IncludeFile .. " from " .. Include.MissionPath ) + local f = base.loadfile( Include.MissionPath .. IncludeFile .. ".lua" ) + if f == nil then + error ("Could not load MOOSE file " .. IncludeFile .. ".lua" ) + else + env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.MissionPath ) + return f() + end + else + env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.ProgramPath ) + return f() + end + end +end + +--porting in Slmod's "safestring" basic serialize +Include.basicSerialize = function(s) + if s == nil then + return "\"\"" + else + if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then + return tostring(s) + elseif type(s) == 'string' then + s = string.format('%q', s) + return s + end + end +end + +-- porting in Slmod's serialize_slmod2 +Include.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function + if type(tbl) == 'table' then --function only works for tables! + + local tbl_str = {} + + tbl_str[#tbl_str + 1] = '{' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = ']=' + else --must be a string + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = Include.basicSerialize(ind) + tbl_str[#tbl_str + 1] = ']=' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ',' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = Include.basicSerialize(val) + tbl_str[#tbl_str + 1] = ',' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil,' + elseif type(val) == 'table' then + if ind == "__index" then + tbl_str[#tbl_str + 1] = "__index" + tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + else + tbl_str[#tbl_str + 1] = Include.oneLineSerialize(val) + tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + end + elseif type(val) == 'function' then + tbl_str[#tbl_str + 1] = "function " .. tostring(ind) + tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + else + env.info('unable to serialize value type ' .. Include.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) + env.info( debug.traceback() ) + end + + end + tbl_str[#tbl_str + 1] = '}' + return table.concat(tbl_str) + else + return tostring(tbl) + end +end + +Include.ScanFiles( ) + +Include.File( "Database" ) + +env.info("Loaded MOOSE Include Engine") \ No newline at end of file diff --git a/Moose/Base.lua b/Moose/Base.lua index 97fca2da8..dfc1d888a 100644 --- a/Moose/Base.lua +++ b/Moose/Base.lua @@ -4,12 +4,22 @@ Include.File( "Routines" ) +_TraceOn = true +_TraceClass = { + DATABASE = true, + --SEAD = true, + --DESTROYBASETASK = true, + --MOVEMENT = true, + --SPAWN = true, + --GROUP = true, + --UNIT = true, + } + BASE = { ClassName = "BASE", ClassID = 0, Events = {} - } --- The base constructor. This is the top top class of all classed defined within the MOOSE. @@ -53,7 +63,7 @@ function BASE:Inherit( Child, Parent ) Child.__index = Child end --Child.ClassName = Child.ClassName .. '.' .. Child.ClassID - trace.i( Child.ClassName, 'Inherited from ' .. Parent.ClassName ) + self:T( 'Inherited from ' .. Parent.ClassName ) return Child end @@ -104,6 +114,7 @@ trace.f( self.ClassName ) return self end + BaseEventCodes = { "S_EVENT_SHOT", "S_EVENT_HIT", @@ -129,7 +140,46 @@ BaseEventCodes = { "S_EVENT_SHOOTING_START", "S_EVENT_SHOOTING_END", "S_EVENT_MAX", - } +} + +--onEvent( {[1]="S_EVENT_BIRTH",[2]={["subPlace"]=5,["time"]=0,["initiator"]={["id_"]=16884480,},["place"]={["id_"]=5000040,},["id"]=15,["IniUnitName"]="US F-15C@RAMP-Air Support Mountains#001-01",},} +-- Event = { +-- id = enum world.event, +-- time = Time, +-- initiator = Unit, +-- target = Unit, +-- place = Unit, +-- subPlace = enum world.BirthPlace, +-- weapon = Weapon +-- } + + +function BASE:CreateEventBirth( EventTime, Initiator, IniUnitName, place, subplace ) +trace.f( self.ClassName, { EventTime, Initiator, IniUnitName, place, subplace } ) + + local Event = { + id = world.event.S_EVENT_BIRTH, + time = EventTime, + initiator = Initiator, + IniUnitName = IniUnitName, + place = place, + subplace = subplace + } + + world.onEvent( Event ) +end + +function BASE:CreateEventCrash( EventTime, Initiator ) +trace.f( self.ClassName, { EventTime, Initiator } ) + + local Event = { + id = world.event.S_EVENT_CRASH, + time = EventTime, + initiator = Initiator, + } + + world.onEvent( Event ) +end function BASE:onEvent(event) --trace.f(self.ClassName, event ) @@ -159,3 +209,22 @@ function BASE:onEvent(event) end +-- Trace section + + +function BASE:T( Arguments ) + + if _TraceOn and _TraceClass[self.ClassName] then + + local DebugInfo = debug.getinfo( 2, "nl" ) + + local Function = "function" + if DebugInfo.name then + Function = DebugInfo.name + end + + local Line = DebugInfo.currentline + + env.info( string.format( "%6d/%1s:%20s%05d.%s\(%s\)" , Line, "T", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) + end +end diff --git a/Moose/Cargo.lua b/Moose/Cargo.lua index 866c06a5b..3ff9617a8 100644 --- a/Moose/Cargo.lua +++ b/Moose/Cargo.lua @@ -1,33 +1,992 @@ ---- CARGO +--- CARGO Classes -- @classmod CARGO ---@todo need to define CARGO Class that is used within a mission... ---- Structures --- @section Structures +Include.File( "Routines" ) +Include.File( "Base" ) +Include.File( "Message" ) ---[[-- - Internal Table to understand the form of the CARGO. - @table CARGO_TRANSPORT ---]] -CARGO_TRANSPORT = { UNIT = 1, SLING = 2, STATIC = 3, INVISIBLE = 4 } +--- Clients are those Groups defined within the Mission Editor that have the skillset defined as "Client" or "Player". +-- These clients are defined within the Mission Orchestration Framework (MOF) ---[[-- - CARGO_TYPE Defines the different types of transports, which has an impact on the menu commands shown in F10. - @table CARGO_TYPE - @field TROOPS - @field GOODS - @field VEHICLES - @field INFANTRY - @field ENGINEERS - @field PACKAGE - @field CARGO ---]] -CARGO_TYPE = { - TROOPS = { ID = 1, TEXT = "Troops", TRANSPORT = CARGO_TRANSPORT.UNIT }, - GOODS = { ID = 2, TEXT = "Goods", TRANSPORT = CARGO_TRANSPORT.STATIC }, - VEHICLES = { ID = 3, TEXT = "Vehicles", TRANSPORT = CARGO_TRANSPORT.VEHICLES }, - INFANTRY = { ID = 4, TEXT = "Infantry", TRANSPORT = CARGO_TRANSPORT.UNIT }, - ENGINEERS = { ID = 5, TEXT = "Engineers", TRANSPORT = CARGO_TRANSPORT.UNIT }, - PACKAGE = { ID = 5, TEXT = "Package", TRANSPORT = CARGO_TRANSPORT.INVISIBLE }, - CARGO = { ID = 5, TEXT = "Cargo", TRANSPORT = CARGO_TRANSPORT.STATIC }, +CARGOS = {} + + +CARGO_ZONE = { + ClassName="CARGO_ZONE", + CargoZoneName = '', + CargoHostUnitName = '', + SIGNAL = { + TYPE = { + SMOKE = { ID = 1, TEXT = "smoke" }, + FLARE = { ID = 2, TEXT = "flare" } + }, + COLOR = { + GREEN = { ID = 1, TRIGGERCOLOR = trigger.smokeColor.Green, TEXT = "A green" }, + RED = { ID = 2, TRIGGERCOLOR = trigger.smokeColor.Red, TEXT = "A red" }, + WHITE = { ID = 3, TRIGGERCOLOR = trigger.smokeColor.White, TEXT = "A white" }, + ORANGE = { ID = 4, TRIGGERCOLOR = trigger.smokeColor.Orange, TEXT = "An orange" }, + BLUE = { ID = 5, TRIGGERCOLOR = trigger.smokeColor.Blue, TEXT = "A blue" }, + YELLOW = { ID = 6, TRIGGERCOLOR = trigger.flareColor.Yellow, TEXT = "A yellow" } + } + } } + +function CARGO_ZONE:New( CargoZoneName, CargoHostName ) local self = BASE:Inherit( self, BASE:New() ) +self:T( { CargoZoneName, CargoHostName } ) + + self.CargoZoneName = CargoZoneName + self.CargoZone = trigger.misc.getZone( CargoZoneName ) + + + if CargoHostName then + self.CargoHostName = CargoHostName + self.CargoHostSpawn = SPAWN:New( CargoHostName ) + end + + self:T( self.CargoZone ) + + return self +end + +function CARGO_ZONE:Spawn() +self:T( CargoHostSpawn ) + + if self.CargoHostSpawn then + local CargoHostGroup = Group.getByName( self.CargoHostSpawn:SpawnGroupName() ) + if CargoHostGroup then + if not CargoHostGroup:isExist() then + self.CargoHostSpawn:ReSpawn() + end + else + self.CargoHostSpawn:ReSpawn() + end + end + + return self +end + +function CARGO_ZONE:GetHostUnit() + + if self.CargoHostName then + + -- A Host has been given, signal the host + local CargoHostGroup = Group.getByName( self.CargoHostSpawn:SpawnGroupName() ) + local CargoHostUnit + if CargoHostGroup == nil then + CargoHostUnit = StaticObject.getByName( self.CargoHostName ) + else + CargoHostUnit = CargoHostGroup:getUnits()[1] + end + + return CargoHostUnit + end + + return nil +end + +function CARGO_ZONE:ReportCargosToClient( Client, CargoType ) +self:T() + + local SignalUnit = self:GetHostUnit() + + if SignalUnit then + + local SignalUnitTypeName = SignalUnit:getTypeName() + + local HostMessage = "" + + local IsCargo = false + for CargoID, Cargo in pairs( CARGOS ) do + if Cargo.CargoType == Task.CargoType then + if Cargo:IsStatusNone() then + HostMessage = HostMessage .. " - " .. Cargo.CargoName .. " - " .. Cargo.CargoType .. " (" .. Cargo.Weight .. "kg)" .. "\n" + IsCargo = true + end + end + end + + if not IsCargo then + HostMessage = "No Cargo Available." + end + + Client:Message( HostMessage, 20, Mission.Name .. "/StageHosts." .. SignalUnitTypeName, SignalUnitTypeName .. ": Reporting Cargo", 10 ) + end +end + +function CARGO_ZONE:Signal() +self:T() + + local Signalled = false + + if self.SignalType then + + if self.CargoHostName then + + -- A Host has been given, signal the host + + local SignalUnit = self:GetHostUnit() + + if SignalUnit then + + self:T( 'Signalling Unit' ) + local SignalVehiclePos = SignalUnit:getPosition().p + SignalVehiclePos.y = SignalVehiclePos.y + 2 + + if self.SignalType.ID == CARGO_ZONE.SIGNAL.TYPE.SMOKE.ID then + + trigger.action.smoke( SignalVehiclePos, self.SignalColor.TRIGGERCOLOR ) + Signalled = true + + elseif self.SignalType.ID == CARGO_ZONE.SIGNAL.TYPE.FLARE.ID then + + trigger.action.signalFlare( SignalVehiclePos, self.SignalColor.TRIGGERCOLOR , 0 ) + Signalled = false + + end + end + + else + + local CurrentPosition = { x = self.CargoZone.point.x, y = self.CargoZone.point.z } + self.CargoZone.point.y = land.getHeight( CurrentPosition ) + 2 + + if self.SignalType.ID == CARGO_ZONE.SIGNAL.TYPE.SMOKE.ID then + + trigger.action.smoke( self.CargoZone.point, self.SignalColor.TRIGGERCOLOR ) + Signalled = true + + elseif self.SignalType.ID == CARGO_ZONE.SIGNAL.TYPE.FLARE.ID then + trigger.action.signalFlare( self.CargoZone.point, self.SignalColor.TRIGGERCOLOR, 0 ) + Signalled = false + + end + end + end + + return Signalled + +end + +function CARGO_ZONE:WhiteSmoke() +self:T() + + self.SignalType = CARGO_ZONE.SIGNAL.TYPE.SMOKE + self.SignalColor = CARGO_ZONE.SIGNAL.COLOR.WHITE + + return self +end + +function CARGO_ZONE:BlueSmoke() +self:T() + + self.SignalType = CARGO_ZONE.SIGNAL.TYPE.SMOKE + self.SignalColor = CARGO_ZONE.SIGNAL.COLOR.BLUE + + return self +end + +function CARGO_ZONE:RedSmoke() +self:T() + + self.SignalType = CARGO_ZONE.SIGNAL.TYPE.SMOKE + self.SignalColor = CARGO_ZONE.SIGNAL.COLOR.RED + + return self +end + +function CARGO_ZONE:OrangeSmoke() +self:T() + + self.SignalType = CARGO_ZONE.SIGNAL.TYPE.SMOKE + self.SignalColor = CARGO_ZONE.SIGNAL.COLOR.ORANGE + + return self +end + +function CARGO_ZONE:GreenSmoke() +self:T() + + self.SignalType = CARGO_ZONE.SIGNAL.TYPE.SMOKE + self.SignalColor = CARGO_ZONE.SIGNAL.COLOR.GREEN + + return self +end + + +function CARGO_ZONE:WhiteFlare() +self:T() + + self.SignalType = CARGO_ZONE.SIGNAL.TYPE.FLARE + self.SignalColor = CARGO_ZONE.SIGNAL.COLOR.WHITE + + return self +end + +function CARGO_ZONE:RedFlare() +self:T() + + self.SignalType = CARGO_ZONE.SIGNAL.TYPE.FLARE + self.SignalColor = CARGO_ZONE.SIGNAL.COLOR.RED + + return self +end + +function CARGO_ZONE:GreenFlare() +self:T() + + self.SignalType = CARGO_ZONE.SIGNAL.TYPE.FLARE + self.SignalColor = CARGO_ZONE.SIGNAL.COLOR.GREEN + + return self +end + +function CARGO_ZONE:YellowFlare() +self:T() + + self.SignalType = CARGO_ZONE.SIGNAL.TYPE.FLARE + self.SignalColor = CARGO_ZONE.SIGNAL.COLOR.YELLOW + + return self +end + + +function CARGO_ZONE:GetCargoHostUnit() +self:T() + + local CargoHostUnit = Group.getByName( self.CargoHostSpawn:SpawnGroupName() ):getUnit(1) + if CargoHostUnit and CargoHostUnit:isExist() then + return CargoHostUnit + end + + return nil +end + +function CARGO_ZONE:GetCargoZoneName() +self:T() + + return self.CargoZoneName +end + +CARGO = { + ClassName = "CARGO", + STATUS = { + NONE = 0, + LOADED = 1, + UNLOADED = 2, + LOADING = 3 + }, + CargoClient = nil +} + +--- Add Cargo to the mission... Cargo functionality needs to be reworked a bit, so this is still under construction. I need to make a CARGO Class... +function CARGO:New( CargoType, CargoName, CargoWeight ) local self = BASE:Inherit( self, BASE:New() ) +self:T( { CargoType, CargoName, CargoWeight } ) + + + self.CargoType = CargoType + self.CargoName = CargoName + self.CargoWeight = CargoWeight + + self:StatusNone() + + return self +end + +function CARGO:Spawn() +self:T() + + return self + +end + +function CARGO:IsNear( Client, LandingZone ) +self:T() + + local Near = true + + return Near + +end + + +function CARGO:IsLoadingToClient() +self:T() + + if self:IsStatusLoading() then + return self.CargoClient + end + + return nil + +end + + +function CARGO:IsLoadedInClient() +self:T() + + if self:IsStatusLoaded() then + return self.CargoClient + end + + return nil + +end + + +function CARGO:UnLoad( Client, TargetZoneName ) +self:T() + + self:StatusUnLoaded() + + return self +end + +function CARGO:OnBoard( Client, LandingZone ) +self:T() + + local Valid = true + + self.CargoClient = Client + local ClientUnit = Client:GetClientGroupUnit() + + return Valid +end + +function CARGO:OnBoarded( Client, LandingZone ) +self:T() + + local OnBoarded = true + + return OnBoarded +end + +function CARGO:Load( Client ) +self:T() + + self:StatusLoaded( Client ) + + return self +end + +function CARGO:IsLandingRequired() +self:T() + return true +end + +function CARGO:IsSlingLoad() +self:T() + return false +end + + +function CARGO:StatusNone() +self:T() + + self.CargoClient = nil + self.CargoStatus = CARGO.STATUS.NONE + + return self +end + +function CARGO:StatusLoading( Client ) +self:T() + + self.CargoClient = Client + self.CargoStatus = CARGO.STATUS.LOADING + self:T( "Cargo " .. self.CargoName .. " loading to Client: " .. CargoClient:GetClientGroupName() ) + + return self +end + +function CARGO:StatusLoaded( Client ) +self:T() + + self.CargoClient = Client + self.CargoStatus = CARGO.STATUS.LOADED + self:T( "Cargo " .. self.CargoName .. " loaded in Client: " .. self.CargoClient:GetClientGroupName() ) + + return self +end + +function CARGO:StatusUnLoaded() +self:T() + + self.CargoClient = nil + self.CargoStatus = CARGO.STATUS.UNLOADED + + return self +end + + +function CARGO:IsStatusNone() +self:T() + + return self.CargoStatus == CARGO.STATUS.NONE +end + +function CARGO:IsStatusLoading() +self:T() + + return self.CargoStatus == CARGO.STATUS.LOADING +end + +function CARGO:IsStatusLoaded() +self:T() + + return self.CargoStatus == CARGO.STATUS.LOADED +end + +function CARGO:IsStatusUnLoaded() +self:T() + + return self.CargoStatus == CARGO.STATUS.UNLOADED +end + + +CARGO_GROUP = { + ClassName = "CARGO_GROUP" +} + + +function CARGO_GROUP:New( CargoType, CargoName, CargoWeight, CargoGroupTemplate, CargoZone ) local self = BASE:Inherit( self, CARGO:New( CargoType, CargoName, CargoWeight ) ) +self:T( { CargoType, CargoName, CargoWeight, CargoGroupTemplate, CargoZone } ) + + self.CargoSpawn = SPAWN:New( CargoGroupTemplate ) + self.CargoZone = CargoZone + + CARGOS[self.CargoName] = self + + return self + +end + +function CARGO_GROUP:Spawn() +self:T() + + local SpawnCargo = true + + if self:IsStatusNone() then + local CargoGroup = Group.getByName( self.CargoName ) + if CargoGroup and CargoGroup:isExist() then + SpawnCargo = false + end + + elseif self:IsStatusLoading() then + + local Client = self:IsLoadingToClient() + if Client and Client:ClientGroup() then + SpawnCargo = false + else + local CargoGroup = Group.getByName( self.CargoName ) + if CargoGroup and CargoGroup:isExist() then + SpawnCargo = false + end + end + + elseif self:IsStatusLoaded() then + + local Client = self:IsLoadedInClient() + if Client and Client:ClientGroup() then + SpawnCargo = false + end + + elseif self:IsStatusUnLoaded() then + + SpawnCargo = false + + end + + if SpawnCargo then + if self.CargoZone:GetCargoHostUnit() then + --- ReSpawn the Cargo from the CargoHost + self.CargoGroupName = self.CargoSpawn:FromHost( self.CargoZone:GetCargoHostUnit(), 60, 30, self.CargoName, false ).name + else + --- ReSpawn the Cargo in the CargoZone without a host ... + self.CargoGroupName = self.CargoSpawn:InZone( self.CargoZone:GetCargoZoneName(), self.CargoName ).name + end + self:StatusNone() + end + + self:T( { self.CargoGroupName, CARGOS[self.CargoName].CargoGroupName } ) + + return self +end + +function CARGO_GROUP:IsNear( Client, LandingZone ) +self:T() + + local Near = false + + if self.CargoGroupName then + local CargoGroup = Group.getByName( self.CargoGroupName ) + if routines.IsPartOfGroupInRadius( CargoGroup, Client:ClientPosition(), 250 ) then + Near = true + end + end + + return Near + +end + + +function CARGO_GROUP:OnBoard( Client, LandingZone, OnBoardSide ) +self:T() + + local Valid = true + + local ClientUnit = Client:GetClientGroupUnit() + + local CarrierPos = ClientUnit:getPoint() + local CarrierPosMove = ClientUnit:getPoint() + local CarrierPosOnBoard = ClientUnit:getPoint() + + local CargoGroup = Group.getByName( self.CargoGroupName ) + + local CargoUnits = CargoGroup:getUnits() + local CargoPos = CargoUnits[1]:getPoint() + + + local Points = {} + + self:T( 'CargoPos x = ' .. CargoPos.x .. " z = " .. CargoPos.z ) + self:T( 'CarrierPosMove x = ' .. CarrierPosMove.x .. " z = " .. CarrierPosMove.z ) + + Points[#Points+1] = routines.ground.buildWP( CargoPos, "Cone", 10 ) + + self:T( 'Points[1] x = ' .. Points[1].x .. " y = " .. Points[1].y ) + + if OnBoardSide == nil then + OnBoardSide = CLIENT.ONBOARDSIDE.NONE + end + + if OnBoardSide == CLIENT.ONBOARDSIDE.LEFT then + + self:T( "TransportCargoOnBoard: Onboarding LEFT" ) + CarrierPosMove.z = CarrierPosMove.z - 25 + CarrierPosOnBoard.z = CarrierPosOnBoard.z - 5 + Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "Cone", 10 ) + + elseif OnBoardSide == CLIENT.ONBOARDSIDE.RIGHT then + + self:T( "TransportCargoOnBoard: Onboarding RIGHT" ) + CarrierPosMove.z = CarrierPosMove.z + 25 + CarrierPosOnBoard.z = CarrierPosOnBoard.z + 5 + Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "Cone", 10 ) + + elseif OnBoardSide == CLIENT.ONBOARDSIDE.BACK then + + self:T( "TransportCargoOnBoard: Onboarding BACK" ) + CarrierPosMove.x = CarrierPosMove.x - 25 + CarrierPosOnBoard.x = CarrierPosOnBoard.x - 5 + Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "Cone", 10 ) + + elseif OnBoardSide == CLIENT.ONBOARDSIDE.FRONT then + + self:T( "TransportCargoOnBoard: Onboarding FRONT" ) + CarrierPosMove.x = CarrierPosMove.x + 25 + CarrierPosOnBoard.x = CarrierPosOnBoard.x + 5 + Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "Cone", 10 ) + + elseif OnBoardSide == CLIENT.ONBOARDSIDE.NONE then + + self:T( "TransportCargoOnBoard: Onboarding CENTRAL" ) + Points[#Points+1] = routines.ground.buildWP( CarrierPos, "Cone", 10 ) + + end + self:T( "TransportCargoOnBoard: Routing " .. self.CargoGroupName ) + + routines.scheduleFunction( routines.goRoute, { self.CargoGroupName, Points}, timer.getTime() + 4 ) + + self:StatusLoading( Client ) + + return Valid + +end + + +function CARGO_GROUP:OnBoarded( Client, LandingZone ) +self:T() + + local OnBoarded = false + + local CargoGroup = Group.getByName( self.CargoGroupName ) + if routines.IsPartOfGroupInRadius( CargoGroup, Client:ClientPosition(), 25 ) then + CargoGroup:destroy() + self:StatusLoaded( Client ) + OnBoarded = true + end + + return OnBoarded +end + + +function CARGO_GROUP:UnLoad( Client, TargetZoneName ) +self:T() + + self:T( 'self.CargoName = ' .. self.CargoName ) + self:T( 'self.CargoGroupName = ' .. self.CargoGroupName ) + + self.CargoSpawn:FromCarrier( Client:GetClientGroupUnit(), TargetZoneName, self.CargoGroupName ) + + self:StatusUnLoaded() + + return self +end + + +CARGO_PACKAGE = { + ClassName = "CARGO_PACKAGE" +} + + +function CARGO_PACKAGE:New( CargoType, CargoName, CargoWeight, CargoClient ) local self = BASE:Inherit( self, CARGO:New( CargoType, CargoName, CargoWeight ) ) + + self:T( { CargoType, CargoName, CargoWeight, CargoClient.ClientName } ) + + self.CargoClient = CargoClient + + CARGOS[self.CargoName] = self + + return self + +end + + +function CARGO_PACKAGE:Spawn() +self:T() + + -- this needs to be checked thoroughly + + local CargoClientInitGroup = self.CargoClient:ClientGroup() + if not CargoClientInitGroup then + if not self.CargoClientInitGroupSpawn then + self.CargoClientInitGroupSpawn = SPAWN:New( self.CargoClient:GetClientGroupName() ) + end + self.CargoClientInitGroupSpawn:Spawn( self.CargoClient:GetClientGroupName() ) + end + + local SpawnCargo = true + + if self:IsStatusNone() then + + elseif self:IsStatusLoading() or self:IsStatusLoaded() then + + local CargoClientLoaded = self:IsLoadedInClient() + if CargoClientLoaded and CargoClientLoaded:ClientGroup() then + SpawnCargo = false + end + + elseif self:IsStatusUnLoaded() then + + SpawnCargo = false + + else + + end + + if SpawnCargo then + self:StatusLoaded( self.CargoClient ) + end + + return self +end + + +function CARGO_PACKAGE:IsNear( Client, LandingZone ) +self:T() + + local Near = false + + if self.CargoClient and self.CargoClient:ClientGroup() then + self:T( self.CargoClient.ClientName ) + self:T( 'Client Exists.' ) + + if routines.IsUnitInRadius( self.CargoClient:GetClientGroupUnit(), Client:ClientPosition(), 150 ) then + Near = true + end + end + + return Near + +end + + +function CARGO_PACKAGE:OnBoard( Client, LandingZone, OnBoardSide ) +self:T() + + local Valid = true + + local ClientUnit = Client:GetClientGroupUnit() + + local CarrierPos = ClientUnit:getPoint() + local CarrierPosMove = ClientUnit:getPoint() + local CarrierPosOnBoard = ClientUnit:getPoint() + local CarrierPosMoveAway = ClientUnit:getPoint() + + local CargoHostGroup = self.CargoClient:ClientGroup() + local CargoHostName = self.CargoClient:ClientGroup():getName() + + local CargoHostUnits = CargoHostGroup:getUnits() + local CargoPos = CargoHostUnits[1]:getPoint() + + local Points = {} + + self:T( 'CargoPos x = ' .. CargoPos.x .. " z = " .. CargoPos.z ) + self:T( 'CarrierPosMove x = ' .. CarrierPosMove.x .. " z = " .. CarrierPosMove.z ) + + Points[#Points+1] = routines.ground.buildWP( CargoPos, "Cone", 10 ) + + self:T( 'Points[1] x = ' .. Points[1].x .. " y = " .. Points[1].y ) + + if OnBoardSide == nil then + OnBoardSide = CLIENT.ONBOARDSIDE.NONE + end + + if OnBoardSide == CLIENT.ONBOARDSIDE.LEFT then + + self:T( "TransportCargoOnBoard: Onboarding LEFT" ) + CarrierPosMove.z = CarrierPosMove.z - 25 + CarrierPosOnBoard.z = CarrierPosOnBoard.z - 5 + CarrierPosMoveAway.z = CarrierPosMoveAway.z - 20 + Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosMoveAway, "Cone", 10 ) + + elseif OnBoardSide == CLIENT.ONBOARDSIDE.RIGHT then + + self:T( "TransportCargoOnBoard: Onboarding RIGHT" ) + CarrierPosMove.z = CarrierPosMove.z + 25 + CarrierPosOnBoard.z = CarrierPosOnBoard.z + 5 + CarrierPosMoveAway.z = CarrierPosMoveAway.z + 20 + Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosMoveAway, "Cone", 10 ) + + elseif OnBoardSide == CLIENT.ONBOARDSIDE.BACK then + + self:T( "TransportCargoOnBoard: Onboarding BACK" ) + CarrierPosMove.x = CarrierPosMove.x - 25 + CarrierPosOnBoard.x = CarrierPosOnBoard.x - 5 + CarrierPosMoveAway.x = CarrierPosMoveAway.x - 20 + Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosMoveAway, "Cone", 10 ) + + elseif OnBoardSide == CLIENT.ONBOARDSIDE.FRONT then + + self:T( "TransportCargoOnBoard: Onboarding FRONT" ) + CarrierPosMove.x = CarrierPosMove.x + 25 + CarrierPosOnBoard.x = CarrierPosOnBoard.x + 5 + CarrierPosMoveAway.x = CarrierPosMoveAway.x + 20 + Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosMoveAway, "Cone", 10 ) + + elseif OnBoardSide == CLIENT.ONBOARDSIDE.NONE then + + self:T( "TransportCargoOnBoard: Onboarding FRONT" ) + CarrierPosMove.x = CarrierPosMove.x + 25 + CarrierPosOnBoard.x = CarrierPosOnBoard.x + 5 + CarrierPosMoveAway.x = CarrierPosMoveAway.x + 20 + Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "Cone", 10 ) + Points[#Points+1] = routines.ground.buildWP( CarrierPosMoveAway, "Cone", 10 ) + + end + self:T( "Routing " .. CargoHostName ) + + routines.scheduleFunction( routines.goRoute, { CargoHostName, Points}, timer.getTime() + 4 ) + + return Valid + +end + + +function CARGO_PACKAGE:OnBoarded( Client, LandingZone ) +self:T() + + local OnBoarded = false + + if self.CargoClient and self.CargoClient:ClientGroup() then + if routines.IsUnitInRadius( self.CargoClient:GetClientGroupUnit(), self.CargoClient:ClientPosition(), 10 ) then + + -- Switch Cargo from self.CargoClient to Client ... Each cargo can have only one client. So assigning the new client for the cargo is enough. + self:StatusLoaded( Client ) + + -- All done, onboarded the Cargo to the new Client. + OnBoarded = true + end + end + + return OnBoarded +end + + +function CARGO_PACKAGE:UnLoad( Client, TargetZoneName ) +self:T() + + self:T( 'self.CargoName = ' .. self.CargoName ) + --self:T( 'self.CargoHostName = ' .. self.CargoHostName ) + + --self.CargoSpawn:FromCarrier( Client:ClientGroup(), TargetZoneName, self.CargoHostName ) + self:StatusUnLoaded() + + return Cargo +end + + +CARGO_SLINGLOAD = { + ClassName = "CARGO_SLINGLOAD" +} + + +function CARGO_SLINGLOAD:New( CargoType, CargoName, CargoWeight, CargoZone, CargoHostName, CargoCountryID ) + local self = BASE:Inherit( self, CARGO:New( CargoType, CargoName, CargoWeight ) ) + + self:T( { CargoType, CargoName, CargoWeight, CargoZone, CargoHostName, CargoCountryID } ) + + self.CargoHostName = CargoHostName + + -- Cargo will be initialized around the CargoZone position. + self.CargoZone = CargoZone + + self.CargoCount = 0 + self.CargoStaticName = string.format( "%s#%03d", self.CargoName, self.CargoCount ) + + -- The country ID needs to be correctly set. + self.CargoCountryID = CargoCountryID + + CARGOS[self.CargoName] = self + + return self + +end + + +function CARGO_SLINGLOAD:IsLandingRequired() +self:T() + return false +end + + +function CARGO_SLINGLOAD:IsSlingLoad() +self:T() + return true +end + + +function CARGO_SLINGLOAD:Spawn() +self:T() + + local Zone = trigger.misc.getZone( self.CargoZone ) + + local ZonePos = {} + ZonePos.x = Zone.point.x + math.random( Zone.radius / 2 * -1, Zone.radius / 2 ) + ZonePos.y = Zone.point.z + math.random( Zone.radius / 2 * -1, Zone.radius / 2 ) + + self:T( "Cargo Location = " .. ZonePos.x .. ", " .. ZonePos.y ) + + --[[ + -- This does not work in 1.5.2. + CargoStatic = StaticObject.getByName( self.CargoName ) + if CargoStatic then + CargoStatic:destroy() + end + --]] + + CargoStatic = StaticObject.getByName( self.CargoStaticName ) + + if CargoStatic and CargoStatic:isExist() then + CargoStatic:destroy() + end + + -- I need to make every time a new cargo due to bugs in 1.5.2. + + self.CargoCount = self.CargoCount + 1 + self.CargoStaticName = string.format( "%s#%03d", self.CargoName, self.CargoCount ) + + local CargoTemplate = { + ["category"] = "Cargo", + ["shape_name"] = "ab-212_cargo", + ["type"] = "Cargo1", + ["x"] = ZonePos.x, + ["y"] = ZonePos.y, + ["mass"] = self.CargoWeight, + ["name"] = self.CargoStaticName, + ["canCargo"] = true, + ["heading"] = 0, + } + + coalition.addStaticObject( self.CargoCountryID, CargoTemplate ) + +-- end + + return self +end + + +function CARGO_SLINGLOAD:IsNear( Client, LandingZone ) +self:T() + + local Near = false + + return Near +end + + +function CARGO_SLINGLOAD:IsInLandingZone( Client, LandingZone ) +self:T() + + local Near = false + + local CargoStaticUnit = StaticObject.getByName( self.CargoName ) + if CargoStaticUnit then + if routines.IsStaticInZones( CargoStaticUnit, LandingZone ) then + Near = true + end + end + + return Near +end + + +function CARGO_SLINGLOAD:OnBoard( Client, LandingZone, OnBoardSide ) +self:T() + + local Valid = true + + + return Valid +end + + +function CARGO_SLINGLOAD:OnBoarded( Client, LandingZone ) +self:T() + + local OnBoarded = false + + local CargoStaticUnit = StaticObject.getByName( self.CargoName ) + if CargoStaticUnit then + if not routines.IsStaticInZones( CargoStaticUnit, LandingZone ) then + OnBoarded = true + end + end + + return OnBoarded +end + + +function CARGO_SLINGLOAD:UnLoad( Client, TargetZoneName ) +self:T() + + self:T( 'self.CargoName = ' .. self.CargoName ) + self:T( 'self.CargoGroupName = ' .. self.CargoGroupName ) + + self:StatusUnLoaded() + + return Cargo +end diff --git a/Moose/Client.lua b/Moose/Client.lua index bc19ed666..fc74435b1 100644 --- a/Moose/Client.lua +++ b/Moose/Client.lua @@ -23,7 +23,6 @@ CLIENT = { ClientTransport = false, ClientBriefingShown = false, _Menus = {}, - _Cargos = {}, _Tasks = {}, Messages = { } @@ -45,10 +44,9 @@ CLIENT = { -- Mission:AddClient( CLIENT:New( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) function CLIENT:New( ClientName, ClientBriefing ) -trace.f(self.ClassName) - - -- Arrange meta tables local self = BASE:Inherit( self, BASE:New() ) + self:T() + self.ClientName = ClientName self:AddBriefing( ClientBriefing ) self.MessageSwitch = true @@ -59,29 +57,162 @@ end --- Resets a CLIENT. -- @tparam string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client. function CLIENT:Reset( ClientName ) -trace.f(self.ClassName) +self:T() self._Menus = {} - self._Cargos = {} end --- ClientGroup returns the Group of a Client. +-- This function is modified to deal with a couple of bugs in DCS 1.5.3 -- @treturn Group function CLIENT:ClientGroup() ---trace.f(self.ClassName) - local ClientData = Group.getByName( self.ClientName ) - if ClientData and ClientData:isExist() then - trace.i( self.ClassName, self.ClientName .. " : group found!" ) - return ClientData - else --- trace.x( self.ClassName, self.ClientName .. " : no group found!" ) - return nil +--self:T() + +-- local ClientData = Group.getByName( self.ClientName ) +-- if ClientData and ClientData:isExist() then +-- self:T( self.ClientName .. " : group found!" ) +-- return ClientData +-- else +-- return nil +-- end + + local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } + for CoalitionId, CoalitionData in pairs( CoalitionsData ) do + --self:T( { "CoalitionData:", CoalitionData } ) + for UnitId, UnitData in pairs( CoalitionData ) do + --self:T( { "UnitData:", UnitData } ) + if UnitData and UnitData:isExist() then + + local ClientGroup = Group.getByName( self.ClientName ) + if ClientGroup then + self:T( "ClientGroup = " .. self.ClientName ) + if ClientGroup:isExist() then + if ClientGroup:getID() == UnitData:getGroup():getID() then + self:T( "Normal logic" ) + self:T( self.ClientName .. " : group found!" ) + return ClientGroup + end + else + -- Now we need to resolve the bugs in DCS 1.5 ... + -- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil) + self:T( "Bug 1.5 logic" ) + local ClientUnits = _Database.Groups[self.ClientName].Units + self:T( { ClientUnits[1].name, env.getValueDictByKey(ClientUnits[1].name) } ) + for ClientUnitID, ClientUnitData in pairs( ClientUnits ) do + self:T( { tonumber(UnitData:getID()), ClientUnitData.unitId } ) + if tonumber(UnitData:getID()) == ClientUnitData.unitId then + local ClientGroupTemplate = _Database.Groups[self.ClientName].Template + self.ClientGroupID = ClientGroupTemplate.groupId + self.ClientGroupUnit = UnitData + self:T( self.ClientName .. " : group found in bug 1.5 resolvement logic!" ) + return ClientGroup + end + end + end +-- else +-- error( "Client " .. self.ClientName .. " not found!" ) + end + end + end end + + -- For non player clients + local ClientGroup = Group.getByName( self.ClientName ) + if ClientGroup then + self:T( "ClientGroup = " .. self.ClientName ) + if ClientGroup:isExist() then + self:T( "Normal logic" ) + self:T( self.ClientName .. " : group found!" ) + return ClientGroup + end + end + + self.ClientGroupID = nil + self.ClientGroupUnit = nil + + return nil +end + + +function CLIENT:GetClientGroupID() +self:T() + + ClientGroup = self:ClientGroup() + + if ClientGroup then + if ClientGroup:isExist() then + return ClientGroup:getID() + else + return self.ClientGroupID + end + end + + return nil +end + + +function CLIENT:GetClientGroupName() +self:T() + + ClientGroup = self:ClientGroup() + + if ClientGroup then + if ClientGroup:isExist() then + self:T( ClientGroup:getName() ) + return ClientGroup:getName() + else + self:T( self.ClientName ) + return self.ClientName + end + end + + return nil +end + +--- Returns the Unit of the @{CLIENT}. +-- @treturn Unit +function CLIENT:GetClientGroupUnit() +self:T() + + local ClientGroup = self:ClientGroup() + + if ClientGroup then + if ClientGroup:isExist() then + return ClientGroup:getUnits()[1] + else + return self.ClientGroupUnit + end + end + + return nil +end + +function CLIENT:GetUnit() + self:T() + + return UNIT:New( self:GetClientGroupUnit() ) +end + + +--- Returns the Position of the @{CLIENT}. +-- @treturn Position +function CLIENT:ClientPosition() +--self:T() + + ClientGroupUnit = self:GetClientGroupUnit() + + if ClientGroupUnit then + if ClientGroupUnit:isExist() then + return ClientGroupUnit:getPosition() + end + end + + return nil end --- Transport defines that the Client is a Transport. -- @treturn CLIENT function CLIENT:Transport() -trace.f(self.ClassName) +self:T() self.ClientTransport = true return self @@ -91,7 +222,7 @@ end -- @tparam string ClientBriefing is the text defining the Mission briefing. -- @treturn CLIENT function CLIENT:AddBriefing( ClientBriefing ) -trace.f(self.ClassName) +self:T() self.ClientBriefing = ClientBriefing return self end @@ -99,114 +230,29 @@ end --- IsTransport returns if a Client is a transport. -- @treturn bool function CLIENT:IsTransport() -trace.f(self.ClassName) +self:T() return self.ClientTransport end ---- FindCargo finds loaded Cargo within a CLIENT instance. --- Cargo is loaded when certain PICK-UP or DEPLOY Tasks are properly executed. --- @tparam string CargoName is the name of the cargo. --- @treturn CARGO_TYPE -function CLIENT:FindCargo( CargoName ) -trace.f(self.ClassName) - return self._Cargos[CargoName] -end - --- ShowCargo shows the @{CARGO} within the CLIENT to the Player. -- The @{CARGO} is shown throught the MESSAGE system of DCS World. function CLIENT:ShowCargo() -trace.f(self.ClassName) +self:T() - local CargoMsg = "" + local CargoMsg = "" - for CargoName, Cargo in pairs( self._Cargos ) do - if CargoMsg ~= "" then - CargoMsg = CargoMsg .. "\n" - end - CargoMsg = CargoMsg .. Cargo.CargoName .. " Type:" .. Cargo.CargoType.TEXT .. " Weight: " .. Cargo.CargoWeight - end - - if CargoMsg == '' then - CargoMsg = "empty" - end - - self:Message( CargoMsg, 15, self.ClientName .. "/Cargo", "Co-Pilot: Cargo Status", 30 ) - -end - ---- InitCargo allows to initialize @{CARGO} on the CLIENT when the client initializes. --- @tparam string InitCargoNames is a string or a table containing the names of the @{CARGO}s initialized in the Mission. --- @treturn CLIENT -function CLIENT:InitCargo( InitCargoNames ) -trace.f(self.ClassName, { InitCargoNames } ) - - local Valid = true - - if Valid then - if type( InitCargoNames ) == "table" then - self.InitCargoNames = InitCargoNames - else - self.InitCargoNames = { InitCargoNames } + for CargoName, Cargo in pairs( CARGOS ) do + if self == Cargo:IsLoadedInClient() then + CargoMsg = CargoMsg .. Cargo.CargoName .. " Type:" .. Cargo.CargoType .. " Weight: " .. Cargo.CargoWeight .. "\n" + end end - end - return self - -end - ---- AddCargo allows to add @{CARGO} on the CLIENT. --- @tparam string CargoName is the name of the @{CARGO}. --- @tparam string CargoGroupName is the name of an active Group defined within the Mission Editor or Dynamically Spawned. Note that this is only applicable for Unit @{CARGO} Types. --- @tparam CARGO_TYPE CargoType is the Type of the @{CARGO}. --- @tparam number CargoWeight is the weight of the cargo in Kg. --- @tparam string CargoGroupTemplate is the name of an active Group defined within the Mission Editor with "Late Activation". --- @treturn CLIENT -function CLIENT:AddCargo( CargoName, CargoGroupName, CargoType, CargoWeight, CargoGroupTemplate ) -trace.f(self.ClassName, { CargoName, CargoGroupName, CargoType, CargoWeight, CargoGroupTemplate } ) - - local Valid = true - - Valid = routines.ValidateString( CargoName, "CargoName", Valid ) - Valid = routines.ValidateEnumeration( CargoType, "CargoType", CARGO_TYPE, Valid ) - Valid = routines.ValidateNumber( CargoWeight, "CargoWeight", Valid ) - - if Valid then - local Cargo = {} - Cargo.CargoName = CargoName - Cargo.CargoGroupName = CargoGroupName - Cargo.CargoType = CargoType - Cargo.CargoWeight = CargoWeight - if CargoGroupTemplate then - Cargo.CargoGroupTemplate = CargoGroupTemplate + if CargoMsg == "" then + CargoMsg = "empty" end - self._Cargos[CargoName] = Cargo - self:ShowCargo() - end - return self - -end + self:Message( CargoMsg, 15, self.ClientName .. "/Cargo", "Co-Pilot: Cargo Status", 30 ) ---- RemoveCargo removes @{CARGO} from the CLIENT. --- @tparam string CargoName is the name of the @{CARGO}. --- @treturn Cargo -function CLIENT:RemoveCargo( CargoName ) -trace.f(self.ClassName, { CargoName } ) - - - local Valid = true - local Cargo = nil - - Valid = routines.ValidateString( CargoName, "CargoName", Valid ) - - if Valid then - trace.i( "CLIENT", "RemoveCargo: CargoName = " .. CargoName ) - Cargo = routines.utils.deepCopy( self._Cargos[CargoName] ) - self._Cargos[CargoName] = nil - end - - return Cargo - end --- SwitchMessages is a local function called by the DCS World Menu system to switch off messages. @@ -222,13 +268,13 @@ end -- @tparam string MessageCategory is the category of the message (the title). -- @tparam number MessageInterval is the interval in seconds between the display of the Message when the CLIENT is in the air. function CLIENT:Message( Message, MessageDuration, MessageId, MessageCategory, MessageInterval ) -trace.f( self.ClassName, { Message, MessageDuration, MessageId, MessageCategory, MessageInterval } ) +self:T() if not self.MenuMessages then - if self:ClientGroup() and self:ClientGroup():getID() then - self.MenuMessages = MENU_SUB_GROUP:New( self:ClientGroup():getID(), 'Messages' ) - self.MenuRouteMessageOn = MENU_COMMAND_GROUP:New( self:ClientGroup():getID(), 'Messages On', self.MenuMessages, CLIENT.SwitchMessages, { self, true } ) - self.MenuRouteMessageOff = MENU_COMMAND_GROUP:New( self:ClientGroup():getID(),'Messages Off', self.MenuMessages, CLIENT.SwitchMessages, { self, false } ) + if self:GetClientGroupID() then + self.MenuMessages = MENU_SUB_GROUP:New( self:GetClientGroupID(), 'Messages' ) + self.MenuRouteMessageOn = MENU_COMMAND_GROUP:New( self:GetClientGroupID(), 'Messages On', self.MenuMessages, CLIENT.SwitchMessages, { self, true } ) + self.MenuRouteMessageOff = MENU_COMMAND_GROUP:New( self:GetClientGroupID(),'Messages Off', self.MenuMessages, CLIENT.SwitchMessages, { self, false } ) end end @@ -248,7 +294,7 @@ trace.f( self.ClassName, { Message, MessageDuration, MessageId, MessageCategory, end MESSAGE:New( Message, MessageCategory, MessageDuration, MessageId ):ToClient( self ) else - if self:ClientGroup() and self:ClientGroup():getUnits() and self:ClientGroup():getUnits()[1] and not self:ClientGroup():getUnits()[1]:inAir() then + if self:GetClientGroupUnit() and not self:GetClientGroupUnit():inAir() then if timer.getTime() - self.Messages[MessageId].MessageTime >= self.Messages[MessageId].MessageDuration + 10 then MESSAGE:New( Message, MessageCategory, MessageDuration, MessageId ):ToClient( self ) self.Messages[MessageId].MessageTime = timer.getTime() diff --git a/Moose/Database.lua b/Moose/Database.lua index 986896a78..9f41192a4 100644 --- a/Moose/Database.lua +++ b/Moose/Database.lua @@ -13,6 +13,7 @@ DATABASE = { NavPoints = {}, Statics = {}, Players = {}, + ActivePlayers = {}, ClientsByName = {}, ClientsByID = {}, } @@ -27,7 +28,7 @@ DATABASECategory = { [Unit.Category.AIRPLANE] = "Plane", [Unit.Category.HELICOPTER] = "Helicopter", - [Unit.Category.GROUND_UNIT] = "Ground", + [Unit.Category.GROUND_UNIT] = "Vehicle", [Unit.Category.SHIP] = "Ship", [Unit.Category.STRUCTURE] = "Structure", } @@ -39,7 +40,6 @@ DATABASECategory = -- -- Define a new DATABASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE. -- DBObject = DATABASE:New() function DATABASE:New() -trace.f(self.ClassName ) -- Inherits from BASE local self = BASE:Inherit( self, BASE:New() ) @@ -120,14 +120,14 @@ trace.f(self.ClassName ) return self end + --- Instantiate new Groups within the DCSRTE. -- This method expects EXACTLY the same structure as a structure within the ME, and needs 2 additional fields defined: -- SpawnCountryID, SpawnCategoryID -- This method is used by the SPAWN class. function DATABASE:Spawn( SpawnTemplate ) -trace.f( self.ClassName, SpawnTemplate ) - trace.i( self.ClassName, { SpawnTemplate.SpawnCountryID, SpawnTemplate.SpawnCategoryID, SpawnTemplate.name } ) + self:T( { SpawnTemplate.SpawnCountryID, SpawnTemplate.SpawnCategoryID, SpawnTemplate.name } ) local SpawnCountryID = SpawnTemplate.SpawnCountryID local SpawnCategoryID = SpawnTemplate.SpawnCategoryID @@ -140,16 +140,18 @@ trace.f( self.ClassName, SpawnTemplate ) coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate ) end + --- Set a status to a Group within the Database, this to check crossing events for example. function DATABASE:SetStatusGroup( GroupName, Status ) -trace.f( self.ClassName, Status ) + self:T( Status ) self.Groups[GroupName].Status = Status end + --- Get a status to a Group within the Database, this to check crossing events for example. function DATABASE:GetStatusGroup( GroupName ) -trace.f( self.ClassName, Status ) + self:T( Status ) if self.Groups[GroupName] then return self.Groups[GroupName].Status @@ -162,6 +164,7 @@ end --- Private -- @section Private + --- Registers new Group Templates within the DATABASE Object. function DATABASE:_RegisterGroup( GroupTemplate ) @@ -175,7 +178,9 @@ function DATABASE:_RegisterGroup( GroupTemplate ) self.Groups[GroupTemplateName].Template = GroupTemplate self.Groups[GroupTemplateName].groupId = GroupTemplate.groupId self.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units - trace.i( self.ClassName, { "Group", self.Groups[GroupTemplateName].GroupName, self.Groups[GroupTemplateName].UnitCount } ) + self.Groups[GroupTemplateName].Units = GroupTemplate.units + + self:T( { "Group", self.Groups[GroupTemplateName].GroupName, self.Groups[GroupTemplateName].UnitCount } ) for unit_num, UnitTemplate in pairs(GroupTemplate.units) do @@ -190,20 +195,24 @@ function DATABASE:_RegisterGroup( GroupTemplate ) self.ClientsByName[UnitTemplateName] = UnitTemplate self.ClientsByID[UnitTemplate.unitId] = UnitTemplate end - trace.i( self.ClassName, { "Unit", self.Units[UnitTemplateName].UnitName } ) + self:T( { "Unit", self.Units[UnitTemplateName].UnitName } ) end end + --- Events -- @section Events + --- Track DCSRTE DEAD or CRASH events for the internal scoring. function DATABASE:OnDeadOrCrash( event ) -trace.f( self.ClassName, { event } ) + self:T( { event } ) - local TargetUnitName = nil - local TargetGroupName = nil - local TargetPlayerName = nil + local TargetUnit = nil + local TargetGroup = nil + local TargetUnitName = "" + local TargetGroupName = "" + local TargetPlayerName = "" local TargetCoalition = nil local TargetCategory = nil local TargetType = nil @@ -212,30 +221,42 @@ trace.f( self.ClassName, { event } ) local TargetUnitType = nil if event.initiator and Object.getCategory(event.initiator) == Object.Category.UNIT then - TargetUnitName = event.initiator:getName() - TargetGroupName = Unit.getGroup(event.initiator):getName() - TargetPlayerName = event.initiator:getPlayerName() + + TargetUnit = event.initiator + TargetGroup = Unit.getGroup( TargetUnit ) + TargetUnitDesc = TargetUnit:getDesc() + + TargetUnitName = TargetUnit:getName() + if TargetGroup and TargetGroup:isExist() then + TargetGroupName = TargetGroup:getName() + end + TargetPlayerName = TargetUnit:getPlayerName() - TargetCoalition = Unit.getGroup(event.initiator):getCoalition() - TargetCategory = Unit.getGroup(event.initiator):getCategory() - TargetType = event.initiator:getTypeName() + TargetCoalition = TargetUnit:getCoalition() + --TargetCategory = TargetUnit:getCategory() + TargetCategory = TargetUnitDesc.category -- Workaround + TargetType = TargetUnit:getTypeName() TargetUnitCoalition = DATABASECoalition[TargetCoalition] TargetUnitCategory = DATABASECategory[TargetCategory] TargetUnitType = TargetType - trace.i( self.ClassName, { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) + self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) end for PlayerName, PlayerData in pairs( self.Players ) do if PlayerData then -- This should normally not happen, but i'll test it anyway. - trace.i( self.ClassName, "Something got killed" ) + self:T( "Something got killed" ) -- Some variables - local InitUnitCoalition = DATABASECoalition[PlayerData.UnitCoalition] - local InitUnitCategory = DATABASECategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType local InitUnitName = PlayerData.UnitName + local InitUnitType = PlayerData.UnitType + local InitCoalition = PlayerData.UnitCoalition + local InitCategory = PlayerData.UnitCategory + local InitUnitCoalition = DATABASECoalition[InitCoalition] + local InitUnitCategory = DATABASECategory[InitCategory] + + self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) -- What is he hitting? if TargetCategory then @@ -251,17 +272,17 @@ trace.f( self.ClassName, { event } ) PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = 0 end - if PlayerData.UnitCoalition == TargetCoalition then + if InitCoalition == TargetCoalition then PlayerData.Kill[TargetCategory][TargetType].Penalty = PlayerData.Kill[TargetCategory][TargetType].Penalty + 25 PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = PlayerData.Kill[TargetCategory][TargetType].PenaltyKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill .. " times. Score: " .. PlayerData.Kill[TargetCategory][TargetType].Penalty, - "Game Status: Score", 20, "/PENALTY" .. PlayerName .. "/" .. InitUnitName ):ToAll() + MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + PlayerData.Kill[TargetCategory][TargetType].PenaltyKill .. " times. Penalty: -" .. PlayerData.Kill[TargetCategory][TargetType].Penalty, + "Game Status: Penalty", 20, "/PENALTY" .. PlayerName .. "/" .. InitUnitName ):ToAll() self:ScoreAdd( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else PlayerData.Kill[TargetCategory][TargetType].Score = PlayerData.Kill[TargetCategory][TargetType].Score + 10 PlayerData.Kill[TargetCategory][TargetType].ScoreKill = PlayerData.Kill[TargetCategory][TargetType].ScoreKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerData.Kill[TargetCategory][TargetType].ScoreKill .. " times. Score: " .. PlayerData.Kill[TargetCategory][TargetType].Score, "Game Status: Score", 20, "/SCORE" .. PlayerName .. "/" .. InitUnitName ):ToAll() self:ScoreAdd( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) @@ -272,12 +293,14 @@ trace.f( self.ClassName, { event } ) end end + --- Scheduled -- @section Scheduled + --- Follows new players entering Clients within the DCSRTE. function DATABASE:_FollowPlayers() -trace.scheduled( self.ClassName, "_FollowPlayers" ) + self:T( "_FollowPlayers" ) local ClientUnit = 0 local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } @@ -286,73 +309,73 @@ trace.scheduled( self.ClassName, "_FollowPlayers" ) local AlivePlayerUnits = {} for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - trace.l( self.ClassName, "_FollowPlayers", CoalitionData ) + self:T( { "_FollowPlayers", CoalitionData } ) for UnitId, UnitData in pairs( CoalitionData ) do self:_AddPlayerFromUnit( UnitData ) end end end + --- Private -- @section Private + --- Add a new player entering a Unit. function DATABASE:_AddPlayerFromUnit( UnitData ) -trace.f( self.ClassName, UnitData ) + self:T( UnitData ) if UnitData:isExist() then local UnitName = UnitData:getName() - local GroupData = UnitData:getGroup() + local PlayerName = UnitData:getPlayerName() + local UnitDesc = UnitData:getDesc() + local UnitCategory = UnitDesc.category + local UnitCoalition = UnitData:getCoalition() + local UnitTypeName = UnitData:getTypeName() - if GroupData and GroupData:isExist() then - local GroupName = GroupData:getName() - local PlayerName = UnitData:getPlayerName() + self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) - trace.i(self.ClassName, "Player : " .. PlayerName .. " Unit : " .. UnitName .. " Group : " .. GroupName ) - - if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... - self.Players[PlayerName] = {} - self.Players[PlayerName].Hit = {} - self.Players[PlayerName].Kill = {} - self.Players[PlayerName].Mission = {} - - -- for CategoryID, CategoryName in pairs( DATABASECategory ) do - -- self.Players[PlayerName].Hit[CategoryID] = {} - -- self.Players[PlayerName].Kill[CategoryID] = {} - -- end - self.Players[PlayerName].HitPlayers = {} - self.Players[PlayerName].HitUnits = {} - self.Players[PlayerName].Penalty = 0 - self.Players[PlayerName].PenaltyCoalition = 0 - end - - if not self.Players[PlayerName].UnitCoalition then - self.Players[PlayerName].UnitCoalition = Unit.getGroup(UnitData):getCoalition() - else - if self.Players[PlayerName].UnitCoalition ~= Unit.getGroup(UnitData):getCoalition() then - self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 - self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. DATABASECoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. DATABASECoalition[Unit.getGroup(UnitData):getCoalition()] .. - "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", - "Game Status: Penalty", 20, "/PENALTYCOALITION" .. PlayerName ):ToAll() - self:ScoreAdd( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, DATABASECoalition[self.Players[PlayerName].UnitCoalition], DATABASECategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, - UnitName, DATABASECategory[Unit.getGroup(UnitData):getCoalition()], DATABASECategory[Unit.getGroup(UnitData):getCategory()], UnitData:getTypeName() ) - end - end - self.Players[PlayerName].UnitName = UnitName - self.Players[PlayerName].GroupName = GroupName - - self.Players[PlayerName].UnitCoalition = Unit.getGroup(UnitData):getCoalition() - self.Players[PlayerName].UnitCategory = Unit.getGroup(UnitData):getCategory() - self.Players[PlayerName].UnitType = UnitData:getTypeName() + if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... + self.Players[PlayerName] = {} + self.Players[PlayerName].Hit = {} + self.Players[PlayerName].Kill = {} + self.Players[PlayerName].Mission = {} + + -- for CategoryID, CategoryName in pairs( DATABASECategory ) do + -- self.Players[PlayerName].Hit[CategoryID] = {} + -- self.Players[PlayerName].Kill[CategoryID] = {} + -- end + self.Players[PlayerName].HitPlayers = {} + self.Players[PlayerName].HitUnits = {} + self.Players[PlayerName].Penalty = 0 + self.Players[PlayerName].PenaltyCoalition = 0 end + + if not self.Players[PlayerName].UnitCoalition then + self.Players[PlayerName].UnitCoalition = UnitCoalition + else + if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then + self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 + self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 + MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. DATABASECoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. DATABASECoalition[UnitCoalition] .. + "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", + "Game Status: Penalty", 20, "/PENALTYCOALITION" .. PlayerName ):ToAll() + self:ScoreAdd( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, DATABASECoalition[self.Players[PlayerName].UnitCoalition], DATABASECategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, + UnitName, DATABASECoalition[UnitCoalition], DATABASECategory[UnitCategory], UnitData:getTypeName() ) + end + end + self.Players[PlayerName].UnitName = UnitName + self.Players[PlayerName].UnitCoalition = UnitCoalition + self.Players[PlayerName].UnitCategory = UnitCategory + self.Players[PlayerName].UnitType = UnitTypeName end end + --- Registers Scores the players completing a Mission Task. function DATABASE:_AddMissionTaskScore( PlayerUnit, MissionName, Score ) -trace.f( self.ClassName, { PlayerUnit, MissionName, Score } ) + self:T( { PlayerUnit, MissionName, Score } ) local PlayerName = PlayerUnit:getPlayerName() @@ -361,6 +384,9 @@ trace.f( self.ClassName, { PlayerUnit, MissionName, Score } ) self.Players[PlayerName].Mission[MissionName].ScoreTask = 0 self.Players[PlayerName].Mission[MissionName].ScoreMission = 0 end + + self:T( PlayerName ) + self:T( self.Players[PlayerName].Mission[MissionName] ) self.Players[PlayerName].Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score @@ -374,7 +400,7 @@ end --- Registers Mission Scores for possible multiple players that contributed in the Mission. function DATABASE:_AddMissionScore( MissionName, Score ) -trace.f( self.ClassName, { PlayerUnit, MissionName, Score } ) + self:T( { PlayerUnit, MissionName, Score } ) for PlayerName, PlayerData in pairs( self.Players ) do @@ -388,15 +414,18 @@ trace.f( self.ClassName, { PlayerUnit, MissionName, Score } ) end end + --- Events -- @section Events -function DATABASE:OnHit( event ) -trace.f( self.ClassName, { event } ) - local InitUnitName = nil - local InitGroupName = nil - local InitPlayerName = nil +function DATABASE:OnHit( event ) + self:T( { event } ) + + local InitUnit = nil + local InitUnitName = "" + local InitGroupName = "" + local InitPlayerName = "dummy" local InitCoalition = nil local InitCategory = nil @@ -405,9 +434,10 @@ trace.f( self.ClassName, { event } ) local InitUnitCategory = nil local InitUnitType = nil - local TargetUnitName = nil - local TargetGroupName = nil - local TargetPlayerName = nil + local TargetUnit = nil + local TargetUnitName = "" + local TargetGroupName = "" + local TargetPlayerName = "" local TargetCoalition = nil local TargetCategory = nil @@ -419,49 +449,65 @@ trace.f( self.ClassName, { event } ) if event.initiator and event.initiator:getName() then if event.initiator and Object.getCategory(event.initiator) == Object.Category.UNIT then + + InitUnit = event.initiator + InitGroup = Unit.getGroup( InitUnit ) + InitUnitDesc = InitUnit:getDesc() - InitUnitName = event.initiator:getName() - InitGroupName = Unit.getGroup(event.initiator):getName() - InitPlayerName = event.initiator:getPlayerName() + InitUnitName = InitUnit:getName() + if InitGroup and InitGroup:isExist() then + InitGroupName = InitGroup:getName() + end + InitPlayerName = InitUnit:getPlayerName() - InitCoalition = Unit.getGroup(event.initiator):getCoalition() - InitCategory = Unit.getGroup(event.initiator):getCategory() - InitType = event.initiator:getTypeName() + InitCoalition = InitUnit:getCoalition() + --InitCategory = InitUnit:getCategory() + InitCategory = InitUnitDesc.category -- Workaround + InitType = InitUnit:getTypeName() InitUnitCoalition = DATABASECoalition[InitCoalition] InitUnitCategory = DATABASECategory[InitCategory] InitUnitType = InitType - trace.i( self.ClassName, { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } ) + self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } ) + self:T( { InitUnitDesc } ) end if event.target and Object.getCategory(event.target) == Object.Category.UNIT then + + TargetUnit = event.target + TargetGroup = Unit.getGroup( TargetUnit ) + TargetUnitDesc = TargetUnit:getDesc() - TargetUnitName = event.target:getName() - TargetGroupName = Unit.getGroup(event.target):getName() - TargetPlayerName = event.target:getPlayerName() + TargetUnitName = TargetUnit:getName() + if TargetGroup and TargetGroup:isExist() then + TargetGroupName = TargetGroup:getName() + end + TargetPlayerName = TargetUnit:getPlayerName() - TargetCoalition = Unit.getGroup(event.target):getCoalition() - TargetCategory = Unit.getGroup(event.target):getCategory() - TargetType = event.target:getTypeName() + TargetCoalition = TargetUnit:getCoalition() + --TargetCategory = TargetUnit:getCategory() + TargetCategory = TargetUnitDesc.category -- Workaround + TargetType = TargetUnit:getTypeName() TargetUnitCoalition = DATABASECoalition[TargetCoalition] TargetUnitCategory = DATABASECategory[TargetCategory] TargetUnitType = TargetType - trace.i( self.ClassName, { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } ) + self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } ) + self:T( { TargetUnitDesc } ) end if InitPlayerName ~= nil then -- It is a player that is hitting something - self:_AddPlayerFromUnit( event.initiator ) + self:_AddPlayerFromUnit( InitUnit ) if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( event.target ) + self:_AddPlayerFromUnit( TargetUnit ) self.Players[InitPlayerName].HitPlayers = self.Players[InitPlayerName].HitPlayers + 1 end - trace.i( self.ClassName, "Hitting Something" ) + self:T( "Hitting Something" ) -- What is he hitting? if TargetCategory then if not self.Players[InitPlayerName].Hit[TargetCategory] then @@ -479,7 +525,7 @@ trace.f( self.ClassName, { event } ) self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty + 10 self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit + 1 MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: " .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty, + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: -" .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty, "Game Status: Penalty", 20, "/PENALTY" .. InitPlayerName .. "/" .. InitUnitName ):ToAll() self:ScoreAdd( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else @@ -498,14 +544,132 @@ trace.f( self.ClassName, { event } ) end end + function DATABASE:ReportScoreAll() +env.info( "Hello World " ) + local ScoreMessage = "" local PlayerMessage = "" + + self:T( "Score Report" ) for PlayerName, PlayerData in pairs( self.Players ) do if PlayerData then -- This should normally not happen, but i'll test it anyway. - trace.i( self.ClassName, "Score" ) + self:T( "Score Player: " .. PlayerName ) + + -- Some variables + local InitUnitCoalition = DATABASECoalition[PlayerData.UnitCoalition] + local InitUnitCategory = DATABASECategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName + + local PlayerScore = 0 + local PlayerPenalty = 0 + + ScoreMessage = ":\n" + + local ScoreMessageHits = "" + + for CategoryID, CategoryName in pairs( DATABASECategory ) do + self:T( CategoryName ) + if PlayerData.Hit[CategoryID] then + local Score = 0 + local ScoreHit = 0 + local Penalty = 0 + local PenaltyHit = 0 + self:T( "Hit scores exist for player " .. PlayerName ) + for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do + Score = Score + UnitData.Score + ScoreHit = ScoreHit + UnitData.ScoreHit + Penalty = Penalty + UnitData.Penalty + PenaltyHit = UnitData.PenaltyHit + end + local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageHit ) + ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageHits ~= "" then + ScoreMessage = ScoreMessage .. " Hits: " .. ScoreMessageHits .. "\n" + end + + local ScoreMessageKills = "" + for CategoryID, CategoryName in pairs( DATABASECategory ) do + self:T( "Kill scores exist for player " .. PlayerName ) + if PlayerData.Kill[CategoryID] then + local Score = 0 + local ScoreKill = 0 + local Penalty = 0 + local PenaltyKill = 0 + + for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do + Score = Score + UnitData.Score + ScoreKill = ScoreKill + UnitData.ScoreKill + Penalty = Penalty + UnitData.Penalty + PenaltyKill = PenaltyKill + UnitData.PenaltyKill + end + + local ScoreMessageKill = string.format( " %s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageKill ) + ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill + + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageKills ~= "" then + ScoreMessage = ScoreMessage .. " Kills: " .. ScoreMessageKills .. "\n" + end + + local ScoreMessageCoalitionChangePenalties = "" + if PlayerData.PenaltyCoalition ~= 0 then + ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) + PlayerPenalty = PlayerPenalty + PlayerData.Penalty + end + if ScoreMessageCoalitionChangePenalties ~= "" then + ScoreMessage = ScoreMessage .. " Coalition Penalties: " .. ScoreMessageCoalitionChangePenalties .. "\n" + end + + local ScoreMessageMission = "" + local ScoreMission = 0 + local ScoreTask = 0 + for MissionName, MissionData in pairs( PlayerData.Mission ) do + ScoreMission = ScoreMission + MissionData.ScoreMission + ScoreTask = ScoreTask + MissionData.ScoreTask + ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " + end + PlayerScore = PlayerScore + ScoreMission + ScoreTask + + if ScoreMessageMission ~= "" then + ScoreMessage = ScoreMessage .. " Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")\n" + end + + PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score:%d (%d Score -%d Penalties)%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) + end + end + MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() +end + + +function DATABASE:ReportScorePlayer() + +env.info( "Hello World " ) + + local ScoreMessage = "" + local PlayerMessage = "" + + self:T( "Score Report" ) + + for PlayerName, PlayerData in pairs( self.Players ) do + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) -- Some variables local InitUnitCoalition = DATABASECoalition[PlayerData.UnitCoalition] @@ -521,18 +685,22 @@ function DATABASE:ReportScoreAll() local ScoreMessageHits = "" for CategoryID, CategoryName in pairs( DATABASECategory ) do + self:T( CategoryName ) if PlayerData.Hit[CategoryID] then local Score = 0 local ScoreHit = 0 local Penalty = 0 local PenaltyHit = 0 + self:T( "Hit scores exist for player " .. PlayerName ) for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do Score = Score + UnitData.Score ScoreHit = ScoreHit + UnitData.ScoreHit Penalty = Penalty + UnitData.Penalty PenaltyHit = UnitData.PenaltyHit end - ScoreMessageHits = ScoreMessageHits .. string.format( " %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreHit, PenaltyHit ) + local ScoreMessageHit = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreHit, PenaltyHit ) + self:T( ScoreMessageHit ) + ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit PlayerScore = PlayerScore + Score PlayerPenalty = PlayerPenalty + Penalty else @@ -540,11 +708,12 @@ function DATABASE:ReportScoreAll() end end if ScoreMessageHits ~= "" then - ScoreMessage = ScoreMessage .. " Hits: " .. ScoreMessageHits .. " " + ScoreMessage = ScoreMessage .. "\n Hits: " .. ScoreMessageHits .. " " end local ScoreMessageKills = "" for CategoryID, CategoryName in pairs( DATABASECategory ) do + self:T( "Kill scores exist for player " .. PlayerName ) if PlayerData.Kill[CategoryID] then local Score = 0 local ScoreKill = 0 @@ -557,8 +726,10 @@ function DATABASE:ReportScoreAll() Penalty = Penalty + UnitData.Penalty PenaltyKill = PenaltyKill + UnitData.PenaltyKill end - - ScoreMessageKills = ScoreMessageKills .. string.format( " %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill ) + + local ScoreMessageKill = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill ) + self:T( ScoreMessageKill ) + ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill PlayerScore = PlayerScore + Score PlayerPenalty = PlayerPenalty + Penalty @@ -567,16 +738,16 @@ function DATABASE:ReportScoreAll() end end if ScoreMessageKills ~= "" then - ScoreMessage = ScoreMessage .. " Kills: " .. ScoreMessageKills .. " " + ScoreMessage = ScoreMessage .. "\n Kills: " .. ScoreMessageKills .. " " end local ScoreMessageCoalitionChangePenalties = "" if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( "-%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) + ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) PlayerPenalty = PlayerPenalty + PlayerData.Penalty end if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. " Coalition: " .. ScoreMessageCoalitionChangePenalties .. " " + ScoreMessage = ScoreMessage .. "\n Coalition: " .. ScoreMessageCoalitionChangePenalties .. " " end local ScoreMessageMission = "" @@ -590,26 +761,26 @@ function DATABASE:ReportScoreAll() PlayerScore = PlayerScore + ScoreMission + ScoreTask if ScoreMessageMission ~= "" then - ScoreMessage = ScoreMessage .. " Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") " + ScoreMessage = ScoreMessage .. "\n Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") " end - PlayerMessage = string.format( " Player '%s' Score = %d ( %d Score, -%d Penalties ):", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty ) - MESSAGE:New( PlayerMessage .. ScoreMessage, "Player Scores", 30, "/SCORE/" .. PlayerName ):ToAll() + PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties ):%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) end end -end - -function DATABASE:ReportScorePlayer() - + MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() end + function DATABASE:ScoreMenu() local ReportScore = SUBMENU:New( 'Scoring' ) local ReportAllScores = COMMANDMENU:New( 'Score All Active Players', ReportScore, DATABASE.ReportScoreAll, self ) local ReportPlayerScores = COMMANDMENU:New('Your Current Score', ReportScore, DATABASE.ReportScorePlayer, self ) end + + + -- File Logic for tracking the scores function DATABASE:SecondsToClock(sSeconds) @@ -625,6 +796,7 @@ local nSeconds = sSeconds end end + function DATABASE:ScoreOpen() if lfs then local fdir = lfs.writedir() .. [[Logs\]] .. "Player_Scores_" .. os.date( "%Y-%m-%d_%H-%M-%S" ) .. ".csv" @@ -632,12 +804,13 @@ function DATABASE:ScoreOpen() if not self.StatFile then error( "Error: Cannot open 'Player Scores.csv' file in " .. lfs.writedir() ) end - self.StatFile:write( '"Run-ID";Time;"PlayerName";"ScoreType";"PlayerUnitCoaltion";"PlayerUnitCategory";"PlayerUnitType"; "PlayerUnitName";"TargetUnitCoalition";"TargetUnitCategory";"TargetUnitType";"TargetUnitName";Times;Score\n' ) + self.StatFile:write( '"RunID";"Time";"PlayerName";"ScoreType";"PlayerUnitCoaltion";"PlayerUnitCategory";"PlayerUnitType";"PlayerUnitName";"TargetUnitCoalition";"TargetUnitCategory";"TargetUnitType";"TargetUnitName";"Times";"Score"\n' ) self.RunID = os.date("%y-%m-%d_%H-%M-%S") end end + function DATABASE:ScoreAdd( PlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) --write statistic information to file local ScoreTime = self:SecondsToClock(timer.getTime()) @@ -648,11 +821,12 @@ function DATABASE:ScoreAdd( PlayerName, ScoreType, ScoreTimes, ScoreAmount, Play if PlayerUnit then if not PlayerUnitCategory then - PlayerUnitCategory = DATABASECategory[Unit.getGroup(PlayerUnit):getCategory()] + --PlayerUnitCategory = DATABASECategory[PlayerUnit:getCategory()] + PlayerUnitCategory = DATABASECategory[PlayerUnit:getDesc().category] end if not PlayerUnitCoalition then - PlayerUnitCoalition = DATABASECoalition[Unit.getGroup(PlayerUnit):getCoalition()] + PlayerUnitCoalition = DATABASECoalition[PlayerUnit:getCoalition()] end if not PlayerUnitType then @@ -695,6 +869,7 @@ function DATABASE:ScoreAdd( PlayerName, ScoreType, ScoreTimes, ScoreAmount, Play self.StatFile:write( "\n" ) end end + function LogClose() if lfs then diff --git a/Moose/DeployTask.lua b/Moose/DeployTask.lua index 7ba43909c..612f1f6ad 100644 --- a/Moose/DeployTask.lua +++ b/Moose/DeployTask.lua @@ -13,39 +13,64 @@ DEPLOYTASK = { --- Creates a new DEPLOYTASK object, which models the sequence of STAGEs to unload a cargo. -- @tparam table{string,...}|string LandingZones Table or name of the zone(s) where Cargo is to be unloaded. -- @tparam CARGO_TYPE CargoType Type of the Cargo. -function DEPLOYTASK:New( LandingZones, CargoType ) -trace.f(self.ClassName) +function DEPLOYTASK:New( CargoType ) + local self = BASE:Inherit( self, TASK:New() ) + self:T() - -- Child holds the inherited instance of the DEPLOYTASK Class to the BASE class. - local Child = BASE:Inherit( self, TASK:New() ) - local Valid = true - Valid = routines.ValidateZone( LandingZones, "LandingZones", Valid ) - Valid = routines.ValidateEnumeration( CargoType, "CargoType", CARGO_TYPE, Valid ) - if Valid then - Child.Name = 'Deploy Cargo' - Child.TaskBriefing = "Fly to one of the indicated landing zones and deploy " .. CargoType.TEXT .. ". Your co-pilot will provide you with the directions (required flight angle in degrees) and the distance (in km) to the deployment zone." - if type( LandingZones ) == "table" then - Child.LandingZones = LandingZones - else - Child.LandingZones = { LandingZones } - end - Child.CargoType = CargoType - Child.GoalVerb = CargoType.TEXT .. " " .. self.GoalVerb - Child.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGELANDING:New(), STAGELANDED:New(), STAGEUNLOAD:New(), STAGEDONE:New() } - Child.SetStage( Child, 1 ) + self.Name = 'Deploy Cargo' + self.TaskBriefing = "Fly to one of the indicated landing zones and deploy " .. CargoType .. ". Your co-pilot will provide you with the directions (required flight angle in degrees) and the distance (in km) to the deployment zone." + self.CargoType = CargoType + self.GoalVerb = CargoType .. " " .. self.GoalVerb + self.Stages = { STAGE_CARGO_INIT:New(), STAGE_CARGO_LOAD:New(), STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGELANDING:New(), STAGELANDED:New(), STAGEUNLOAD:New(), STAGEDONE:New() } + self.SetStage( self, 1 ) end - return Child + return self +end + +function DEPLOYTASK:ToZone( LandingZone ) +self:T() + + self.LandingZones.LandingZoneNames[LandingZone.CargoZoneName] = LandingZone.CargoZoneName + self.LandingZones.LandingZones[LandingZone.CargoZoneName] = LandingZone + + return self +end + + +function DEPLOYTASK:InitCargo( InitCargos ) +self:T( { InitCargos } ) + + if type( InitCargos ) == "table" then + self.Cargos.InitCargos = InitCargos + else + self.Cargos.InitCargos = { InitCargos } + end + + return self +end + + +function DEPLOYTASK:LoadCargo( LoadCargos ) +self:T( { LoadCargos } ) + + if type( LoadCargos ) == "table" then + self.Cargos.LoadCargos = LoadCargos + else + self.Cargos.LoadCargos = { LoadCargos } + end + + return self end --- When the cargo is unloaded, it will move to the target zone name. -- @tparam string TargetZoneName Name of the Zone to where the Cargo should move after unloading. function DEPLOYTASK:SetCargoTargetZoneName( TargetZoneName ) -trace.f(self.ClassName) +self:T() local Valid = true @@ -60,57 +85,68 @@ trace.f(self.ClassName) end function DEPLOYTASK:AddCargoMenus( Client, Cargos, TransportRadius ) -trace.f(self.ClassName, {Client, Cargos, TransportRadius}) +self:T() - for CargoID, CargoData in pairs( Client._Cargos ) do + local ClientGroupID = Client:GetClientGroupID() + + trace.i( self.ClassName, ClientGroupID ) + + for CargoID, Cargo in pairs( Cargos ) do - trace.i( self.ClassName, { CargoData.CargoName } ) - if Client._Menus[CargoData.CargoType] == nil then - Client._Menus[CargoData.CargoType] = {} - end + trace.i( self.ClassName, { Cargo.ClassName, Cargo.CargoName, Cargo.CargoType, Cargo.CargoWeight } ) - if not Client._Menus[CargoData.CargoType].DeployMenu then - Client._Menus[CargoData.CargoType].DeployMenu = missionCommands.addSubMenuForGroup( - Client:ClientGroup():getID(), - self.TEXT[1], - nil + if Cargo:IsStatusLoaded() and Client == Cargo:IsLoadedInClient() then + + if Client._Menus[Cargo.CargoType] == nil then + Client._Menus[Cargo.CargoType] = {} + end + + if not Client._Menus[Cargo.CargoType].DeployMenu then + Client._Menus[Cargo.CargoType].DeployMenu = missionCommands.addSubMenuForGroup( + ClientGroupID, + self.TEXT[1] .. " " .. Cargo.CargoType, + nil + ) + trace.i( self.ClassName, 'Added DeployMenu ' .. self.TEXT[1] ) + end + + if Client._Menus[Cargo.CargoType].DeploySubMenus == nil then + Client._Menus[Cargo.CargoType].DeploySubMenus = {} + end + + if Client._Menus[Cargo.CargoType].DeployMenu == nil then + trace.i( self.ClassName, 'deploymenu is nil' ) + end + + Client._Menus[Cargo.CargoType].DeploySubMenus[ #Client._Menus[Cargo.CargoType].DeploySubMenus + 1 ] = missionCommands.addCommandForGroup( + ClientGroupID, + Cargo.CargoName .. " ( " .. Cargo.CargoWeight .. "kg )", + Client._Menus[Cargo.CargoType].DeployMenu, + self.MenuAction, + { ReferenceTask = self, CargoTask = Cargo } ) - trace.i( self.ClassName, 'Added DeployMenu ' .. self.TEXT[1] ) + trace.i( self.ClassName, 'Added DeploySubMenu ' .. Cargo.CargoType .. ":" .. Cargo.CargoName .. " ( " .. Cargo.CargoWeight .. "kg )" ) end - - if Client._Menus[CargoData.CargoType].DeploySubMenus == nil then - Client._Menus[CargoData.CargoType].DeploySubMenus = {} - end - - if Client._Menus[CargoData.CargoType].DeployMenu == nil then - trace.i( self.ClassName, 'deploymenu is nil' ) - end - - Client._Menus[CargoData.CargoType].DeploySubMenus[ #Client._Menus[CargoData.CargoType].DeploySubMenus + 1 ].MenuPath = missionCommands.addCommandForGroup( - Client:ClientGroup():getID(), - CargoData.CargoName .. " ( " .. CargoData.CargoWeight .. "kg )", - Client._Menus[CargoData.CargoType].DeployMenu, - self.MenuAction, - { ReferenceTask = self, CargoName = CargoData.CargoName } - ) - trace.i( self.ClassName, 'Added DeploySubMenu ' .. CargoData.CargoType.TEXT .. ":" .. CargoData.CargoName .. " ( " .. CargoData.CargoWeight .. "kg )" ) end end function DEPLOYTASK:RemoveCargoMenus( Client ) -trace.f(self.ClassName, { Client } ) +self:T() + + local ClientGroupID = Client:GetClientGroupID() + trace.i( self.ClassName, ClientGroupID ) for MenuID, MenuData in pairs( Client._Menus ) do if MenuData.DeploySubMenus ~= nil then for SubMenuID, SubMenuData in pairs( MenuData.DeploySubMenus ) do - missionCommands.removeItemForGroup( Client:ClientGroup():getID(), SubMenuData ) + missionCommands.removeItemForGroup( ClientGroupID, SubMenuData ) trace.i( self.ClassName, "Removed DeploySubMenu " ) SubMenuData = nil end end if MenuData.DeployMenu then - missionCommands.removeItemForGroup( Client:ClientGroup():getID(), MenuData.DeployMenu ) + missionCommands.removeItemForGroup( ClientGroupID, MenuData.DeployMenu ) trace.i( self.ClassName, "Removed DeployMenu " ) MenuData.DeployMenu = nil end diff --git a/Moose/DestroyBaseTask.lua b/Moose/DestroyBaseTask.lua index 997e959a1..9e0555a21 100644 --- a/Moose/DestroyBaseTask.lua +++ b/Moose/DestroyBaseTask.lua @@ -20,51 +20,52 @@ DESTROYBASETASK = { -- @tparam ?number DestroyPercentage defines the %-tage that needs to be destroyed to achieve mission success. eg. If in the Group there are 10 units, then a value of 75 would require 8 units to be destroyed from the Group to complete the @{TASK}. -- @treturn DESTROYBASETASK function DESTROYBASETASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupPrefixes, DestroyPercentage ) -trace.f(self.ClassName) - - -- Inheritance - local Child = BASE:Inherit( self, TASK:New() ) + local self = BASE:Inherit( self, TASK:New() ) + self:T() - Child.Name = 'Destroy' - Child.Destroyed = 0 - Child.DestroyGroupPrefixes = DestroyGroupPrefixes - Child.DestroyGroupType = DestroyGroupType - Child.DestroyUnitType = DestroyUnitType - Child.TaskBriefing = "Task: Destroy " .. DestroyGroupType .. "." - Child.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEGROUPSDESTROYED:New(), STAGEDONE:New() } - Child.SetStage( Child, 1 ) + self.Name = 'Destroy' + self.Destroyed = 0 + self.DestroyGroupPrefixes = DestroyGroupPrefixes + self.DestroyGroupType = DestroyGroupType + self.DestroyUnitType = DestroyUnitType + self.TaskBriefing = "Task: Destroy " .. DestroyGroupType .. "." + self.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEGROUPSDESTROYED:New(), STAGEDONE:New() } + self.SetStage( self, 1 ) - --Child.AddEvent( Child, world.event.S_EVENT_DEAD, Child.EventDead ) + --self.AddEvent( self, world.event.S_EVENT_DEAD, self.EventDead ) - --env.info( 'New Table Child = ' .. tostring(Child) ) + --env.info( 'New Table self = ' .. tostring(self) ) --env.info( 'New Table self = ' .. tostring(self) ) - return Child + return self end --- Handle the S_EVENT_DEAD events to validate the destruction of units for the task monitoring. -- @param event Event structure of DCS world. function DESTROYBASETASK:EventDead( event ) -trace.f( self.ClassName, { 'EventDead', event } ) + self:T( { 'EventDead', event } ) - if event.initiator then - local DestroyGroup = Unit.getGroup( event.initiator ) - local DestroyGroupName = DestroyGroup:getName() + if event.initiator and Object.getCategory(event.initiator) == Object.Category.UNIT then local DestroyUnit = event.initiator local DestroyUnitName = DestroyUnit:getName() + local DestroyGroup = Unit.getGroup( DestroyUnit ) + local DestroyGroupName = "" + if DestroyGroup and DestroyGroup:isExist() then + local DestroyGroupName = DestroyGroup:getName() + end local UnitsDestroyed = 0 - trace.i( self.ClassName, DestroyGroupName ) - trace.i( self.ClassName, DestroyUnitName ) + self:T( DestroyGroupName ) + self:T( DestroyUnitName ) for DestroyGroupPrefixID, DestroyGroupPrefix in pairs( self.DestroyGroupPrefixes ) do - trace.i( self.ClassName, DestroyGroupPrefix ) + self:T( DestroyGroupPrefix ) if string.find( DestroyGroupName, DestroyGroupPrefix, 1, true ) then - trace.i( self.ClassName, BASE:Inherited(self).ClassName ) + self:T( BASE:Inherited(self).ClassName ) UnitsDestroyed = self:ReportGoalProgress( DestroyGroup, DestroyUnit ) - trace.i( self.ClassName, UnitsDestroyed ) + self:T( UnitsDestroyed ) end end - trace.i( self.ClassName, { UnitsDestroyed } ) + self:T( { UnitsDestroyed } ) self:IncreaseGoalCount( UnitsDestroyed, self.GoalVerb ) end end @@ -73,7 +74,7 @@ end -- @param DestroyGroup Group structure describing the group to be evaluated. -- @param DestroyUnit Unit structure describing the Unit to be evaluated. function DESTROYBASETASK:ReportGoalProgress( DestroyGroup, DestroyUnit ) -trace.f(self.ClassName) +self:T() return 0 end diff --git a/Moose/Group.lua b/Moose/Group.lua new file mode 100644 index 000000000..48971a3ba --- /dev/null +++ b/Moose/Group.lua @@ -0,0 +1,177 @@ +--- GROUP Classes +-- @classmod GROUP + +Include.File( "Routines" ) +Include.File( "Base" ) +Include.File( "Message" ) +Include.File( "Unit" ) + +GROUPS = {} + + +GROUP = { + ClassName="GROUP", + } + +function GROUP:New( _Group ) + local self = BASE:Inherit( self, BASE:New() ) + self:T( _Group:getName() ) + + self._Group = _Group + self.GroupName = _Group:getName() + self.GroupID = _Group:getID() + + return self +end + + +function GROUP:NewFromName( GroupName ) + local self = BASE:Inherit( self, BASE:New() ) + self:T( GroupName ) + + self._Group = Group.getByName( GroupName ) + self.GroupName = self._Group:getName() + self.GroupID = self._Group:getID() + + return self +end + + +function GROUP:GetName() + self:T( self.GroupName ) + + return self.GroupName +end + + +function GROUP:Destroy() + self:T( self.GroupName ) + + for Index, UnitData in pairs( self._Group:getUnits() ) do + self:CreateEventCrash( timer.getTime(), UnitData ) + end + + self._Group:destroy() +end + + +function GROUP:GetUnit( UnitNumber ) + self:T( self.GroupName ) + return UNIT:New( self._Group:getUnit( UnitNumber ) ) +end + + +function GROUP:IsAir() +self:T() + + local IsAirResult = self._Group:getCategory() == Group.Category.AIRPLANE or self._Group:getCategory() == Group.Category.HELICOPTER + + self:T( IsAirResult ) + return IsAirResult +end + + +function GROUP:AllOnGround() +self:T() + + local AllOnGroundResult = true + + for Index, UnitData in pairs( self._Group:getUnits() ) do + if UnitData:inAir() then + AllOnGroundResult = false + end + end + + self:T( AllOnGroundResult ) + return AllOnGroundResult +end + + +function GROUP:GetMaxVelocity() +self:T() + + local MaxVelocity = 0 + + for Index, UnitData in pairs( self._Group:getUnits() ) do + + local Velocity = UnitData:getVelocity() + local VelocityTotal = math.abs( Velocity.x ) + math.abs( Velocity.y ) + math.abs( Velocity.z ) + + if VelocityTotal < MaxVelocity then + MaxVelocity = VelocityTotal + end + end + + return MaxVelocity +end + + +function GROUP:GetHeight() +self:T() + + +end + + +function GROUP:Land( Point, Duration ) +trace.f( self.ClassName, { self.GroupName, Point, Duration } ) + + local Controller = self:_GetController() + + if Duration and Duration > 0 then + Controller:pushTask( { id = 'Land', params = { point = Point, durationFlag = true, duration = Duration } } ) + else + Controller:pushTask( { id = 'Land', params = { point = Point, durationFlag = false } } ) + end + + return self +end + + +function GROUP:Embarking( Point, Duration, EmbarkingGroup ) +trace.f( self.ClassName, { self.GroupName, Point, Duration, EmbarkingGroup._Group } ) + + local Controller = self:_GetController() + + trace.i( self.ClassName, EmbarkingGroup.GroupID ) + trace.i( self.ClassName, EmbarkingGroup._Group:getID() ) + trace.i( self.ClassName, EmbarkingGroup._Group.id ) + + Controller:pushTask( { id = 'Embarking', + params = { x = Point.x, + y = Point.y, + duration = Duration, + groupsForEmbarking = { EmbarkingGroup.GroupID }, + durationFlag = true, + distributionFlag = false, + distribution = {}, + } + } + ) + + return self +end + + +function GROUP:EmbarkToTransport( Point, Radius ) +trace.f( self.ClassName, { self.GroupName, Point, Radius } ) + + local Controller = self:_GetController() + + Controller:pushTask( { id = 'EmbarkToTransport', + params = { x = Point.x, + y = Point.y, + zoneRadius = Radius, + } + } + ) + + return self +end + + +function GROUP:_GetController() + + return self._Group:getController() + +end diff --git a/Moose/Message.lua b/Moose/Message.lua index f10c54fdd..6ca4b906a 100644 --- a/Moose/Message.lua +++ b/Moose/Message.lua @@ -74,16 +74,52 @@ end function MESSAGE:ToClient( Client ) trace.f(self.ClassName ) - if Client and Client:ClientGroup() then + if Client and Client:GetClientGroupID() then - local ClientGroup = Client:ClientGroup() + local ClientGroupID = Client:GetClientGroupID() trace.i( self.ClassName, self.MessageCategory .. '\n' .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForGroup( ClientGroup:getID(), self.MessageCategory .. '\n' .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) + trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. '\n' .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) end return self end +--- Sends a MESSAGE to the Blue coalition. +-- @treturn MESSAGE +-- @usage +-- -- Send a message created with the @{New} method to the BLUE coalition. +-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue() +-- or +-- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue() +-- or +-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) +-- MessageBLUE:ToBlue() +function MESSAGE:ToBlue() +trace.f(self.ClassName ) + + self:ToCoalition( coalition.side.BLUE ) + + return self +end + +--- Sends a MESSAGE to the Red Coalition. +-- @treturn MESSAGE +-- @usage +-- -- Send a message created with the @{New} method to the RED coalition. +-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() +-- or +-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() +-- or +-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) +-- MessageRED:ToRed() +function MESSAGE:ToRed( ) +trace.f(self.ClassName ) + + self:ToCoalition( coalition.side.RED ) + + return self +end + --- Sends a MESSAGE to a Coalition. -- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. -- @treturn MESSAGE diff --git a/Moose/Mission.lua b/Moose/Mission.lua index 2ead4c912..3d699212f 100644 --- a/Moose/Mission.lua +++ b/Moose/Mission.lua @@ -4,7 +4,6 @@ Include.File( "Routines" ) Include.File( "Base" ) -Include.File( "Mission" ) Include.File( "Client" ) Include.File( "Task" ) @@ -16,7 +15,6 @@ MISSION = { _Clients = {}, _Tasks = {}, _ActiveTasks = {}, - _Cargos = {}, GoalFunction = nil, MissionReportTrigger = 0, MissionProgressTrigger = 0, @@ -30,23 +28,13 @@ MISSION = { _GoalTasks = {} } -CARGOSTATUS = { - NONE = 0, - LOADED = 1, - UNLOADED = 2, - LOADING = 3, - LoadCount= 0, - UnloadCount = 0 -} - function MISSION:Meta() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, BASE:New() ) -trace.r( self.ClassName, "", { Child } ) - return Child + local self = BASE:Inherit( self, BASE:New() ) + self:T() + + return self end --- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. @@ -66,8 +54,9 @@ end -- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Sling Load', 'Operational', 'Fly to the cargo pickup zone at Dzegvi or Kaspi, and sling the cargo to Soganlug airbase.', 'NATO' ) -- local Mission = MISSIONSCHEDULER.AddMission( 'Rescue secret agent', 'Tactical', 'In order to be in full control of the situation, we need you to rescue a secret agent from the woods behind enemy lines. Avoid the Russian defenses and rescue the agent. Keep south until Khasuri, and keep your eyes open for any SAM presence. The agent is located at waypoint 4 on your kneeboard.', 'NATO' ) function MISSION:New( MissionName, MissionPriority, MissionBriefing, MissionCoalition ) -trace.f(self.ClassName, { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) + self = MISSION:Meta() + self:T({ MissionName, MissionPriority, MissionBriefing, MissionCoalition }) local Valid = true @@ -83,20 +72,19 @@ trace.f(self.ClassName, { MissionName, MissionPriority, MissionBriefing, Mission self.MissionCoalition = MissionCoalition end -trace.r( self.ClassName, "" ) return self end --- Returns if a Mission has completed. -- @treturn bool function MISSION:IsCompleted() -trace.f(self.ClassName) + self:T() return self.MissionStatus == "ACCOMPLISHED" end --- Set a Mission to completed. function MISSION:Completed() -trace.f(self.ClassName) + self:T() self.MissionStatus = "ACCOMPLISHED" self:StatusToClients() end @@ -104,27 +92,27 @@ end --- Returns if a Mission is ongoing. -- treturn bool function MISSION:IsOngoing() -trace.f(self.ClassName) + self:T() return self.MissionStatus == "ONGOING" end --- Set a Mission to ongoing. function MISSION:Ongoing() -trace.f(self.ClassName) + self:T() self.MissionStatus = "ONGOING" - self:StatusToClients() + --self:StatusToClients() end --- Returns if a Mission is pending. -- treturn bool function MISSION:IsPending() -trace.f(self.ClassName) + self:T() return self.MissionStatus == "PENDING" end --- Set a Mission to pending. function MISSION:Pending() -trace.f(self.ClassName) + self:T() self.MissionStatus = "PENDING" self:StatusToClients() end @@ -132,31 +120,31 @@ end --- Returns if a Mission has failed. -- treturn bool function MISSION:IsFailed() -trace.f(self.ClassName) + self:T() return self.MissionStatus == "FAILED" end --- Set a Mission to failed. function MISSION:Failed() -trace.f(self.ClassName) + self:T() self.MissionStatus = "FAILED" self:StatusToClients() end --- Send the status of the MISSION to all Clients. function MISSION:StatusToClients() -trace.f(self.ClassName) - if timer.getTime() >= self.MissionReportTrigger then + self:T() + if self.MissionReportFlash then for ClientID, Client in pairs( self._Clients ) do Client:Message( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. '! ( ' .. self.MissionPriority .. ' mission ) ', 10, self.Name .. '/Status', "Mission Command: Mission Status") end end -trace.e() end --- Handles the reporting. After certain time intervals, a MISSION report MESSAGE will be shown to All Players. function MISSION:ReportTrigger() -trace.f(self.ClassName) + self:T() + if self.MissionReportShow == true then self.MissionReportShow = false trace.r( "MISSION", "1", { true } ) @@ -181,16 +169,17 @@ end --- Report the status of all MISSIONs to all active Clients. function MISSION:ReportToAll() -trace.f(self.ClassName) + self:T() + local AlivePlayers = '' for ClientID, Client in pairs( self._Clients ) do if Client:ClientGroup() then - if Client:ClientGroup():getUnit(1) then - if Client:ClientGroup():getUnit(1):getLife() > 0.0 then + if Client:GetClientGroupUnit() then + if Client:GetClientGroupUnit():getLife() > 0.0 then if AlivePlayers == '' then - AlivePlayers = ' Players: ' .. Client:ClientGroup():getUnit(1):getPlayerName() + AlivePlayers = ' Players: ' .. Client:GetClientGroupUnit():getPlayerName() else - AlivePlayers = AlivePlayers .. ' / ' .. Client:ClientGroup():getUnit(1):getPlayerName() + AlivePlayers = AlivePlayers .. ' / ' .. Client:GetClientGroupUnit():getPlayerName() end end end @@ -202,7 +191,6 @@ trace.f(self.ClassName) TaskText = TaskText .. " - Task " .. TaskID .. ": " .. TaskData.Name .. ": " .. TaskData:GetGoalProgress() .. "\n" end MESSAGE:New( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. ' ( ' .. self.MissionPriority .. ' mission )' .. AlivePlayers .. "\n" .. TaskText:gsub("\n$",""), "Mission Command: Mission Report", 10, self.Name .. '/Status'):ToAll() -trace.e() end @@ -243,27 +231,26 @@ end -- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Transport Troops', 'Operational', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.', 'NATO' ) -- Mission:AddGoalFunction( DeployPatriotTroopsGoal ) function MISSION:AddGoalFunction( GoalFunction ) -trace.f(self.ClassName) + self:T() self.GoalFunction = GoalFunction -trace.e() end --- Show the briefing of the MISSION to the CLIENT. -- @tparam CLIENT Client to show briefing to. -- @treturn CLIENT function MISSION:ShowBriefing( Client ) -trace.f(self.ClassName, { Client } ) + self:T( { Client.ClientName } ) if not Client.ClientBriefingShown then Client.ClientBriefingShown = true - Client:Message( '(Press the keys [LEFT ALT]+[B] to view the briefing pages. Browse through the graphical briefing.)\n' .. - self.MissionBriefing, 40, self.Name .. '/MissionBriefing', "Mission Command: Mission Briefing" ) + local Briefing = self.MissionBriefing if Client.ClientBriefing then - Client:Message( Client.ClientBriefing, 40, self.Name .. '/ClientBriefing', "Mission Command: Mission Briefing" ) + Briefing = Briefing .. "\n" .. Client.ClientBriefing end + Briefing = Briefing .. "\n (Press [LEFT ALT]+[B] to view the graphical documentation.)" + Client:Message( Briefing, 30, self.Name .. '/MissionBriefing', "Command: Mission Briefing" ) end -trace.r( "", "", { Client } ) return Client end @@ -277,7 +264,7 @@ end -- Mission:AddClient( CLIENT:New( 'US UH-1H*HOT-Deploy Troops 2', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) -- Mission:AddClient( CLIENT:New( 'US UH-1H*RAMP-Deploy Troops 4', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() ) function MISSION:AddClient( Client ) -trace.f(self.ClassName, { Client } ) + self:T( { Client } ) local Valid = true @@ -296,8 +283,7 @@ end -- -- Seach for Client "Bomber" within the Mission. -- local BomberClient = Mission:FindClient( "Bomber" ) function MISSION:FindClient( ClientName ) -trace.f(self.ClassName) -trace.r( "", "", { self._Clients[ClientName] } ) + self:T( { self._Clients[ClientName] } ) return self._Clients[ClientName] end @@ -328,13 +314,12 @@ end -- Mission:AddTask( DeployTask, 2 ) function MISSION:AddTask( Task, TaskNumber ) -trace.f(self.ClassName) + self:T() self._Tasks[TaskNumber] = Task self._Tasks[TaskNumber]:EnableEvents() self._Tasks[TaskNumber].ID = TaskNumber -trace.r( self.ClassName, "" ) return Task end @@ -346,7 +331,7 @@ trace.r( self.ClassName, "" ) -- Task2 = Mission:GetTask( 2 ) function MISSION:GetTask( TaskNumber ) -trace.f(self.ClassName) + self:T() local Valid = true @@ -370,51 +355,11 @@ end -- Tasks = Mission:GetTasks() -- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) function MISSION:GetTasks() -trace.f(self.ClassName) + self:T() return self._Tasks end ---- Add Cargo to the mission... Cargo functionality needs to be reworked a bit, so this is still under construction. I need to make a CARGO Class... -SpawnCargo = {} -function MISSION:AddCargo( CargoName, CargoType, CargoWeight, CargoGroupControlCenter, CargoGroupTemplate, CargoZone ) -trace.f(self.ClassName, { CargoName, CargoType, CargoWeight, CargoGroupControlCenter, CargoGroupTemplate, CargoZone } ) - - local Cargo = {} - Cargo.CargoName = CargoName - if CargoType.TRANSPORT == CARGO_TRANSPORT.UNIT then - if not SpawnCargo[CargoGroupTemplate] then - SpawnCargo[CargoGroupTemplate] = SPAWN:New( CargoGroupTemplate ) - end - if CargoGroupControlCenter == nil then - --- @todo check this - Cargo.CargoGroupName = SpawnCargo[CargoGroupTemplate]:InZone( CargoZone ).name - else - --- @todo check this - env.info( "SpawnFromCarrier") - Cargo.CargoGroupName = SpawnCargo[CargoGroupTemplate]:FromCarrier( Group.getByName( CargoGroupControlCenter ), CargoZone, nil, true ).name - --trigger.action.activateGroup( Group.getByName( Cargo.CargoGroupName ) ) - --trigger.action.setGroupAIOff( Cargo.CargoGroupName ) - trace.i( self.ClassName, Cargo.CargoGroupName ) - - end - else - Cargo.CargoGroupName = CargoGroupControlCenter - end - Cargo.CargoType = CargoType - Cargo.CargoWeight = CargoWeight - Cargo.CargoGroupControlCenter = CargoGroupControlCenter - Cargo.CargoGroupTemplate = CargoGroupTemplate - Cargo.CargoZone = CargoZone - Cargo.Status = CARGOSTATUS.NONE - self._Cargos[CargoName] = Cargo - -trace.r( self.ClassName, "AddCargo", { Cargo.CargoGroupName } ) - return Cargo.CargoGroupName -end - - - --[[ _TransportExecuteStage: Defines the different stages of Transport unload/load execution. This table is internal and is used to control the validity of Transport load/unload timing. @@ -450,6 +395,8 @@ trace.scheduled("MISSIONSCHEDULER","Scheduler") -- loop through the missions in the TransportTasks for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do + + trace.i( "MISSIONSCHEDULER", MissionName ) if not Mission:IsCompleted() then @@ -457,8 +404,10 @@ trace.scheduled("MISSIONSCHEDULER","Scheduler") local ClientsAlive = false for ClientID, Client in pairs( Mission._Clients ) do + + trace.i( "MISSIONSCHEDULER", "Client: " .. Client.ClientName ) - if Client:ClientGroup() and Client:ClientGroup():getUnits() and Client:ClientGroup():getUnits()[1] and Client:ClientGroup():getUnits()[1]:getLife() > 0.0 then + if Client:ClientGroup() then -- There is at least one Client that is alive... So the Mission status is set to Ongoing. ClientsAlive = true @@ -476,13 +425,10 @@ trace.scheduled("MISSIONSCHEDULER","Scheduler") Client._Tasks[TaskNumber] = routines.utils.deepCopy( Mission._Tasks[TaskNumber] ) -- Each MissionTask must point to the original Mission. Client._Tasks[TaskNumber].MissionTask = Mission._Tasks[TaskNumber] + Client._Tasks[TaskNumber].Cargos = Mission._Tasks[TaskNumber].Cargos + Client._Tasks[TaskNumber].LandingZones = Mission._Tasks[TaskNumber].LandingZones end - Client._Cargos = {} - if Client.InitCargoNames then - for InitCargoID, InitCargoName in pairs( Client.InitCargoNames ) do - Client._Cargos[InitCargoName] = Mission._Cargos[InitCargoName] - end - end + Mission:Ongoing() end @@ -524,6 +470,7 @@ trace.scheduled("MISSIONSCHEDULER","Scheduler") end if Task:IsDone() then + trace.i( "MISSIONSCHEDULER", "Task " .. Task.Name .. " is Done." ) --env.info( 'Scheduler: Mission '.. Mission.Name .. ' Task ' .. Task.Name .. ' Stage ' .. Task.Stage.Name .. ' done. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) ) TaskComplete = true -- when a task is not yet completed, a mission cannot be completed @@ -539,7 +486,7 @@ trace.scheduled("MISSIONSCHEDULER","Scheduler") if Mission.GoalFunction ~= nil then Mission.GoalFunction( Mission, Client ) end - _Database:_AddMissionTaskScore( Client:ClientGroup():getUnit(1), Mission.Name, 25 ) + _Database:_AddMissionTaskScore( Client:GetClientGroupUnit(), Mission.Name, 25 ) -- if not Mission:IsCompleted() then -- end @@ -580,9 +527,6 @@ trace.scheduled("MISSIONSCHEDULER","Scheduler") -- So first sanitize Client._Tasks[TaskNumber].MissionTask, after that, sanitize only the whole _Tasks structure... --Client._Tasks[TaskNumber].MissionTask = nil --Client._Tasks = nil - - -- Sanitize the Client._Cargos. Any cargo within the Client will be lost when the client crashes. This is an important statement. - Client._Cargos = nil end end end diff --git a/Moose/Movement.lua b/Moose/Movement.lua index 69f6d1971..3ef4e4638 100644 --- a/Moose/Movement.lua +++ b/Moose/Movement.lua @@ -20,61 +20,61 @@ MOVEMENT = { -- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 ) function MOVEMENT:New( MovePrefixes, MoveMaximum ) -trace.f(self.ClassName, { MovePrefixes, MoveMaximum } ) - - -- Inherits from BASE - local Child = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) + self:T( { MovePrefixes, MoveMaximum } ) if type( MovePrefixes ) == 'table' then - Child.MovePrefixes = MovePrefixes + self.MovePrefixes = MovePrefixes else - Child.MovePrefixes = { MovePrefixes } + self.MovePrefixes = { MovePrefixes } end - Child.MoveCount = 0 -- The internal counter of the amount of Moveing the has happened since MoveStart. - Child.MoveMaximum = MoveMaximum -- Contains the Maximum amount of units that are allowed to move... - Child.AliveUnits = 0 -- Contains the counter how many units are currently alive - Child.MoveGroups = {} -- Reflects if the Moveing for this MovePrefixes is going to be scheduled or not. + self.MoveCount = 0 -- The internal counter of the amount of Moveing the has happened since MoveStart. + self.MoveMaximum = MoveMaximum -- Contains the Maximum amount of units that are allowed to move... + self.AliveUnits = 0 -- Contains the counter how many units are currently alive + self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not. - Child.AddEvent( Child, world.event.S_EVENT_BIRTH, Child.OnBirth ) - Child.AddEvent( Child, world.event.S_EVENT_DEAD, Child.OnDeadOrCrash ) - Child.AddEvent( Child, world.event.S_EVENT_CRASH, Child.OnDeadOrCrash ) + self.AddEvent( self, world.event.S_EVENT_BIRTH, self.OnBirth ) + self.AddEvent( self, world.event.S_EVENT_DEAD, self.OnDeadOrCrash ) + self.AddEvent( self, world.event.S_EVENT_CRASH, self.OnDeadOrCrash ) - Child.EnableEvents( Child ) + self.EnableEvents( self ) - Child.ScheduleStart( Child ) + self.ScheduleStart( self ) - return Child + return self end --- Call this function to start the MOVEMENT scheduling. function MOVEMENT:ScheduleStart() -trace.f( self.ClassName ) +self:T() self.MoveFunction = routines.scheduleFunction( self._Scheduler, { self }, timer.getTime() + 1, 120 ) end --- Call this function to stop the MOVEMENT scheduling. -- @todo need to implement it ... Forgot. function MOVEMENT:ScheduleStop() -trace.f( self.ClassName ) +self:T() end --- Captures the birth events when new Units were spawned. -- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. function MOVEMENT:OnBirth( event ) -trace.f( self.ClassName, { event } ) +self:T( { event } ) if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - if event.initiator and event.initiator:getName() then - trace.l(self.ClassName, "OnBirth", "Birth object : " .. event.initiator:getName() ) - local GroupData = Unit.getGroup(event.initiator) - if GroupData and GroupData:isExist() then - local EventGroupName = GroupData:getName() + if event.initiator and Object.getCategory(event.initiator) == Object.Category.UNIT then + local MovementUnit = event.initiator + local MovementUnitName = MovementUnit:getName() + self:T( "Birth object : " .. MovementUnitName ) + local MovementGroup = MovementUnit:getGroup() + if MovementGroup and MovementGroup:isExist() then + local MovementGroupName = MovementGroup:getName() for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( EventGroupName, MovePrefix, 1, true ) then + if string.find( MovementUnitName, MovePrefix, 1, true ) then self.AliveUnits = self.AliveUnits + 1 - self.MoveGroups[EventGroupName] = EventGroupName - trace.l(self.ClassName, "OnBirth", self.AliveUnits ) + self.MoveUnits[MovementUnitName] = MovementGroupName + self:T( self.AliveUnits ) end end end @@ -86,16 +86,17 @@ end --- Captures the Dead or Crash events when Units crash or are destroyed. -- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. function MOVEMENT:OnDeadOrCrash( event ) -trace.f( self.ClassName, { event } ) +self:T( { event } ) - if event.initiator and event.initiator:getName() then - trace.l( self.ClassName, "OnDeadOrCrash", "Dead object : " .. event.initiator:getName() ) - local EventGroupName = Unit.getGroup(event.initiator):getName() + if event.initiator and Object.getCategory(event.initiator) == Object.Category.UNIT then + local MovementUnit = event.initiator + local MovementUnitName = MovementUnit:getName() + self:T( "Dead object : " .. MovementUnitName ) for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( EventGroupName, MovePrefix, 1, true ) then + if string.find( MovementUnitName, MovePrefix, 1, true ) then self.AliveUnits = self.AliveUnits - 1 - self.MoveGroups[EventGroupName] = nil - trace.l( self.ClassName, "OnDeadOrCrash", self.AliveUnits ) + self.MoveUnits[MovementUnitName] = nil + self:T( self.AliveUnits ) end end end @@ -103,24 +104,26 @@ end --- This function is called automatically by the MOVEMENT scheduler. A new function is scheduled when MoveScheduled is true. function MOVEMENT:_Scheduler() -trace.l( self.ClassName, '_Scheduler', { self.MovePrefixes, self.MoveMaximum, self.AliveUnits, self.MoveGroups } ) +self:T( { self.MovePrefixes, self.MoveMaximum, self.AliveUnits, self.MovementGroups } ) if self.AliveUnits > 0 then local MoveProbability = ( self.MoveMaximum * 100 ) / self.AliveUnits - trace.l( self.ClassName, '_Scheduler', 'Move Probability = ' .. MoveProbability ) + self:T( 'Move Probability = ' .. MoveProbability ) - for MoveGroupID, MoveGroupName in pairs( self.MoveGroups ) do - local MoveGroup = Group.getByName( MoveGroupName ) - if MoveGroup then + for MovementUnitName, MovementGroupName in pairs( self.MoveUnits ) do + local MovementGroup = Group.getByName( MovementGroupName ) + if MovementGroup and MovementGroup:isExist() then local MoveOrStop = math.random( 1, 100 ) - trace.l( self.ClassName, '_Scheduler', 'MoveOrStop = ' .. MoveOrStop ) + self:T( 'MoveOrStop = ' .. MoveOrStop ) if MoveOrStop <= MoveProbability then - trace.l( self.ClassName, '_Scheduler', 'Group continues moving = ' .. MoveGroupName ) - trigger.action.groupContinueMoving( MoveGroup ) + self:T( 'Group continues moving = ' .. MovementGroupName ) + trigger.action.groupContinueMoving( MovementGroup ) else - trace.l( self.ClassName, '_Scheduler', 'Group stops moving = ' .. MoveGroupName ) - trigger.action.groupStopMoving( MoveGroup ) + self:T( 'Group stops moving = ' .. MovementGroupName ) + trigger.action.groupStopMoving( MovementGroup ) end + else + self.MoveUnits[MovementUnitName] = nil end end end diff --git a/Moose/PickupTask.lua b/Moose/PickupTask.lua index 401a24f07..ad26b7c4d 100644 --- a/Moose/PickupTask.lua +++ b/Moose/PickupTask.lua @@ -3,6 +3,7 @@ -- @parent TASK Include.File("Task") +Include.File("Cargo") PICKUPTASK = { ClassName = "PICKUPTASK", @@ -14,87 +15,103 @@ PICKUPTASK = { -- @tparam table{string,...}|string LandingZones Table of Zone names where Cargo is to be loaded. -- @tparam CARGO_TYPE CargoType Type of the Cargo. The type must be of the following Enumeration:.. -- @tparam number OnBoardSide Reflects from which side the cargo Group will be on-boarded on the Carrier. -function PICKUPTASK:New( LandingZones, CargoType, OnBoardSide ) -trace.f(self.ClassName) +function PICKUPTASK:New( CargoType, OnBoardSide ) + local self = BASE:Inherit( self, TASK:New() ) + self:T() - -- Child holds the inherited instance of the PICKUPTASK Class to the BASE class. - local Child = BASE:Inherit( self, TASK:New() ) + -- self holds the inherited instance of the PICKUPTASK Class to the BASE class. local Valid = true - Valid = routines.ValidateZone( LandingZones, "LandingZones", Valid ) - Valid = routines.ValidateEnumeration( CargoType, "CargoType", CARGO_TYPE, Valid ) - Valid = routines.ValidateEnumeration( CargoType, "CargoType", CARGO_TYPE, Valid ) - --Valid = routines.ValidateEnumeration( OnBoardSide, "OnBoardSide", CLIENT.ONBOARDSIDE, Valid ) - if Valid then - Child.Name = 'Pickup Cargo' - Child.TaskBriefing = "Task: Fly to the indicated landing zones and pickup " .. CargoType.TEXT .. ". Your co-pilot will provide you with the directions (required flight angle in degrees) and the distance (in km) to the pickup zone." - if type( LandingZones ) == "table" then - Child.LandingZones = LandingZones - else - Child.LandingZones = { LandingZones } - end - Child.CargoType = CargoType - Child.GoalVerb = CargoType.TEXT .. " " .. Child.GoalVerb - Child.OnBoardSide = OnBoardSide - Child.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGELANDING:New(), STAGELANDED:New(), STAGELOAD:New(), STAGEDONE:New() } - Child.SetStage( Child, 1 ) - + self.Name = 'Pickup Cargo' + self.TaskBriefing = "Task: Fly to the indicated landing zones and pickup " .. CargoType .. ". Your co-pilot will provide you with the directions (required flight angle in degrees) and the distance (in km) to the pickup zone." + self.CargoType = CargoType + self.GoalVerb = CargoType .. " " .. self.GoalVerb + self.OnBoardSide = OnBoardSide + self.IsLandingRequired = false -- required to decide whether the client needs to land or not + self.IsSlingLoad = false -- Indicates whether the cargo is a sling load cargo + self.Stages = { STAGE_CARGO_INIT:New(), STAGE_CARGO_LOAD:New(), STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGELANDING:New(), STAGELANDED:New(), STAGELOAD:New(), STAGEDONE:New() } + self.SetStage( self, 1 ) end - return Child + return self +end + +function PICKUPTASK:FromZone( LandingZone ) +self:T() + + self.LandingZones.LandingZoneNames[LandingZone.CargoZoneName] = LandingZone.CargoZoneName + self.LandingZones.LandingZones[LandingZone.CargoZoneName] = LandingZone + + return self +end + +function PICKUPTASK:InitCargo( InitCargos ) +self:T( { InitCargos } ) + + if type( InitCargos ) == "table" then + self.Cargos.InitCargos = InitCargos + else + self.Cargos.InitCargos = { InitCargos } + end + + return self +end + +function PICKUPTASK:LoadCargo( LoadCargos ) +self:T( { LoadCargos } ) + + if type( LoadCargos ) == "table" then + self.Cargos.LoadCargos = LoadCargos + else + self.Cargos.LoadCargos = { LoadCargos } + end + + return self end function PICKUPTASK:AddCargoMenus( Client, Cargos, TransportRadius ) -trace.f(self.ClassName, { Client, Cargos, TransportRadius } ) +self:T() - for CargoID, CargoData in pairs( Cargos ) do + for CargoID, Cargo in pairs( Cargos ) do - if CargoData.Status ~= CARGOSTATUS.LOADED and CargoData.Status ~= CARGOSTATUS.LOADING then + self:T( { Cargo.ClassName, Cargo.CargoName, Cargo.CargoType, Cargo:IsStatusNone(), Cargo:IsStatusLoaded(), Cargo:IsStatusLoading(), Cargo:IsStatusUnLoaded() } ) - if Group.getByName( CargoData.CargoGroupName ) then + -- If the Cargo has no status, allow the menu option. + if Cargo:IsStatusNone() or ( Cargo:IsStatusLoading() and Client == Cargo:IsLoadingToClient() ) then + + local MenuAdd = false + if Cargo:IsNear( Client, self.CurrentCargoZone ) then + MenuAdd = true + end - if Group.getByName( CargoData.CargoGroupName ):getSize() >= 1 then - - if Client._Menus[CargoData.CargoType] == nil then - Client._Menus[CargoData.CargoType] = {} - end - - if not Client._Menus[CargoData.CargoType].PickupMenu then - Client._Menus[CargoData.CargoType].PickupMenu = missionCommands.addSubMenuForGroup( - Client:ClientGroup():getID(), - self.TEXT[1], - nil - ) - trace.i( self.ClassName, 'Added PickupMenu' .. self.TEXT[1] ) - end - - if Client._Menus[CargoData.CargoType].PickupSubMenus == nil then - Client._Menus[CargoData.CargoType].PickupSubMenus = {} - end - - local MenuAdd = false - if CargoData.CargoType.TRANSPORT == CARGO_TRANSPORT.UNIT then - CargoGroup = Group.getByName( CargoData.CargoGroupName ) - if routines.IsPartOfGroupInRadius( CargoGroup, Client:ClientGroup(), TransportRadius ) then - MenuAdd = true - end - else - MenuAdd = true - end - - if MenuAdd then - Client._Menus[CargoData.CargoType].PickupSubMenus[ #Client._Menus[CargoData.CargoType].PickupSubMenus + 1 ] = missionCommands.addCommandForGroup( - Client:ClientGroup():getID(), - CargoData.CargoName .. " ( " .. CargoData.CargoWeight .. "kg )", - Client._Menus[CargoData.CargoType].PickupMenu, - self.MenuAction, - { ReferenceTask = self, CargoName = CargoData.CargoName } - ) - trace.i( self.ClassName, 'Added PickupSubMenu' .. CargoData.CargoType.TEXT .. ":" .. CargoData.CargoName .. " ( " .. CargoData.CargoWeight .. "kg )" ) - end + if MenuAdd then + if Client._Menus[Cargo.CargoType] == nil then + Client._Menus[Cargo.CargoType] = {} end + + if not Client._Menus[Cargo.CargoType].PickupMenu then + Client._Menus[Cargo.CargoType].PickupMenu = missionCommands.addSubMenuForGroup( + Client:GetClientGroupID(), + self.TEXT[1] .. " " .. Cargo.CargoType, + nil + ) + self:T( 'Added PickupMenu: ' .. self.TEXT[1] .. " " .. Cargo.CargoType ) + end + + if Client._Menus[Cargo.CargoType].PickupSubMenus == nil then + Client._Menus[Cargo.CargoType].PickupSubMenus = {} + end + + Client._Menus[Cargo.CargoType].PickupSubMenus[ #Client._Menus[Cargo.CargoType].PickupSubMenus + 1 ] = missionCommands.addCommandForGroup( + Client:GetClientGroupID(), + Cargo.CargoName .. " ( " .. Cargo.CargoWeight .. "kg )", + Client._Menus[Cargo.CargoType].PickupMenu, + self.MenuAction, + { ReferenceTask = self, CargoTask = Cargo } + ) + self:T( 'Added PickupSubMenu' .. Cargo.CargoType .. ":" .. Cargo.CargoName .. " ( " .. Cargo.CargoWeight .. "kg )" ) end end end @@ -102,103 +119,36 @@ trace.f(self.ClassName, { Client, Cargos, TransportRadius } ) end function PICKUPTASK:RemoveCargoMenus( Client ) -trace.f(self.ClassName, { Client } ) +self:T() for MenuID, MenuData in pairs( Client._Menus ) do for SubMenuID, SubMenuData in pairs( MenuData.PickupSubMenus ) do - missionCommands.removeItemForGroup( Client:ClientGroup():getID(), SubMenuData ) - trace.i( self.ClassName, "Removed PickupSubMenu " ) + missionCommands.removeItemForGroup( Client:GetClientGroupID(), SubMenuData ) + self:T( "Removed PickupSubMenu " ) SubMenuData = nil end if MenuData.PickupMenu then - missionCommands.removeItemForGroup( Client:ClientGroup():getID(), MenuData.PickupMenu ) - trace.i( self.ClassName, "Removed PickupMenu " ) + missionCommands.removeItemForGroup( Client:GetClientGroupID(), MenuData.PickupMenu ) + self:T( "Removed PickupMenu " ) MenuData.PickupMenu = nil end end - + + for CargoID, Cargo in pairs( CARGOS ) do + self:T( { Cargo.ClassName, Cargo.CargoName, Cargo.CargoType, Cargo:IsStatusNone(), Cargo:IsStatusLoaded(), Cargo:IsStatusLoading(), Cargo:IsStatusUnLoaded() } ) + if Cargo:IsStatusLoading() and Client == Cargo:IsLoadingToClient() then + Cargo:StatusNone() + end + end + end function PICKUPTASK:HasFailed( ClientDead ) -trace.f(self.ClassName) +self:T() local TaskHasFailed = self.TaskFailed return TaskHasFailed end -function PICKUPTASK:OnBoardCargo( ClientGroup, Cargos ) -trace.f(self.ClassName, { ClientGroup, Cargos } ) - - local Valid = true - - Valid = routines.ValidateGroup( ClientGroup, "ClientGroup", Valid ) - - if Valid then - - local CarrierPos = ClientGroup:getUnits()[1]:getPoint() - local CarrierPosMove = ClientGroup:getUnits()[1]:getPoint() - local CarrierPosOnBoard = ClientGroup:getUnits()[1]:getPoint() - - local CargoGroup = Group.getByName( Cargos[ self.CargoName ].CargoGroupName ) - trigger.action.activateGroup( CargoGroup ) - trigger.action.setGroupAIOn( CargoGroup ) - - local CargoUnits = CargoGroup:getUnits() - local CargoPos = CargoUnits[1]:getPoint() - - - local Points = {} - - trace.i( self.ClassName, 'CargoPos x = ' .. CargoPos.x .. " z = " .. CargoPos.z ) - trace.i( self.ClassName, 'CarrierPosMove x = ' .. CarrierPosMove.x .. " z = " .. CarrierPosMove.z ) - - Points[#Points+1] = routines.ground.buildWP( CargoPos, "off road", 6 ) - - trace.i( self.ClassName, 'Points[1] x = ' .. Points[1].x .. " y = " .. Points[1].y ) - - if self.OnBoardSide == nil then - self.OnBoardSide = CLIENT.ONBOARDSIDE.NONE - end - - if self.OnBoardSide == CLIENT.ONBOARDSIDE.LEFT then - trace.i( self.ClassName, "TransportCargoOnBoard: Onboarding LEFT" ) - CarrierPosMove.z = CarrierPosMove.z - 50 - CarrierPosOnBoard.z = CarrierPosOnBoard.z - 5 - Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "diamond", 6 ) - Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "diamond", 6 ) - elseif self.OnBoardSide == CLIENT.ONBOARDSIDE.RIGHT then - trace.i( self.ClassName, "TransportCargoOnBoard: Onboarding RIGHT" ) - CarrierPosMove.z = CarrierPosMove.z + 50 - CarrierPosOnBoard.z = CarrierPosOnBoard.z + 5 - Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "diamond", 6 ) - Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "diamond", 6 ) - elseif self.OnBoardSide == CLIENT.ONBOARDSIDE.BACK then - trace.i( self.ClassName, "TransportCargoOnBoard: Onboarding BACK" ) - CarrierPosMove.x = CarrierPosMove.x - 50 - CarrierPosOnBoard.x = CarrierPosOnBoard.x - 5 - Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "diamond", 6 ) - Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "diamond", 6 ) - elseif self.OnBoardSide == CLIENT.ONBOARDSIDE.FRONT then - trace.i( self.ClassName, "TransportCargoOnBoard: Onboarding FRONT" ) - CarrierPosMove.x = CarrierPosMove.x + 50 - CarrierPosOnBoard.x = CarrierPosOnBoard.x + 5 - Points[#Points+1] = routines.ground.buildWP( CarrierPosMove, "diamond", 6 ) - Points[#Points+1] = routines.ground.buildWP( CarrierPosOnBoard, "diamond", 6 ) - elseif self.OnBoardSide == CLIENT.ONBOARDSIDE.NONE then - trace.i( self.ClassName, "TransportCargoOnBoard: Onboarding CENTRAL" ) - Points[#Points+1] = routines.ground.buildWP( CarrierPos, "diamond", 6 ) - end - trace.i( self.ClassName, "TransportCargoOnBoard: Routing " .. Cargos[ self.CargoName ].CargoGroupName ) - - trace.i( self.ClassName, 'Points[2] x = ' .. Points[2].x .. " y = " .. Points[2].y ) - trace.i( self.ClassName, 'Points[3] x = ' .. Points[3].x .. " y = " .. Points[3].y ) - - routines.scheduleFunction(routines.goRoute, {Cargos[ self.CargoName ].CargoGroupName, Points}, timer.getTime() + 8) - --routines.goRoute( Cargos[ self.CargoName ].CargoGroupName, Points ) - end - - return Valid - -end diff --git a/Moose/Routines.lua b/Moose/Routines.lua index ee1fb2e64..bdcc7924f 100644 --- a/Moose/Routines.lua +++ b/Moose/Routines.lua @@ -25,6 +25,107 @@ routines.build = 22 -- Utils- conversion, Lua utils, etc. routines.utils = {} +--from http://lua-users.org/wiki/CopyTable +routines.utils.deepCopy = function(object) + local lookup_table = {} + local function _copy(object) + if type(object) ~= "table" then + return object + elseif lookup_table[object] then + return lookup_table[object] + end + local new_table = {} + lookup_table[object] = new_table + for index, value in pairs(object) do + new_table[_copy(index)] = _copy(value) + end + return setmetatable(new_table, getmetatable(object)) + end + local objectreturn = _copy(object) + return objectreturn +end + + +-- porting in Slmod's serialize_slmod2 +routines.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function + + lookup_table = {} + + local function _Serialize( tbl ) + + if type(tbl) == 'table' then --function only works for tables! + + if lookup_table[tbl] then + return lookup_table[object] + end + + local tbl_str = {} + + lookup_table[tbl] = tbl_str + + tbl_str[#tbl_str + 1] = '{' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = ']=' + else --must be a string + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = routines.utils.basicSerialize(ind) + tbl_str[#tbl_str + 1] = ']=' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ',' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = routines.utils.basicSerialize(val) + tbl_str[#tbl_str + 1] = ',' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil,' + elseif type(val) == 'table' then + if ind == "__index" then + tbl_str[#tbl_str + 1] = "__index" + tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + else + + tbl_str[#tbl_str + 1] = _Serialize(val) + tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + end + elseif type(val) == 'function' then + tbl_str[#tbl_str + 1] = "function " .. tostring(ind) + tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + else + env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) + env.info( debug.traceback() ) + end + + end + tbl_str[#tbl_str + 1] = '}' + return table.concat(tbl_str) + else + return tostring(tbl) + end + end + + local objectreturn = _Serialize(tbl) + return objectreturn +end + +--porting in Slmod's "safestring" basic serialize +routines.utils.basicSerialize = function(s) + if s == nil then + return "\"\"" + else + if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then + return tostring(s) + elseif type(s) == 'string' then + s = string.format('%q', s) + return s + end + end +end routines.utils.toDegree = function(angle) @@ -137,91 +238,6 @@ function routines.utils.get3DDist(point1, point2) end ---from http://lua-users.org/wiki/CopyTable -routines.utils.deepCopy = function(object) - local lookup_table = {} - local function _copy(object) - if type(object) ~= "table" then - return object - elseif lookup_table[object] then - return lookup_table[object] - end - local new_table = {} - lookup_table[object] = new_table - for index, value in pairs(object) do - new_table[_copy(index)] = _copy(value) - end - return setmetatable(new_table, getmetatable(object)) - end - local objectreturn = _copy(object) - return objectreturn -end - - --- porting in Slmod's serialize_slmod2 -routines.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - if type(tbl) == 'table' then --function only works for tables! - - local tbl_str = {} - - tbl_str[#tbl_str + 1] = '{' - - for ind,val in pairs(tbl) do -- serialize its fields - if type(ind) == "number" then - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = tostring(ind) - tbl_str[#tbl_str + 1] = ']=' - else --must be a string - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = routines.utils.basicSerialize(ind) - tbl_str[#tbl_str + 1] = ']=' - end - - if ((type(val) == 'number') or (type(val) == 'boolean')) then - tbl_str[#tbl_str + 1] = tostring(val) - tbl_str[#tbl_str + 1] = ',' - elseif type(val) == 'string' then - tbl_str[#tbl_str + 1] = routines.utils.basicSerialize(val) - tbl_str[#tbl_str + 1] = ',' - elseif type(val) == 'nil' then -- won't ever happen, right? - tbl_str[#tbl_str + 1] = 'nil,' - elseif type(val) == 'table' then - if ind == "__index" then - tbl_str[#tbl_str + 1] = "__index" - tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else - tbl_str[#tbl_str + 1] = routines.utils.oneLineSerialize(val) - tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - end - elseif type(val) == 'function' then - tbl_str[#tbl_str + 1] = "function " .. tostring(ind) - tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else - env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) - env.info( debug.traceback() ) - end - - end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) - else - return tostring(tbl) - end -end - ---porting in Slmod's "safestring" basic serialize -routines.utils.basicSerialize = function(s) - if s == nil then - return "\"\"" - else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then - return tostring(s) - elseif type(s) == 'string' then - s = string.format('%q', s) - return s - end - end -end -- From http://lua-users.org/wiki/SimpleRound -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place @@ -280,6 +296,9 @@ routines.vec.rotateVec2 = function(vec2, theta) end --------------------------------------------------------------------------------------------------------------------------- + + + -- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. routines.tostringMGRS = function(MGRS, acc) if acc == 0 then @@ -1521,85 +1540,48 @@ trace.r( "", "", { TransportZoneResult } ) end -function routines.IsUnitInRadius( CargoUnit, ReferenceGroup, Radius ) +function routines.IsUnitInRadius( CargoUnit, ReferencePosition, Radius ) trace.f() local Valid = true -- fill-up some local variables to support further calculations to determine location of units within the zone. local CargoPos = CargoUnit:getPosition().p - local ReferenceGroupPos = ReferenceGroup:getUnits()[1]:getPosition().p + local ReferenceP = ReferencePosition.p - if (((CargoPos.x - ReferenceGroupPos.x)^2 + (CargoPos.z - ReferenceGroupPos.z)^2)^0.5 <= Radius) then + if (((CargoPos.x - ReferenceP.x)^2 + (CargoPos.z - ReferenceP.z)^2)^0.5 <= Radius) then else Valid = false end -trace.r( "", "", { Valid } ) return Valid end -function routines.IsPartOfGroupInRadius( CargoGroup, ReferenceGroup, Radius ) +function routines.IsPartOfGroupInRadius( CargoGroup, ReferencePosition, Radius ) trace.f() local Valid = true Valid = routines.ValidateGroup( CargoGroup, "CargoGroup", Valid ) - Valid = routines.ValidateGroup( ReferenceGroup, "ReferenceGroup", Valid ) -- fill-up some local variables to support further calculations to determine location of units within the zone local CargoUnits = CargoGroup:getUnits() for CargoUnitId, CargoUnit in pairs( CargoUnits ) do local CargoUnitPos = CargoUnit:getPosition().p -- env.info( 'routines.IsPartOfGroupInRadius: CargoUnitPos.x = ' .. CargoUnitPos.x .. ' CargoUnitPos.z = ' .. CargoUnitPos.z ) - local ReferenceGroupPos = ReferenceGroup:getUnits()[1]:getPosition().p + local ReferenceP = ReferencePosition.p -- env.info( 'routines.IsPartOfGroupInRadius: ReferenceGroupPos.x = ' .. ReferenceGroupPos.x .. ' ReferenceGroupPos.z = ' .. ReferenceGroupPos.z ) - if ((( CargoUnitPos.x - ReferenceGroupPos.x)^2 + (CargoUnitPos.z - ReferenceGroupPos.z)^2)^0.5 <= Radius) then + if ((( CargoUnitPos.x - ReferenceP.x)^2 + (CargoUnitPos.z - ReferenceP.z)^2)^0.5 <= Radius) then else Valid = false break end end -trace.r( "", "", { Valid } ) return Valid end -function routines.DestroyGroupInRadiusFromGroup( CargoGroup, ReferenceGroup, Radius ) -trace.f() - - local Valid = true - - Valid = routines.ValidateGroup( CargoGroup, "CargoGroup", Valid ) - Valid = routines.ValidateGroup( ReferenceGroup, "ReferenceGroup", Valid ) - - if Valid then - -- fill-up some local variables to support further calculations to determine location of units within the zone - local CargoUnits = CargoGroup:getUnits() - local AliveCargoUnits = #CargoUnits - for CargoUnitId, CargoUnit in pairs( CargoUnits ) do - local CargoUnitPos = CargoUnit:getPosition().p --- env.info( 'routines.DestroyGroupInRadiusFromGroup: CargoUnitPos.x = ' .. CargoUnitPos.x .. ' CargoUnitPos.z = ' .. CargoUnitPos.z ) - local ReferenceGroupPos = ReferenceGroup:getUnits()[1]:getPosition().p --- env.info( 'routines.DestroyGroupInRadiusFromGroup: ReferenceGroupPos.x = ' .. ReferenceGroupPos.x .. ' ReferenceGroupPos.z = ' .. ReferenceGroupPos.z ) - - if ((( CargoUnitPos.x - ReferenceGroupPos.x)^2 + (CargoUnitPos.z - ReferenceGroupPos.z)^2)^0.5 <= Radius) then - CargoUnit:destroy() - AliveCargoUnits = AliveCargoUnits - 1 - else - Valid = false - break - end - end - else - AliveCargoUnits = -1 - end - -trace.r( "", "", { AliveCargoUnits } ) - return AliveCargoUnits -end - function routines.ValidateString( Variable, VariableName, Valid ) trace.f() @@ -1866,6 +1848,23 @@ routines.ground.patrol = function(gpData, pType, form, speed) return end +function routines.GetUnitHeight( CheckUnit ) +trace.f( "routines" ) + + local UnitPoint = CheckUnit:getPoint() + local UnitPosition = { x = UnitPoint.x, y = UnitPoint.z } + local UnitHeight = UnitPoint.y + + local LandHeight = land.getHeight( UnitPosition ) + + --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) + + trace.f( "routines", "Unit Height = " .. UnitHeight - LandHeight ) + + return UnitHeight - LandHeight + +end + Su34Status = { status = {} } @@ -2399,7 +2398,7 @@ trace.f() --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - return CarrierHeight - LandHeight + return UnitHeight - LandHeight end diff --git a/Moose/Sead.lua b/Moose/Sead.lua index e9b2a31f3..e1891c039 100644 --- a/Moose/Sead.lua +++ b/Moose/Sead.lua @@ -30,51 +30,48 @@ SEAD = { -- -- Defends the Russian SA installations from SEAD attacks. -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) function SEAD:New( SEADGroupPrefixes ) -trace.f(self.ClassName, SEADGroupPrefixes ) - - -- Arrange meta tables - local Child = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) + self:T( SEADGroupPrefixes ) if type( SEADGroupPrefixes ) == 'table' then for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do - Child.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix + self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix end else - Child.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes + self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes end - Child.AddEvent( Child, world.event.S_EVENT_SHOT, Child.EventShot ) - Child.EnableEvents( Child ) + self.AddEvent( self, world.event.S_EVENT_SHOT, self.EventShot ) + self.EnableEvents( self ) - return Child + return self end --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. -- @see SEAD function SEAD:EventShot( event ) -trace.f( self.ClassName, { event } ) +self:T( { event } ) - local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired - local _groupname = _grp:getName() -- return the name of the group - local _unittable = {event.initiator:getName()} -- return the name of the units in the group - local _SEADmissile = event.weapon -- Identify the weapon fired - local _SEADmissileName = _SEADmissile:getTypeName() -- return weapon type - --trigger.action.outText( string.format("Alerte, depart missile " ..string.format(_SEADmissileName)), 20) --debug message + local SEADUnit = event.initiator + local SEADUnitName = SEADUnit:getName() + local SEADWeapon = event.weapon -- Identify the weapon fired + local SEADWeaponName = SEADWeapon:getTypeName() -- return weapon type + --trigger.action.outText( string.format("Alerte, depart missile " ..string.format(SEADWeaponName)), 20) --debug message -- Start of the 2nd loop - trace.i( self.ClassName, "Missile Launched = " .. _SEADmissileName ) - if _SEADmissileName == "KH-58" or _SEADmissileName == "KH-25MPU" or _SEADmissileName == "AGM-88" or _SEADmissileName == "KH-31A" or _SEADmissileName == "KH-31P" then -- Check if the missile is a SEAD + self:T( "Missile Launched = " .. SEADWeaponName ) + if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD local _evade = math.random (1,100) -- random number for chance of evading action - local _targetMim = Weapon.getTarget(_SEADmissile) -- Identify target + local _targetMim = Weapon.getTarget(SEADWeapon) -- Identify target local _targetMimname = Unit.getName(_targetMim) - local _targetMimgroup = Unit.getGroup(Weapon.getTarget(_SEADmissile)) + local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroupName = _targetMimgroup:getName() local _targetMimcont= _targetMimgroup:getController() local _targetskill = _Database.Units[_targetMimname].Template.skill - trace.i( self.ClassName, self.SEADGroupPrefixes ) - trace.i( self.ClassName, _targetMimgroupName ) + self:T( self.SEADGroupPrefixes ) + self:T( _targetMimgroupName ) local SEADGroupFound = false for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then SEADGroupFound = true - trace.i( self.ClassName, 'Group Found' ) + self:T( 'Group Found' ) break end end @@ -83,15 +80,15 @@ trace.f( self.ClassName, { event } ) local Skills = { "Average", "Good", "High", "Excellent" } _targetskill = Skills[ math.random(1,4) ] end - trace.i( self.ClassName, _targetskill ) -- debug message for skill check + self:T( _targetskill ) -- debug message for skill check if self.TargetSkill[_targetskill] then if (_evade > self.TargetSkill[_targetskill].Evade) then - trace.i( self.ClassName, string.format("Evading, target skill " ..string.format(_targetskill)) ) --debug message - local _targetMim = Weapon.getTarget(_SEADmissile) + self:T( string.format("Evading, target skill " ..string.format(_targetskill)) ) --debug message + local _targetMim = Weapon.getTarget(SEADWeapon) local _targetMimname = Unit.getName(_targetMim) - local _targetMimgroup = Unit.getGroup(Weapon.getTarget(_SEADmissile)) + local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimcont= _targetMimgroup:getController() - routines.groupRandomDistSelf(_targetMimgroup,300,'Rank',250,20) -- move randomly + routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly local SuppressedGroups1 = {} -- unit suppressed radar off for a random time local function SuppressionEnd1(id) id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) diff --git a/Moose/SlingLoadHookTask.lua b/Moose/SlingLoadHookTask.lua deleted file mode 100644 index 3248d4f95..000000000 --- a/Moose/SlingLoadHookTask.lua +++ /dev/null @@ -1,39 +0,0 @@ ---- A SLINGLOADHOOKTASK will orchestrate the sling-load hook activity to slingload a CARGO from a specific landing zone(s). --- @classmod SLINGLOADHOOKTASK - -Include.File("Task") - -SLINGLOADHOOKTASK = { - ClassName = "SLINGLOADHOOKTASK", - GoalVerb = "Hook and Sling Cargo" -} - ---- Creates a new SLINGLOADHOOKTASK. --- @tparam table{string,...}|string LandingZones Table or name of the zone(s) where Cargo is to be loaded. --- @tparam table{string,...)|string CargoPrefixes is the name or prefix of the name of the Cargo objects defined within the DCS ME. --- @treturn SLINGLOADHOOKTASK -function SLINGLOADHOOKTASK:New( LandingZones, CargoPrefixes ) -trace.f(self.ClassName) - - local self = BASE:Inherit( self, TASK:New() ) - - self.Name = 'Hook and Sling Cargo' - self.TaskBriefing = "Task: Hook" - - if type( LandingZones ) == "table" then - self.LandingZones = LandingZones - else - self.LandingZones = { LandingZones } - end - - if type( CargoPrefixes ) == "table" then - self.CargoPrefixes = CargoPrefixes - else - self.CargoPrefixes = { CargoPrefixes } - end - - self.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGE_SLINGLOAD_HOOK:New(), STAGEDONE:New() } - self:SetStage( 1 ) - - return self -end diff --git a/Moose/SlingLoadUnHookTask.lua b/Moose/SlingLoadUnHookTask.lua deleted file mode 100644 index 6ac3bd131..000000000 --- a/Moose/SlingLoadUnHookTask.lua +++ /dev/null @@ -1,38 +0,0 @@ ---- A SLINGLOADUNHOOKTASK will orchestrate the sling-load unhook activity to (sling)load a CARGO and deploy it in a specific landing zone(s). --- @classmod SLINGLOADUNHOOKTASK - -Include.File("Task") - -SLINGLOADUNHOOKTASK = { - ClassName = "SLINGLOADUNHOOKTASK", - GoalVerb = "Sling and UnHook Cargo" -} - ---- Creates a new SLINGLOADUNHOOKTASK. --- @tparam table{string,...}|string LandingZones Table or name of the zone(s) where Cargo is to be loaded. --- @tparam table{string,...}|string CargoPrefixes is the name or prefix of the name of the Cargo objects defined within the DCS ME. -function SLINGLOADUNHOOKTASK:New( LandingZones, CargoPrefixes ) -trace.f(self.ClassName) - - local self = BASE:Inherit( self, TASK:New() ) - - self.Name = 'Sling and Unhook Cargo' - self.TaskBriefing = "Task: UnHook" - - if type( LandingZones ) == "table" then - self.LandingZones = LandingZones - else - self.LandingZones = { LandingZones } - end - - if type( CargoPrefixes ) == "table" then - self.CargoPrefixes = CargoPrefixes - else - self.CargoPrefixes = { CargoPrefixes } - end - - self.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGE_SLINGLOAD_UNHOOK:New(), STAGEDONE:New() } - self:SetStage( 1 ) - - return self -end diff --git a/Moose/Spawn.lua b/Moose/Spawn.lua index 61acdd908..3885e0553 100644 --- a/Moose/Spawn.lua +++ b/Moose/Spawn.lua @@ -2,9 +2,13 @@ -- @classmod SPAWN -- @author Flightcontrol +MOOSE_Version = "0.1.1.1" + Include.File( "Routines" ) Include.File( "Base" ) Include.File( "Database" ) +Include.File( "Group" ) + SPAWN = { ClassName = "SPAWN", @@ -23,10 +27,8 @@ SPAWN = { -- -- NATO helicopters engaging in the battle field. -- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ) function SPAWN:New( SpawnPrefix ) -trace.f(self.ClassName, SpawnPrefix) - - -- Inherits from BASE local self = BASE:Inherit( self, BASE:New() ) + self:T( SpawnPrefix) local TemplateGroup = Group.getByName( SpawnPrefix ) if TemplateGroup then @@ -39,6 +41,7 @@ trace.f(self.ClassName, SpawnPrefix) self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. self.SpawnMaxGroupsAlive = 0 -- The maximum amount of groups that can be alive of SpawnPrefix at the same time. self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. + self.SpawnRandomize = false else error( "SPAWN:New: There is no group declared in the mission editor with SpawnPrefix = '" .. SpawnPrefix .. "'" ) end @@ -65,11 +68,12 @@ end -- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):RandomizeRoute( 2, 2, 2000 ) function SPAWN:RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius ) -trace.f( self.ClassName, { SpawnStartPoint, SpawnEndPoint, SpawnRadius } ) +self:T( { SpawnStartPoint, SpawnEndPoint, SpawnRadius } ) self.SpawnStartPoint = SpawnStartPoint -- When the spawning occurs, randomize the route points from SpawnStartPoint. self.SpawnEndPoint = SpawnEndPoint -- When the spawning occurs, randomize the route points till SpawnEndPoint. self.SpawnRadius = SpawnRadius -- The Radius of randomization of the route points from SpawnStartPoint till SpawnEndPoint. + self.SpawnRandomize = true return self end @@ -89,7 +93,7 @@ end -- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 ) function SPAWN:Schedule( SpawnTime, SpawnTimeVariation ) -trace.f( self.ClassName, { SpawnTime, SpawnTimeVariation } ) +self:T( { SpawnTime, SpawnTimeVariation } ) self.SpawnCurrentTimer = 0 -- The internal timer counter to trigger a scheduled spawning of SpawnPrefix. self.SpawnSetTimer = 0 -- The internal timer value when a scheduled spawning of SpawnPrefix occurs. @@ -103,7 +107,7 @@ trace.f( self.ClassName, { SpawnTime, SpawnTimeVariation } ) self:ScheduleStart() end - trace.i( self.ClassName, { self.SpawnLowTimer, self.SpawnHighTimer } ) + self:T( { self.SpawnLowTimer, self.SpawnHighTimer } ) return self end @@ -111,7 +115,7 @@ end --- Will start the SPAWNing timers. -- This function is called automatically when @{Schedule} is called. function SPAWN:ScheduleStart() -trace.f( self.ClassName ) +self:T() --local ClientUnit = #AlivePlayerUnits() @@ -128,7 +132,7 @@ end --- Will stop the scheduled SPAWNing activity. function SPAWN:ScheduleStop() -trace.f( self.ClassName ) +self:T() self.SpawnScheduled = false end @@ -146,7 +150,7 @@ end -- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Limit( 2, 24 ) function SPAWN:Limit( SpawnMaxGroupsAlive, SpawnMaxGroups ) -trace.f( self.ClassName, { SpawnMaxGroupsAlive, SpawnMaxGroups } ) +self:T( { SpawnMaxGroupsAlive, SpawnMaxGroups } ) self.SpawnMaxGroupsAlive = SpawnMaxGroupsAlive -- The maximum amount of groups that can be alive of SpawnPrefix at the same time. self.SpawnMaxGroups = SpawnMaxGroups -- The maximum amount of groups that can be spawned. @@ -173,7 +177,7 @@ end -- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):Limit( 12, 150 ):Schedule( 200, 0.4 ):RandomizeTemplate( Spawn_US_Platoon ):RandomizeRoute( 3, 3, 2000 ) function SPAWN:RandomizeTemplate( SpawnPrefixTable ) -trace.f( self.ClassName, { SpawnPrefix, SpawnPrefixTable } ) +self:T( { SpawnPrefix, SpawnPrefixTable } ) self.SpawnPrefixTable = SpawnPrefixTable @@ -190,7 +194,7 @@ end -- SpawnRU_SU34 = SPAWN:New( 'TF1 RU Su-34 Krymsk@AI - Attack Ships' ):Schedule( 2, 3, 1800, 0.4 ):SpawnUncontrolled():RandomizeRoute( 1, 1, 3000 ):RepeatOnEngineShutDown() function SPAWN:Repeat() -trace.f( self.ClassName ) +self:T() self.SpawnRepeat = true self.RepeatOnEngineShutDown = false @@ -209,7 +213,7 @@ end -- @see Repeat function SPAWN:RepeatOnLanding() -trace.f( self.ClassName ) +self:T() self:Repeat() self.RepeatOnEngineShutDown = false @@ -223,7 +227,7 @@ end -- @see Repeat function SPAWN:RepeatOnEngineShutDown() -trace.f( self.ClassName ) +self:T() self:Repeat() self.RepeatOnEngineShutDown = true @@ -232,15 +236,64 @@ trace.f( self.ClassName ) return self end +function SPAWN:CleanUp( SpawnCleanUpInterval ) +self:T() + + self.SpawnCleanUpInterval = SpawnCleanUpInterval + self.SpawnCleanUpTimeStamps = {} + self.CleanUpFunction = routines.scheduleFunction( self._SpawnCleanUpScheduler, { self }, timer.getTime() + 1, 60 ) +end + +function SPAWN:_SpawnCleanUpScheduler() +self:T() + + local SpawnGroup = self:GetFirstAliveGroup() + + while SpawnGroup do + + if SpawnGroup:AllOnGround() and SpawnGroup:GetMaxVelocity() < 1 then + if not self.SpawnCleanUpTimeStamps[SpawnGroup:GetName()] then + self.SpawnCleanUpTimeStamps[SpawnGroup:GetName()] = timer.getTime() + else + if self.SpawnCleanUpTimeStamps[SpawnGroup:GetName()] + self.SpawnCleanUpInterval < timer.getTime() then + SpawnGroup:Destroy() + end + end + else + self.SpawnCleanUpTimeStamps[SpawnGroup:GetName()] = nil + end + + SpawnGroup = self:GetNextAliveGroup() + end + +end --- Will SPAWN a Group whenever you want to do this. -- Note that the configuration with the above functions will apply when calling this method: Maxima, Randomization of routes, Scheduler, ... -- Uses @{DATABASE} global object defined in MOOSE. -- @treturn SPAWN -function SPAWN:Spawn() -trace.f( self.ClassName ) - local SpawnTemplate = self:_Prepare( true ) - if self.SpawnStartPoint ~= 0 or self.SpawnEndPoint ~= 0 then +function SPAWN:Spawn( SpawnGroupName ) + self:T( { self.SpawnPrefix, SpawnGroupName } ) + local SpawnTemplate = self:_Prepare( SpawnGroupName ) + if self.SpawnRandomize then + SpawnTemplate = self:_RandomizeRoute( SpawnTemplate ) + end + _Database:Spawn( SpawnTemplate ) + if self.SpawnRepeat then + _Database:SetStatusGroup( SpawnTemplate.name, "ReSpawn" ) + end + return self +end + + +--- Will SPAWN a Group with a specified index number whenever you want to do this. +-- Note that the configuration with the above functions will apply when calling this method: Maxima, Randomization of routes, Scheduler, ... +-- Uses @{DATABASE} global object defined in MOOSE. +-- @treturn SPAWN +function SPAWN:SpawnWithIndex( SpawnIndex ) + self:T( { self.SpawnPrefix, SpawnIndex } ) + local SpawnTemplate = self:_Prepare( self:SpawnGroupName( SpawnIndex ) ) + if self.SpawnRandomize then SpawnTemplate = self:_RandomizeRoute( SpawnTemplate ) end _Database:Spawn( SpawnTemplate ) @@ -257,12 +310,15 @@ end -- @treturn SPAWN -- Uses _Database global object defined in MOOSE. function SPAWN:ReSpawn( SpawnGroupName ) -trace.f( self.ClassName, { SpawnGroupName } ) +self:T( { SpawnGroupName } ) + local SpawnGroup = Group.getByName( SpawnGroupName ) - SpawnGroup:destroy() - local SpawnTemplate = self:_Prepare( false ) - -- Give the Group the original name of the Group. - SpawnTemplate.name = SpawnGroupName + if SpawnGroup then + SpawnGroup:destroy() + end + + local SpawnTemplate = self:_Prepare( SpawnGroupName ) + -- Give the units of the Group the name following the SPAWN naming convention, so that they don't replace other units within the ME. local SpawnUnits = table.getn( SpawnTemplate.units ) for u = 1, SpawnUnits do @@ -276,13 +332,13 @@ end --- Will SPAWN a Group whenever you want to do this, but for AIR Groups only to be applied, and will SPAWN the Group in Uncontrolled mode... This will be similar to the Uncontrolled flag setting in the ME. -- @treturn SPAWN function SPAWN:SpawnUncontrolled() -trace.f( self.ClassName ) +self:T() self.UnControlled = true local SpawnCountStart = self.SpawnCount + 1 for SpawnCount = SpawnCountStart, self.SpawnMaxGroups do - local SpawnTemplate = self:_Prepare( true ) + local SpawnTemplate = self:_Prepare( ) SpawnTemplate.uncontrolled = true _Database:Spawn( SpawnTemplate ) end @@ -293,94 +349,212 @@ end --- Will SPAWN a Group from a Carrier. This function is mostly advisable to be used if you want to simulate SPAWNing from air units, like helicopters, which are dropping infantry into a defined Landing Zone. --- @tparam Group CarrierGroup is the Group of the AIR unit or GROUND unit dropping or unloading other units. +-- @tparam Group HostUnit is the AIR unit or GROUND unit dropping or unloading the Spawn group. -- @tparam string TargetZonePrefix is the Prefix of the Zone defined in the ME where the Group should be moving to after drop. -- @tparam string NewGroupName (forgot this). -- @tparam bool LateActivate (optional) does the SPAWNing with Lateactivation on. -function SPAWN:FromCarrier( CarrierGroup, TargetZonePrefix, NewGroupName, LateActivate ) -trace.f( self.ClassName, { CarrierGroup, TargetZonePrefix, NewGroupName, LateActivate } ) +function SPAWN:FromHost( HostUnit, OuterRadius, InnerRadius, NewGroupName, LateActivate ) +self:T( { HostUnit, OuterRadius, InnerRadius, NewGroupName, LateActivate } ) local SpawnTemplate - if CarrierGroup and CarrierGroup:isExist() and CarrierGroup:getUnit(1) then -- and CarrierGroup:getUnit(1):inAir() == false then + if HostUnit and HostUnit:isExist() then -- and HostUnit:getUnit(1):inAir() == false then - local GroupUnits = CarrierGroup:getUnits() - local GroupUnitCount = table.getn(GroupUnits) - trace.i( self.ClassName, "CarrierGroup:getSize() = " .. CarrierGroup:getSize() ) - trace.i( self.ClassName, 'GroupUnitCount = ' .. GroupUnitCount ) - - for UnitId, UnitData in pairs(GroupUnits) do + SpawnTemplate = self:_Prepare( NewGroupName ) - UnitDeploy = UnitData - - SpawnTemplate = self:_Prepare( true ) - - if ( self.SpawnMaxGroups == 0 ) or ( self.SpawnCount <= self.SpawnMaxGroups ) then - if ( self.SpawnMaxGroupsAlive == 0 ) or ( self.AliveUnits < self.SpawnMaxGroupsAlive * #self.SpawnTemplate.units ) or self.UnControlled then + if ( self.SpawnMaxGroups == 0 ) or ( self.SpawnCount <= self.SpawnMaxGroups ) then + if ( self.SpawnMaxGroupsAlive == 0 ) or ( self.AliveUnits < self.SpawnMaxGroupsAlive * #self.SpawnTemplate.units ) or self.UnControlled then - if NewGroupName ~= nil then - SpawnTemplate.name = NewGroupName + if LateActivate ~= nil then + if LateActivate == true then + SpawnTemplate.lateActivation = true + SpawnTemplate.visible = true end - - if LateActivate ~= nil then - if LateActivate == true then - SpawnTemplate.lateActivation = true - SpawnTemplate.visible = true - end - end - - SpawnTemplate = self:_RandomizeRoute( SpawnTemplate ) - - local TargetZone = trigger.misc.getZone( TargetZonePrefix ) - local TargetZonePos = {} - TargetZonePos.x = TargetZone.point.x + math.random(TargetZone.radius * -1, TargetZone.radius) - TargetZonePos.z = TargetZone.point.z + math.random(TargetZone.radius * -1, TargetZone.radius) - - local RouteCount = table.getn( SpawnTemplate.route.points ) - trace.i( self.ClassName, "RouteCount = " .. RouteCount ) - - local UnitDeployPosition = UnitDeploy:getPoint() - SpawnTemplate.route.points[1].x = UnitDeployPosition.x - 50 - SpawnTemplate.route.points[1].y = UnitDeployPosition.z - SpawnTemplate.route.points[1].alt = nil - SpawnTemplate.route.points[1].alt_type = nil - - if SpawnStartPoint ~= 0 and SpawnEndPoint ~= 0 then - SpawnTemplate.route.points[RouteCount].x = TargetZonePos.x - SpawnTemplate.route.points[RouteCount].y = TargetZonePos.z - else - SpawnTemplate.route.points[RouteCount].x = TargetZone.point.x - SpawnTemplate.route.points[RouteCount].y = TargetZone.point.z - end - - trace.i( self.ClassName, 'SpawnTemplate.route.points['..RouteCount..'].x = ' .. SpawnTemplate.route.points[RouteCount].x .. ', SpawnTemplate.route.points['..RouteCount..'].y = ' .. SpawnTemplate.route.points[RouteCount].y ) - - for v = 1, table.getn( SpawnTemplate.units ) do - local SpawnPos = routines.getRandPointInCircle( UnitDeployPosition, 40, 10 ) - SpawnTemplate.units[v].x = SpawnPos.x - SpawnTemplate.units[v].y = SpawnPos.y - trace.i( self.ClassName, 'SpawnTemplate.units['..v..'].x = ' .. SpawnTemplate.units[v].x .. ', SpawnTemplate.units['..v..'].y = ' .. SpawnTemplate.units[v].y ) - end - - _Database:Spawn( SpawnTemplate ) end + + SpawnTemplate = self:_RandomizeRoute( SpawnTemplate ) + + local RouteCount = table.getn( SpawnTemplate.route.points ) + self:T( "RouteCount = " .. RouteCount ) + + local UnitDeployPosition = HostUnit:getPoint() + for PointID, Point in pairs( SpawnTemplate.route.points ) do + Point.x = UnitDeployPosition.x + Point.y = UnitDeployPosition.z + Point.alt = nil + Point.alt_type = nil + end + + for v = 1, table.getn( SpawnTemplate.units ) do + local SpawnPos = routines.getRandPointInCircle( UnitDeployPosition, OuterRadius, InnerRadius ) + SpawnTemplate.units[v].x = SpawnPos.x + SpawnTemplate.units[v].y = SpawnPos.y + self:T( 'SpawnTemplate.units['..v..'].x = ' .. SpawnTemplate.units[v].x .. ', SpawnTemplate.units['..v..'].y = ' .. SpawnTemplate.units[v].y ) + end + + _Database:Spawn( SpawnTemplate ) end end - end - trace.r( self.ClassName, "" ) + return SpawnTemplate +end + +--- Will SPAWN a Group from a Carrier. This function is mostly advisable to be used if you want to simulate SPAWNing from air units, like helicopters, which are dropping infantry into a defined Landing Zone. +-- @tparam Group CarrierUnit is the AIR unit or GROUND unit dropping or unloading the Spawn group. +-- @tparam string TargetZonePrefix is the Prefix of the Zone defined in the ME where the Group should be moving to after drop. +-- @tparam string NewGroupName (forgot this). +-- @tparam bool LateActivate (optional) does the SPAWNing with Lateactivation on. +function SPAWN:FromCarrier( CarrierUnit, TargetZonePrefix, NewGroupName, LateActivate ) +self:T( { CarrierUnit, TargetZonePrefix, NewGroupName, LateActivate } ) + + local SpawnTemplate + + if CarrierUnit and CarrierUnit:isExist() then -- and CarrierUnit:getUnit(1):inAir() == false then + + SpawnTemplate = self:_Prepare( NewGroupName ) + + if ( self.SpawnMaxGroups == 0 ) or ( self.SpawnCount <= self.SpawnMaxGroups ) then + if ( self.SpawnMaxGroupsAlive == 0 ) or ( self.AliveUnits < self.SpawnMaxGroupsAlive * #self.SpawnTemplate.units ) or self.UnControlled then + + if LateActivate ~= nil then + if LateActivate == true then + SpawnTemplate.lateActivation = true + SpawnTemplate.visible = true + end + end + + SpawnTemplate = self:_RandomizeRoute( SpawnTemplate ) + + local TargetZone = trigger.misc.getZone( TargetZonePrefix ) + local TargetZonePos = {} + TargetZonePos.x = TargetZone.point.x + math.random(TargetZone.radius / 2 * -1, TargetZone.radius / 2 ) + TargetZonePos.z = TargetZone.point.z + math.random(TargetZone.radius / 2 * -1, TargetZone.radius / 2 ) + + local RouteCount = table.getn( SpawnTemplate.route.points ) + self:T( "RouteCount = " .. RouteCount ) + + local UnitDeployPosition = CarrierUnit:getPosition().p + SpawnTemplate.route.points[1].x = UnitDeployPosition.x - 50 + SpawnTemplate.route.points[1].y = UnitDeployPosition.z + SpawnTemplate.route.points[1].alt = nil + SpawnTemplate.route.points[1].alt_type = nil + + if self.SpawnRandomize then + SpawnTemplate.route.points[RouteCount].x = TargetZonePos.x + SpawnTemplate.route.points[RouteCount].y = TargetZonePos.z + else + SpawnTemplate.route.points[RouteCount].x = TargetZone.point.x + SpawnTemplate.route.points[RouteCount].y = TargetZone.point.z + end + + self:T( 'SpawnTemplate.route.points['..RouteCount..'].x = ' .. SpawnTemplate.route.points[RouteCount].x .. ', SpawnTemplate.route.points['..RouteCount..'].y = ' .. SpawnTemplate.route.points[RouteCount].y ) + + for v = 1, table.getn( SpawnTemplate.units ) do + local SpawnPos = routines.getRandPointInCircle( UnitDeployPosition, 40, 10 ) + SpawnTemplate.units[v].x = SpawnPos.x + SpawnTemplate.units[v].y = SpawnPos.y + self:T( 'SpawnTemplate.units['..v..'].x = ' .. SpawnTemplate.units[v].x .. ', SpawnTemplate.units['..v..'].y = ' .. SpawnTemplate.units[v].y ) + end + + _Database:Spawn( SpawnTemplate ) + end + end + end return SpawnTemplate end + +--- Will return the SpawnGroupName either with with a specific count number or without any count. +-- @tparam number SpawnIndex is the number of the Group that is to be SPAWNed. +-- @treturn string SpawnGroupName +function SPAWN:SpawnGroupName( SpawnIndex ) + self:T( { self.SpawnPrefix, SpawnIndex } ) + + if SpawnIndex then + self:T( string.format( '%s#%03d', self.SpawnPrefix, SpawnIndex ) ) + return string.format( '%s#%03d', self.SpawnPrefix, SpawnIndex ) + else + self:T( self.SpawnPrefix ) + return self.SpawnPrefix + end + +end + +function SPAWN:GetIndexFromGroup( Group ) + self:T( { self.SpawnPrefix, Group } ) + + local IndexString = string.match( Group:GetName(), "#.*$" ) + local Index = tonumber( IndexString:sub(2) ) + + self:T( IndexString, Index ) + return Index + +end + +function SPAWN:GetLastIndex() + + return self.SpawnCount +end + + + +function SPAWN:GetFirstAliveGroup() +self:T() + + self.SpawnIndex = 1 + for SpawnIndex = 1, self.SpawnCount do + SpawnGroupName = self:SpawnGroupName( SpawnIndex ) + SpawnGroup = Group.getByName( SpawnGroupName ) + if SpawnGroup and SpawnGroup:isExist() then + self.SpawnIndex = SpawnIndex + return GROUP:New( SpawnGroup ) + end + end + + self.SpawnIndex = nil + return nil +end + +function SPAWN:GetNextAliveGroup() +self:T() + + self.SpawnIndex = self.SpawnIndex + 1 + for SpawnIndex = self.SpawnIndex, self.SpawnCount do + SpawnGroupName = self:SpawnGroupName( SpawnIndex ) + SpawnGroup = Group.getByName( SpawnGroupName ) + if SpawnGroup and SpawnGroup:isExist() then + self.SpawnIndex = SpawnIndex + return GROUP:New( SpawnGroup ) + end + end + + self.SpawnIndex = nil + return nil +end + +function SPAWN:GetLastAliveGroup() +self:T() + + local LastGroupName = self:SpawnGroupName( self:GetLastIndex() ) + + return GROUP:New( Group.getByName( LastGroupName ) ) +end + --- Will SPAWN a Group within a given ZoneName. -- @tparam string ZonePrefix is the name of the zone where the Group is to be SPAWNed. -- @treturn SpawnTemplate -function SPAWN:InZone( ZonePrefix ) -trace.f("Spawn", ZonePrefix ) +function SPAWN:InZone( ZonePrefix, SpawnGroupName ) +self:T( ZonePrefix ) - local SpawnTemplate = self:_Prepare( true ) + local SpawnTemplate + + if SpawnGroupName then + SpawnTemplate = self:_Prepare( SpawnGroupName ) + else + SpawnTemplate = self:_Prepare() + end local Zone = trigger.misc.getZone( ZonePrefix ) local ZonePos = {} @@ -443,7 +617,7 @@ end --- Gets the Group Template from the ME environment definition. -- This method used the @{DATABASE} object, which contains ALL initial and new SPAWNed object in MOOSE. function SPAWN:_GetTemplate( SpawnPrefix ) -trace.f( self.ClassName, SpawnPrefix ) +self:T( SpawnPrefix ) local SpawnTemplate = nil @@ -457,13 +631,13 @@ trace.f( self.ClassName, SpawnPrefix ) SpawnTemplate.SpawnCategoryID = self:_GetGroupCategoryID( SpawnPrefix ) SpawnTemplate.SpawnCountryID = self:_GetGroupCountryID( SpawnPrefix ) - trace.r( self.ClassName, "", { SpawnTemplate } ) + self:T( { SpawnTemplate } ) return SpawnTemplate end --- Prepares the new Group Template before Spawning. -function SPAWN:_Prepare( SpawnIncrement ) -trace.f( self.ClassName ) +function SPAWN:_Prepare( SpawnGroupName ) +self:T() local SpawnCount local SpawnUnits @@ -476,11 +650,14 @@ trace.f( self.ClassName ) end -- Increase the spawn counter for the group - if SpawnIncrement == true then + if SpawnGroupName then + SpawnTemplate.name = SpawnGroupName + else self.SpawnCount = self.SpawnCount + 1 + SpawnTemplate.name = self:SpawnGroupName( self.SpawnCount ) end - SpawnTemplate.name = string.format( self.SpawnPrefix .. '#%03d', self.SpawnCount ) + SpawnTemplate.groupId = nil SpawnTemplate.lateActivation = false if SpawnTemplate.SpawnCategoryID == Group.Category.GROUND then @@ -501,32 +678,32 @@ trace.f( self.ClassName ) SpawnUnits = table.getn( SpawnTemplate.units ) for u = 1, SpawnUnits do - SpawnTemplate.units[u].name = string.format( self.SpawnPrefix .. '#%03d-%02d', self.SpawnCount, u ) + SpawnTemplate.units[u].name = string.format( SpawnTemplate.name .. '-%02d', u ) SpawnTemplate.units[u].unitId = nil - SpawnTemplate.units[u].x = SpawnTemplate.route.points[1].x - SpawnTemplate.units[u].y = SpawnTemplate.route.points[1].y + SpawnTemplate.units[u].x = SpawnTemplate.route.points[1].x + math.random( -50, 50 ) + SpawnTemplate.units[u].y = SpawnTemplate.route.points[1].y + math.random( -50, 50 ) end - trace.r( self.ClassName, "", SpawnTemplate.name ) + self:T( SpawnTemplate.name ) return SpawnTemplate end --- Will randomize the route of the Group Template. function SPAWN:_RandomizeRoute( SpawnTemplate ) -trace.f( self.ClassName, SpawnTemplate.name ) +self:T( SpawnTemplate.name ) - if self.SpawnStartPoint and self.SpawnEndPoint then + if self.SpawnRandomize then local RouteCount = table.getn( SpawnTemplate.route.points ) - for t = self.SpawnStartPoint, RouteCount - self.SpawnEndPoint do + for t = self.SpawnStartPoint+1, RouteCount - self.SpawnEndPoint do SpawnTemplate.route.points[t].x = SpawnTemplate.route.points[t].x + math.random( self.SpawnRadius * -1, self.SpawnRadius ) SpawnTemplate.route.points[t].y = SpawnTemplate.route.points[t].y + math.random( self.SpawnRadius * -1, self.SpawnRadius ) SpawnTemplate.route.points[t].alt = nil --SpawnGroup.route.points[t].alt_type = nil - trace.i( self.ClassName, 'SpawnTemplate.route.points[' .. t .. '].x = ' .. SpawnTemplate.route.points[t].x .. ', SpawnTemplate.route.points[' .. t .. '].y = ' .. SpawnTemplate.route.points[t].y ) + self:T( 'SpawnTemplate.route.points[' .. t .. '].x = ' .. SpawnTemplate.route.points[t].x .. ', SpawnTemplate.route.points[' .. t .. '].y = ' .. SpawnTemplate.route.points[t].y ) end end - trace.r( self.ClassName, "", SpawnTemplate.name ) + self:T( SpawnTemplate.name ) return SpawnTemplate end @@ -536,16 +713,16 @@ end --- Obscolete -- @todo Need to delete this... _Database does this now ... function SPAWN:OnBirth( event ) -trace.f( self.ClassName, { event } ) +self:T( { event } ) if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line if event.initiator and event.initiator:getName() then - trace.l(self.ClassName, "OnBirth", "Birth object : " .. event.initiator:getName() ) + self:T( "Birth object : " .. event.initiator:getName() ) local EventPrefix = string.match( event.initiator:getName(), ".*#" ) if EventPrefix == self.SpawnPrefix .. '#' then --MessageToAll( "Mission command: unit " .. SpawnPrefix .. " spawned." , 5, EventPrefix .. '/Event') self.AliveUnits = self.AliveUnits + 1 - trace.l(self.ClassName, "OnBirth", self.AliveUnits ) + self:T( self.AliveUnits ) end end end @@ -555,17 +732,17 @@ end --- Obscolete -- @todo Need to delete this... _Database does this now ... function SPAWN:OnDeadOrCrash( event ) -trace.f( self.ClassName, { event } ) +self:T( { event } ) if event.initiator and event.initiator:getName() then - trace.l( self.ClassName, "OnDeadOrCrash", "Dead object : " .. event.initiator:getName() ) + self:T( "Dead object : " .. event.initiator:getName() ) local EventPrefix = string.match( event.initiator:getName(), ".*#" ) if EventPrefix == self.SpawnPrefix .. '#' then -- local DestroyedUnit = Unit.getByName( EventPrefix ) -- if DestroyedUnit and DestroyedUnit.getLife() <= 1.0 then --MessageToAll( "Mission command: unit " .. SpawnPrefix .. " crashed." , 5, EventPrefix .. '/Event') self.AliveUnits = self.AliveUnits - 1 - trace.l( self.ClassName, "OnDeadOrCrash", self.AliveUnits ) + self:T( self.AliveUnits ) -- end end end @@ -575,17 +752,17 @@ end -- This is needed to ensure that Re-SPAWNing is only done for landed AIR Groups. -- @todo Need to test for AIR Groups only... function SPAWN:OnLand( event ) -trace.f( self.ClassName, { event } ) +self:T( { event } ) if event.initiator and event.initiator:getName() then - trace.l( self.ClassName, "OnLand", "Landed object : " .. event.initiator:getName() ) + self:T( "Landed object : " .. event.initiator:getName() ) local EventPrefix = string.match( event.initiator:getName(), ".*#" ) if EventPrefix == self.SpawnPrefix .. '#' then self.Landed = true - trace.l( self.ClassName, "OnLand", "self.Landed = true" ) + self:T( "self.Landed = true" ) if self.Landed and self.RepeatOnLanding then local SpawnGroupName = Unit.getGroup(event.initiator):getName() - trace.l( self.ClassName, "OnLand", "ReSpawn " .. SpawnGroupName ) + self:T( "ReSpawn " .. SpawnGroupName ) self:ReSpawn( SpawnGroupName ) end end @@ -596,13 +773,13 @@ end -- This is needed to ensure that Re-SPAWNing only is done for landed AIR Groups. -- @todo Need to test for AIR Groups only... function SPAWN:OnTakeOff( event ) -trace.f( self.ClassName, { event } ) +self:T( { event } ) if event.initiator and event.initiator:getName() then - trace.l( self.ClassName, "OnTakeOff", "TakeOff object : " .. event.initiator:getName() ) + self:T( "TakeOff object : " .. event.initiator:getName() ) local EventPrefix = string.match( event.initiator:getName(), ".*#" ) if EventPrefix == self.SpawnPrefix .. '#' then - trace.l( self.ClassName, "OnTakeOff", "self.Landed = false" ) + self:T( "self.Landed = false" ) self.Landed = false end end @@ -615,15 +792,15 @@ end -- @see OnLand -- @todo Need to test for AIR Groups only... function SPAWN:OnEngineShutDown( event ) -trace.f( self.ClassName, { event } ) +self:T( { event } ) if event.initiator and event.initiator:getName() then - trace.l( self.ClassName, "OnEngineShutDown", "EngineShutDown object : " .. event.initiator:getName() ) + self:T( "EngineShutDown object : " .. event.initiator:getName() ) local EventPrefix = string.match( event.initiator:getName(), ".*#" ) if EventPrefix == self.SpawnPrefix .. '#' then if self.Landed and self.RepeatOnEngineShutDown then local SpawnGroupName = Unit.getGroup(event.initiator):getName() - trace.l( self.ClassName, "OnEngineShutDown", "ReSpawn " .. SpawnGroupName ) + self:T( "ReSpawn " .. SpawnGroupName ) self:ReSpawn( SpawnGroupName ) end end @@ -636,7 +813,7 @@ end --- This function is called automatically by the Spawning scheduler. -- It is the internal worker method SPAWNing new Groups on the defined time intervals. function SPAWN:_Scheduler() -trace.l( self.ClassName, '_Scheduler', self.SpawnPrefix ) +self:T( self.SpawnPrefix ) if self.SpawnInit or self.SpawnCurrentTimer == self.SpawnSetTimer then -- Validate if there are still groups left in the batch... diff --git a/Moose/Stage.lua b/Moose/Stage.lua index 9eb6106f9..5b79bfd8a 100644 --- a/Moose/Stage.lua +++ b/Moose/Stage.lua @@ -27,13 +27,12 @@ STAGE = { function STAGE:New() -trace.f(self.ClassName) local self = BASE:Inherit( self, BASE:New() ) + self:T() return self end function STAGE:Execute( Mission, Client, Task ) -trace.f(self.ClassName) local Valid = true @@ -41,13 +40,10 @@ trace.f(self.ClassName) end function STAGE:Executing( Mission, Client, Task ) -trace.f(self.ClassName) end function STAGE:Validate( Mission, Client, Task ) -trace.f(self.ClassName) - local Valid = true return Valid @@ -56,31 +52,30 @@ end STAGEBRIEF = { ClassName = "BRIEF", - MSG = { ID = "Brief", TIME = 30 }, + MSG = { ID = "Brief", TIME = 1 }, Name = "Brief", StageBriefingTime = 0, - StageBriefingDuration = 30 + StageBriefingDuration = 1 } function STAGEBRIEF:New() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, STAGE:New() ) - Child.StageType = 'CLIENT' - return Child + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'CLIENT' + return self end function STAGEBRIEF:Execute( Mission, Client, Task ) -trace.f(self.ClassName) local Valid = BASE:Inherited(self):Execute( Mission, Client, Task ) + self:T() Mission:ShowBriefing( Client ) self.StageBriefingTime = timer.getTime() return Valid end function STAGEBRIEF:Validate( Mission, Client, Task ) -trace.f(self.ClassName) local Valid = STAGE:Validate( Mission, Client, Task ) + self:T() if timer.getTime() - self.StageBriefingTime <= self.StageBriefingDuration then return 0 @@ -94,34 +89,33 @@ end STAGESTART = { ClassName = "START", - MSG = { ID = "Start", TIME = 30 }, + MSG = { ID = "Start", TIME = 1 }, Name = "Start", StageStartTime = 0, - StageStartDuration = 30 + StageStartDuration = 1 } function STAGESTART:New() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, STAGE:New() ) - Child.StageType = 'CLIENT' - return Child + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'CLIENT' + return self end function STAGESTART:Execute( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() local Valid = BASE:Inherited(self):Execute( Mission, Client, Task ) if Task.TaskBriefing then - Client:Message( Task.TaskBriefing, self.StageStartDuration, Mission.Name .. "/Stage", "Mission Command: Tasking" ) + Client:Message( Task.TaskBriefing, 30, Mission.Name .. "/Stage", "Mission Command: Tasking" ) else - Client:Message( 'Task ' .. Task.TaskNumber .. '.', self.StageStartDuration, Mission.Name .. "/Stage", "Mission Command: Tasking" ) + Client:Message( 'Task ' .. Task.TaskNumber .. '.', 30, Mission.Name .. "/Stage", "Mission Command: Tasking" ) end self.StageStartTime = timer.getTime() return Valid end function STAGESTART:Validate( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() local Valid = STAGE:Validate( Mission, Client, Task ) if timer.getTime() - self.StageStartTime <= self.StageStartDuration then @@ -135,17 +129,90 @@ trace.f(self.ClassName) end +STAGE_CARGO_LOAD = { + ClassName = "STAGE_CARGO_LOAD" +} + +function STAGE_CARGO_LOAD:New() + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'CLIENT' + return self +end + +function STAGE_CARGO_LOAD:Execute( Mission, Client, Task ) +self:T() + local Valid = BASE:Inherited(self):Execute( Mission, Client, Task ) + + for LoadCargoID, LoadCargo in pairs( Task.Cargos.LoadCargos ) do + LoadCargo:Load( Client ) + end + + if Mission.MissionReportFlash and Client:IsTransport() then + Client:ShowCargo() + end + + return Valid +end + +function STAGE_CARGO_LOAD:Validate( Mission, Client, Task ) +self:T() + local Valid = STAGE:Validate( Mission, Client, Task ) + + return 1 +end + + +STAGE_CARGO_INIT = { + ClassName = "STAGE_CARGO_INIT" +} + +function STAGE_CARGO_INIT:New() + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'CLIENT' + return self +end + +function STAGE_CARGO_INIT:Execute( Mission, Client, Task ) +self:T() + local Valid = BASE:Inherited(self):Execute( Mission, Client, Task ) + + for InitLandingZoneID, InitLandingZone in pairs( Task.LandingZones.LandingZones ) do + self:T( InitLandingZone ) + InitLandingZone:Spawn() + end + + + self:T( Task.Cargos.InitCargos ) + for InitCargoID, InitCargoData in pairs( Task.Cargos.InitCargos ) do + self:T( { InitCargoData } ) + InitCargoData:Spawn() + end + + return Valid +end + + +function STAGE_CARGO_INIT:Validate( Mission, Client, Task ) +self:T() + local Valid = STAGE:Validate( Mission, Client, Task ) + + return 1 +end + + + STAGEROUTE = { ClassName = "STAGEROUTE", - MSG = { ID = "Route", TIME = 1 }, + MSG = { ID = "Route", TIME = 5 }, Frequency = STAGE.FREQUENCY.REPEAT, Name = "Route" } function STAGEROUTE:New() -trace.f(self.ClassName) - -- Arrange meta tables local self = BASE:Inherit( self, STAGE:New() ) + self:T() self.StageType = 'CLIENT' self.MessageSwitch = true return self @@ -153,19 +220,17 @@ end function STAGEROUTE:Execute( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() local Valid = BASE:Inherited(self):Execute( Mission, Client, Task ) - if type( Task.LandingZones) == "table" then - local RouteMessage = "Fly to " - for LandingZoneID, LandingZoneName in pairs( Task.LandingZones ) do - RouteMessage = RouteMessage .. LandingZoneName .. ' at ' .. routines.getBRStringZone( { zone = LandingZoneName, ref = Client:ClientGroup():getUnit(1):getPoint(), true, true } ) .. ' km. ' - end - Client:Message( RouteMessage, self.MSG.TIME, Mission.Name .. "/StageRoute", "Co-Pilot: Route", 10 ) - else - Client:Message( "Fly to " .. Task.LandingZones .. ' at ' .. routines.getBRStringZone( { zone = Task.LandingZones, ref = Client:ClientGroup():getUnit(1):getPoint(), true, true } ) .. ' km. ', self.MSG.TIME, Mission.Name .. "/StageRoute", "Co-Pilot: Route", 1 ) + local RouteMessage = "Fly to " + self:T( Task.LandingZones ) + for LandingZoneID, LandingZoneName in pairs( Task.LandingZones.LandingZoneNames ) do + RouteMessage = RouteMessage .. LandingZoneName .. ' at ' .. routines.getBRStringZone( { zone = LandingZoneName, ref = Client:GetClientGroupUnit():getPoint(), true, true } ) .. ' km. ' end - if Client:IsTransport() then + Client:Message( RouteMessage, self.MSG.TIME, Mission.Name .. "/StageRoute", "Co-Pilot: Route", 20 ) + + if Mission.MissionReportFlash and Client:IsTransport() then Client:ShowCargo() end @@ -173,59 +238,32 @@ trace.f(self.ClassName) end function STAGEROUTE:Validate( Mission, Client, Task ) -trace.f(self.ClassName) - local Valid = STAGE:Validate( Mission, Client, Task ) +self:T() + local Valid = STAGE:Validate( Mission, Client, Task ) + + -- check if the Client is in the landing zone + self:T( Task.LandingZones.LandingZoneNames ) + Task.CurrentLandingZoneName = routines.IsUnitInZones( Client:GetClientGroupUnit(), Task.LandingZones.LandingZoneNames ) + + if Task.CurrentLandingZoneName then - -- check if this carrier is in the landing zone - Task.CurrentLandingZoneID = routines.IsUnitInZones( Client:ClientGroup():getUnits()[1], Task.LandingZones ) - if ( Task.CurrentLandingZoneID ) then - if not Task.Signalled then - - if Task.LandingZoneSignalType then - env.info( 'TransportSchedule: Task.LandingZoneSignalType = ' .. Task.LandingZoneSignalType.TEXT ) - if Task.LandingZoneSignalUnitNames then - local LandingZoneSignalUnit = Task.LandingZoneSignalUnitNames[Task.CurrentLandingZoneID] - trace.i( self.ClassName, 'LandingZoneSignalUnit = ' .. LandingZoneSignalUnit ) - - local SignalUnit = Unit.getByName(LandingZoneSignalUnit) - if SignalUnit == nil then - SignalUnit = StaticObject.getByName( LandingZoneSignalUnit ) - end - if SignalUnit ~= nil then - trace.i( self.ClassName, 'Signalling Unit' ) - local SignalVehiclePos = SignalUnit:getPosition().p - SignalVehiclePos.y = SignalVehiclePos.y + Task.LandingZoneSignalHeight - if Task.LandingZoneSignalType.ID == Task.SIGNAL.TYPE.SMOKE.ID then - trigger.action.smoke( SignalVehiclePos, Task.LandingZoneSignalColor.COLOR ) - elseif Task.LandingZoneSignalType.ID == Task.SIGNAL.TYPE.FLARE.ID then - trigger.action.signalFlare( SignalVehiclePos, Task.LandingZoneSignalColor.COLOR, 0 ) - end - end - else - env.info( 'TransportSchedule: Signaling landing zone ' ) - - local LandingZone = trigger.misc.getZone( Task.LandingZones [ Task.CurrentLandingZoneID ] ) - local CurrentPosition = { x = LandingZone.point.x, y = LandingZone.point.z } - LandingZone.point.y = land.getHeight( CurrentPosition ) + 10 - - if Task.LandingZoneSignalType.ID == Task.SIGNAL.TYPE.SMOKE.ID then - env.info( 'TransportSchedule: Smoking zone x = ' .. LandingZone.point.x .. ' y = ' .. LandingZone.point.y .. ' z = ' .. LandingZone.point.z ) - trigger.action.smoke( LandingZone.point, Task.LandingZoneSignalColor.COLOR ) - elseif Task.LandingZoneSignalType.ID == Task.SIGNAL.TYPE.SMOKE.FLARE.ID then - env.info( 'TransportSchedule: Flaring zone x = ' .. LandingZone.point.x .. ' y = ' .. LandingZone.point.y .. ' z = ' .. LandingZone.point.z ) - trigger.action.signalFlare( LandingZone.point, Task.LandingZoneSignalColor.COLOR, 0 ) - end + Task.CurrentLandingZone = Task.LandingZones.LandingZones[Task.CurrentLandingZoneName].CargoZone + Task.CurrentCargoZone = Task.LandingZones.LandingZones[Task.CurrentLandingZoneName] + + if Task.CurrentCargoZone then + if not Task.Signalled then + Task.Signalled = Task.CurrentCargoZone:Signal() end end - self.Signalled = true + + return 1 end - return 1 - end - - return 0 + return 0 end + + STAGELANDING = { ClassName = "STAGELANDING", MSG = { ID = "Landing", TIME = 10 }, @@ -234,37 +272,96 @@ STAGELANDING = { } function STAGELANDING:New() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, STAGE:New() ) - Child.StageType = 'CLIENT' - return Child + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'CLIENT' + return self end function STAGELANDING:Execute( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() - Client:Message( 'We have arrived at ' .. Task.LandingZones[Task.CurrentLandingZoneID] .. '. Land the helicopter to ' .. Task.TEXT[1] .. ' the ' .. Task.CargoType.TEXT .. '.', - self.MSG.TIME, Mission.Name .. "/Stage", "Co-Pilot: Landing" ) - + Client:Message( "We have arrived at the landing zone.", self.MSG.TIME, Mission.Name .. "/StageArrived", "Co-Pilot: Arrived", 10 ) + + Task.HostUnit = Task.CurrentCargoZone:GetHostUnit() + + if Task.HostUnit then + + Task.HostUnitName = Task.HostUnit:getName() + Task.HostUnitTypeName = Task.HostUnit:getTypeName() + + local HostMessage = "" + Task.CargoNames = "" + + local IsFirst = true + + for CargoID, Cargo in pairs( CARGOS ) do + if Cargo.CargoType == Task.CargoType then + + if Cargo:IsLandingRequired() then + self:T( "Task for cargo " .. Cargo.CargoType .. " requires landing.") + Task.IsLandingRequired = true + end + + if Cargo:IsSlingLoad() then + self:T( "Task for cargo " .. Cargo.CargoType .. " is a slingload.") + Task.IsSlingLoad = true + end + + if IsFirst then + IsFirst = false + Task.CargoNames = Task.CargoNames .. Cargo.CargoName .. "( " .. Cargo.CargoWeight .. " )" + else + Task.CargoNames = Task.CargoNames .. "; " .. Cargo.CargoName .. "( " .. Cargo.CargoWeight .. " )" + end + end + end + + if Task.IsLandingRequired then + HostMessage = "Land the helicopter to " .. Task.TEXT[1] .. " " .. Task.CargoNames .. "." + else + HostMessage = "Use the Radio menu and F6 to find the cargo, then fly or land near the cargo and " .. Task.TEXT[1] .. " " .. Task.CargoNames .. "." + end + + Client:Message( HostMessage, self.MSG.TIME, Mission.Name .. "/STAGELANDING.EXEC." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":", 10 ) + + end end function STAGELANDING:Validate( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() - if routines.IsUnitInZones( Client:ClientGroup():getUnits()[1], Task.LandingZones[Task.CurrentLandingZoneID] ) then - else - Task.Signalled = false - Task:RemoveCargoMenus( Client ) - return -1 - end + Task.CurrentLandingZoneName = routines.IsUnitInZones( Client:GetClientGroupUnit(), Task.LandingZones.LandingZoneNames ) + if Task.CurrentLandingZoneName then + + -- Client is in de landing zone. + self:T( Task.CurrentLandingZoneName ) + + Task.CurrentLandingZone = Task.LandingZones.LandingZones[Task.CurrentLandingZoneName].CargoZone + Task.CurrentCargoZone = Task.LandingZones.LandingZones[Task.CurrentLandingZoneName] + + if Task.CurrentCargoZone then + if not Task.Signalled then + Task.Signalled = Task.CurrentCargoZone:Signal() + end + end + else + if Task.CurrentLandingZone then + Task.CurrentLandingZone = nil + end + if Task.CurrentCargoZone then + Task.CurrentCargoZone = nil + end + Task.Signalled = false + Task:RemoveCargoMenus( Client ) + return -1 + end - if not Client:ClientGroup():getUnits()[1]:inAir() then - else - return 0 - end + if Task.IsLandingRequired and Client:GetClientGroupUnit():inAir() then + return 0 + end - return 1 + return 1 end STAGELANDED = { @@ -275,46 +372,52 @@ STAGELANDED = { } function STAGELANDED:New() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, STAGE:New() ) - Child.StageType = 'CLIENT' - return Child + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'CLIENT' + return self end function STAGELANDED:Execute( Mission, Client, Task ) -trace.f(self.ClassName) - Client:Message( 'We have landed within the landing zone. Use the radio menu (F10) to ' .. Task.TEXT[1] .. ' the ' .. Task.CargoType.TEXT .. '.', self.MSG.TIME, Mission.Name .. "/Stage", "Co-Pilot: Landed" ) - if not self.MenusAdded then - Task:RemoveCargoMenus( Client ) - Task:AddCargoMenus( Client, Mission._Cargos, 250 ) +self:T() + + if Task.IsLandingRequired then + Client:Message( 'We have landed within the landing zone. Use the radio menu (F10) to ' .. Task.TEXT[1] .. ' the ' .. Task.CargoType .. '.', + self.MSG.TIME, Mission.Name .. "/STAGELANDED.EXEC." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":" ) + if not self.MenusAdded then + Task.Cargo = nil + Task:RemoveCargoMenus( Client ) + Task:AddCargoMenus( Client, CARGOS, 250 ) + end end end function STAGELANDED:Validate( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() - if routines.IsUnitInZones( Client:ClientGroup():getUnits()[1], Task.LandingZones[Task.CurrentLandingZoneID] ) then - else - Task.Signalled = false - Task:RemoveCargoMenus( Client ) - return -2 - end + if not routines.IsUnitInZones( Client:GetClientGroupUnit(), Task.CurrentLandingZoneName ) then + self:T( "Client is not anymore in the landing zone, go back to stage Route, and remove cargo menus." ) + Task.Signalled = false + Task:RemoveCargoMenus( Client ) + return -2 + end - if not Client:ClientGroup():getUnits()[1]:inAir() then - else - Task.Signalled = false - return -1 - end + if Task.IsLandingRequired and Client:GetClientGroupUnit():inAir() then + self:T( "Client went back in the air. Go back to stage Landing." ) + Task.Signalled = false + return -1 + end - if Task.ExecuteStage == _TransportExecuteStage.EXECUTING then - else - return 0 - end + -- Wait until cargo is selected from the menu. + if Task.IsLandingRequired then + if not Task.Cargo then + return 0 + end + end - return 1 + return 1 end STAGEUNLOAD = { @@ -324,72 +427,63 @@ STAGEUNLOAD = { } function STAGEUNLOAD:New() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, STAGE:New() ) - Child.StageType = 'CLIENT' - return Child + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'CLIENT' + return self end function STAGEUNLOAD:Execute( Mission, Client, Task ) -trace.f(self.ClassName) - Client:Message( 'The ' .. Task.CargoType.TEXT .. ' are being ' .. Task.TEXT[2] .. ' within the landing zone. Wait until the helicopter is ' .. Task.TEXT[3] .. '.', - self.MSG.TIME, Mission.Name .. "/Stage", "Co-Pilot: Unload" ) - Task:RemoveCargoMenus( Client ) +self:T() + Client:Message( 'The ' .. Task.CargoType .. ' are being ' .. Task.TEXT[2] .. ' within the landing zone. Wait until the helicopter is ' .. Task.TEXT[3] .. '.', + self.MSG.TIME, Mission.Name .. "/StageUnLoad", "Co-Pilot: Unload" ) + Task:RemoveCargoMenus( Client ) end function STAGEUNLOAD:Executing( Mission, Client, Task ) -trace.f(self.ClassName) - env.info( 'STAGEUNLOAD:Executing() Task.CargoName = ' .. Task.CargoName ) - local Cargo = Client:RemoveCargo( Task.CargoName ) - if Cargo then - env.info( 'STAGEUNLOAD:Executing() Cargo.CargoName = ' .. Cargo.CargoName ) - env.info( 'STAGEUNLOAD:Executing() Cargo.CargoGroupName = ' .. Cargo.CargoGroupName ) - env.info( 'STAGEUNLOAD:Executing() Mission._Cargos[Cargo.CargoName].CargoGroupTemplate = ' .. Mission._Cargos[Cargo.CargoName].CargoGroupTemplate ) - - if Cargo.CargoType.TRANSPORT == CARGO_TRANSPORT.UNIT then - if Cargo.CargoName then - if Task.TargetZoneName then - SPAWN:New( Mission._Cargos[Cargo.CargoName].CargoGroupTemplate ):FromCarrier ( Client:ClientGroup(), - Task.TargetZoneName, - Mission._Cargos[Cargo.CargoName].CargoGroupName ) - else - SPAWN:New( Mission._Cargos[Cargo.CargoName].CargoGroupTemplate ):FromCarrier ( Client:ClientGroup(), - Task.LandingZones[Task.CurrentLandingZoneID], - Mission._Cargos[Cargo.CargoName].CargoGroupName ) - end - end - end +self:T() + env.info( 'STAGEUNLOAD:Executing() Task.Cargo.CargoName = ' .. Task.Cargo.CargoName ) + + local TargetZoneName + + if Task.TargetZoneName then + TargetZoneName = Task.TargetZoneName + else + TargetZoneName = Task.CurrentLandingZoneName + end + + if Task.Cargo:UnLoad( Client, TargetZoneName ) then Task.ExecuteStage = _TransportExecuteStage.SUCCESS - Client:ShowCargo() + if Mission.MissionReportFlash then + Client:ShowCargo() + end end end function STAGEUNLOAD:Validate( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() env.info( 'STAGEUNLOAD:Validate()' ) - if routines.IsUnitInZones( Client:ClientGroup():getUnits()[1], Task.LandingZones[Task.CurrentLandingZoneID] ) then + if routines.IsUnitInZones( Client:GetClientGroupUnit(), Task.CurrentLandingZoneName ) then else Task.ExecuteStage = _TransportExecuteStage.FAILED Task:RemoveCargoMenus( Client ) - Client:Message( 'The ' .. Task.CargoType.TEXT .. " haven't been successfully " .. Task.TEXT[3] .. ' within the landing zone. Task and mission has failed.', + Client:Message( 'The ' .. Task.CargoType .. " haven't been successfully " .. Task.TEXT[3] .. ' within the landing zone. Task and mission has failed.', _TransportStageMsgTime.DONE, Mission.Name .. "/StageFailure", "Co-Pilot: Unload" ) return 1 end - if not Client:ClientGroup():getUnits()[1]:inAir() then + if not Client:GetClientGroupUnit():inAir() then else Task.ExecuteStage = _TransportExecuteStage.FAILED Task:RemoveCargoMenus( Client ) - Client:Message( 'The ' .. Task.CargoType.TEXT .. " haven't been successfully " .. Task.TEXT[3] .. ' within the landing zone. Task and mission has failed.', + Client:Message( 'The ' .. Task.CargoType .. " haven't been successfully " .. Task.TEXT[3] .. ' within the landing zone. Task and mission has failed.', _TransportStageMsgTime.DONE, Mission.Name .. "/StageFailure", "Co-Pilot: Unload" ) return 1 end if Task.ExecuteStage == _TransportExecuteStage.SUCCESS then - Client:Message( 'The ' .. Task.CargoType.TEXT .. ' have been sucessfully ' .. Task.TEXT[3] .. ' within the landing zone.', _TransportStageMsgTime.DONE, Mission.Name .. "/Stage", "Co-Pilot: Unload" ) - Mission._Cargos[Task.CargoName].Status = CARGOSTATUS.UNLOADED + Client:Message( 'The ' .. Task.CargoType .. ' have been sucessfully ' .. Task.TEXT[3] .. ' within the landing zone.', _TransportStageMsgTime.DONE, Mission.Name .. "/Stage", "Co-Pilot: Unload" ) Task:RemoveCargoMenus( Client ) Task.MissionTask:AddGoalCompletion( Task.MissionTask.GoalVerb, Task.CargoName, 1 ) -- We set the cargo as one more goal completed in the mission. return 1 @@ -405,173 +499,131 @@ STAGELOAD = { } function STAGELOAD:New() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, STAGE:New() ) - Child.StageType = 'CLIENT' - return Child + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'CLIENT' + return self end function STAGELOAD:Execute( Mission, Client, Task ) -trace.f(self.ClassName) - Client:Message( 'The ' .. Task.CargoType.TEXT .. ' are being ' .. Task.TEXT[2] .. ' within the landing zone. Wait until the helicopter is ' .. Task.TEXT[3] .. '.', - _TransportStageMsgTime.EXECUTING, Mission.Name .. "/Stage", "Co-Pilot: Load" ) +self:T() + + if not Task.IsSlingLoad then + Client:Message( 'The ' .. Task.CargoType .. ' are being ' .. Task.TEXT[2] .. ' within the landing zone. Wait until the helicopter is ' .. Task.TEXT[3] .. '.', + _TransportStageMsgTime.EXECUTING, Mission.Name .. "/STAGELOAD.EXEC." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":" ) - -- Route the cargo to the Carrier - if Mission._Cargos[Task.CargoName].CargoType.TRANSPORT == CARGO_TRANSPORT.UNIT then - Task:OnBoardCargo( Client:ClientGroup(), Mission._Cargos ) + -- Route the cargo to the Carrier + Task.Cargo:OnBoard( Client, Task.CurrentCargoZone, Task.OnBoardSide ) Task.ExecuteStage = _TransportExecuteStage.EXECUTING else - -- Add the group to the internal cargo; - Client:AddCargo( Task.CargoName, Mission._Cargos[Task.CargoName].CargoGroupName, Mission._Cargos[Task.CargoName].CargoType, Mission._Cargos[Task.CargoName].CargoWeight, Mission._Cargos[Task.CargoName].CargoGroupTemplate ) - Task.ExecuteStage = _TransportExecuteStage.SUCCESS + Task.ExecuteStage = _TransportExecuteStage.EXECUTING end end function STAGELOAD:Executing( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() - -- Remove the loaded object from the battle zone. - - if routines.IsPartOfGroupInRadius( Group.getByName(Mission._Cargos[Task.CargoName].CargoGroupName), Client:ClientGroup(), 75 ) then - routines.DestroyGroupInRadiusFromGroup( Group.getByName(Mission._Cargos[Task.CargoName].CargoGroupName), Client:ClientGroup(), 75 ) - env.info('trying to remove cargo') + -- If the Cargo is ready to be loaded, load it into the Client. - -- Add the group to the internal cargo; - Client:AddCargo( Task.CargoName, Mission._Cargos[Task.CargoName].CargoGroupName, Mission._Cargos[Task.CargoName].CargoType, Mission._Cargos[Task.CargoName].CargoWeight, Mission._Cargos[Task.CargoName].CargoGroupTemplate ) - - -- Message to the pilot that cargo has been loaded. - Client:Message( "The cargo " .. Task.CargoName .. " has been loaded in our helicopter.", 20, Mission.Name .. "/Stage", "Co-Pilot: Load" ) - Task.ExecuteStage = _TransportExecuteStage.SUCCESS - Client:ShowCargo() - end - -end + + if not Task.IsSlingLoad then + trace.i(self.ClassName, Task.Cargo.CargoName) + + if Task.Cargo:OnBoarded( Client, Task.CurrentCargoZone ) then -function STAGELOAD:Validate( Mission, Client, Task ) -trace.f(self.ClassName) - if routines.IsUnitInZones( Client:ClientGroup():getUnits()[1], Task.LandingZones[Task.CurrentLandingZoneID] ) then + -- Load the Cargo onto the Client + Task.Cargo:Load( Client ) + + -- Message to the pilot that cargo has been loaded. + Client:Message( "The cargo " .. Task.Cargo.CargoName .. " has been loaded in our helicopter.", + 20, Mission.Name .. "/STAGELANDING.LOADING1." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":" ) + Task.ExecuteStage = _TransportExecuteStage.SUCCESS + + Client:ShowCargo() + end else - Task:RemoveCargoMenus( Client ) - Task.ExecuteStage = _TransportExecuteStage.FAILED - Task.CargoName = nil - Client:Message( "The " .. Task.CargoType.TEXT .. " loading has been aborted. You flew outside the pick-up zone while loading. ", - _TransportStageMsgTime.DONE, Mission.Name .. "/StageSuccess", "Co-Pilot: Load" ) - return 1 - end - - if not Client:ClientGroup():getUnits()[1]:inAir() then - else - -- The carrier is back in the air, undo the loading process. - Task:RemoveCargoMenus( Client ) - Task.ExecuteStage = _TransportExecuteStage.NONE - Task.CargoName = nil - Client:Message( "The " .. Task.CargoType.TEXT .. " loading has been aborted. Land the helicopter and load the cargo. Don't fly outside the pick-up zone. ", - _TransportStageMsgTime.DONE, Mission.Name .. "/StageSuccess", "Co-Pilot: Load" ) - return -1 - end - - if Task.ExecuteStage == _TransportExecuteStage.SUCCESS then - Mission._Cargos[Task.CargoName].Status = CARGOSTATUS.LOADED - Task:RemoveCargoMenus( Client ) - Client:Message( 'Co-Pilot: The ' .. Task.CargoType.TEXT .. ' have been sucessfully ' .. Task.TEXT[3] .. ' within the landing zone.', - _TransportStageMsgTime.DONE, Mission.Name .. "/Stage", "Co-Pilot: Load" ) - Task.MissionTask:AddGoalCompletion( Task.MissionTask.GoalVerb, Task.CargoName, 1 ) - return 1 - end - - return 0 -end - -STAGE_SLINGLOAD_HOOK = { - ClassName = "STAGE_SLINGLOAD_HOOK", - MSG = { ID = "SlingLoadHook", TIME = 10 }, - Name = "SlingLoadHook" -} - -function STAGE_SLINGLOAD_HOOK:New() -trace.f(self.ClassName) - -- Arrange meta tables - local self = BASE:Inherit( self, STAGE:New() ) - self.StageType = 'CLIENT' - return self -end - -function STAGE_SLINGLOAD_HOOK:Execute( Mission, Client, Task ) -trace.f(self.ClassName) - Client:Message( 'Hook the Cargo onto the helicopter, and fly out the pick-up zone. Due to a bug in DCS world it cannot be chacked (for the moment) ' .. - 'if the cargo is in our out of the zone and attached to your helicopter...', self.MSG.TIME, Mission.Name .. "/Stage", "Co-Pilot: Hook" ) -end - -function STAGE_SLINGLOAD_HOOK:Validate( Mission, Client, Task ) -trace.f(self.ClassName) - - - for CargoID, CargoName in pairs( Task.CargoPrefixes ) do - env.info( CargoName ) - if StaticObject.getByName( CargoName ):inAir() then - Task.CargoName = CargoName - Task.CargoID = CargoID - Client:Message( 'Co-Pilot: The Cargo has been successfully hooked onto the helicopter within the landing zone.', self.MSG.TIME, Mission.Name .. "/StageSuccess" ) - break - end - end - - if Task.CargoName then - if routines.IsStaticInZones( StaticObject.getByName( Task.CargoName ), Task.LandingZones[Task.CurrentLandingZoneID] ) then - else - Mission._Cargos[Task.CargoName].Status = CARGOSTATUS.LOADED - return 1 - end - end - - return 1 -end - -STAGE_SLINGLOAD_UNHOOK = { - ClassName = "STAGE_SLINGLOAD_UNHOOK", - MSG = { ID = "SlingLoadUnHook", TIME = 10 }, - Name = "SlingLoadUnHook" -} - -function STAGE_SLINGLOAD_UNHOOK:New() -trace.f(self.ClassName) - -- Arrange meta tables - local self = BASE:Inherit( self, STAGE:New() ) - self.StageType = 'CLIENT' - return self -end - -function STAGE_SLINGLOAD_UNHOOK:Execute( Mission, Client, Task ) -trace.f(self.ClassName) - Client:Message( 'Deploy the Cargo in the Landing Zone and unhook the cargo, and fly out of the drop zone.', self.MSG.TIME, Mission.Name .. "/StageUnhook", "Co-Pilot: Unhook" ) -end - -function STAGE_SLINGLOAD_UNHOOK:Validate( Mission, Client, Task ) -trace.f(self.ClassName) - - for CargoID, CargoName in pairs( Task.CargoPrefixes ) do - if StaticObject.getByName( CargoName ):inAir() then - Task.CargoName = CargoName - Task.CargoID = CargoID - Client:Message( 'Co-Pilot: Drop the cargo within the landing zone and unhook.', self.MSG.TIME, Mission.Name .. "/Stage" ) - break - end - end - - if Task.CargoName then - if not StaticObject.getByName( Task.CargoName ):inAir() then - if routines.IsUnitInZones( Client:ClientGroup():getUnits()[1], Task.LandingZones[Task.CurrentLandingZoneID] ) then - else - Client:Message( 'Co-Pilot: The Cargo is Dropped in the Landing Zone, and You have flown outside of the landing zone.', self.MSG.TIME, Mission.Name .. "/Stage" ) - return 1 + Client:Message( "Hook the " .. Task.CargoNames .. " onto the helicopter " .. Task.TEXT[3] .. " within the landing zone.", + _TransportStageMsgTime.EXECUTING, Mission.Name .. "/STAGELOAD.LOADING.1." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":", 10 ) + for CargoID, Cargo in pairs( CARGOS ) do + self:T( "Cargo.CargoName = " .. Cargo.CargoName ) + + if Cargo:IsSlingLoad() then + local CargoStatic = StaticObject.getByName( Cargo.CargoStaticName ) + if CargoStatic then + trace.i(self.ClassName, "Cargo is found in the DCS simulator.") + local CargoStaticPosition = CargoStatic:getPosition().p + trace.i(self.ClassName, "Cargo Position x = " .. CargoStaticPosition.x .. ", y = " .. CargoStaticPosition.y .. ", z = " .. CargoStaticPosition.z ) + local CargoStaticHeight = routines.GetUnitHeight( CargoStatic ) + if CargoStaticHeight > 5 then + trace.i(self.ClassName, "Cargo is airborne.") + Cargo:StatusLoaded() + Task.Cargo = Cargo + Client:Message( 'The Cargo has been successfully hooked onto the helicopter and is now being sling loaded. Fly outside the landing zone.', + self.MSG.TIME, Mission.Name .. "/STAGELANDING.LOADING.2." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":" ) + Task.ExecuteStage = _TransportExecuteStage.SUCCESS + break + end + else + self:T( "Cargo not found in the DCS simulator." ) + end end end end - return 1 end +function STAGELOAD:Validate( Mission, Client, Task ) +self:T() + + self:T( "Task.CurrentLandingZoneName = " .. Task.CurrentLandingZoneName ) + + if not Task.IsSlingLoad then + if not routines.IsUnitInZones( Client:GetClientGroupUnit(), Task.CurrentLandingZoneName ) then + Task:RemoveCargoMenus( Client ) + Task.ExecuteStage = _TransportExecuteStage.FAILED + Task.CargoName = nil + Client:Message( "The " .. Task.CargoType .. " loading has been aborted. You flew outside the pick-up zone while loading. ", + self.MSG.TIME, Mission.Name .. "/STAGELANDING.VALIDATE.1." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":" ) + return -1 + end + + if not Client:GetClientGroupUnit():inAir() then + else + -- The carrier is back in the air, undo the loading process. + Task:RemoveCargoMenus( Client ) + Task.ExecuteStage = _TransportExecuteStage.NONE + Task.CargoName = nil + Client:Message( "The " .. Task.CargoType .. " loading has been aborted. Re-start the " .. Task.TEXT[3] .. " process. Don't fly outside the pick-up zone.", + self.MSG.TIME, Mission.Name .. "/STAGELANDING.VALIDATE.2." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":" ) + return -1 + end + + if Task.ExecuteStage == _TransportExecuteStage.SUCCESS then + Task:RemoveCargoMenus( Client ) + Client:Message( "Good Job. The " .. Task.CargoType .. " has been sucessfully " .. Task.TEXT[3] .. " within the landing zone.", + self.MSG.TIME, Mission.Name .. "/STAGELANDING.VALIDATE.3." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":" ) + Task.MissionTask:AddGoalCompletion( Task.MissionTask.GoalVerb, Task.CargoName, 1 ) + return 1 + end + + else + if Task.ExecuteStage == _TransportExecuteStage.SUCCESS then + CargoStatic = StaticObject.getByName( Task.Cargo.CargoStaticName ) + if CargoStatic and not routines.IsStaticInZones( CargoStatic, Task.CurrentLandingZoneName ) then + Client:Message( "Good Job. The " .. Task.CargoType .. " has been sucessfully " .. Task.TEXT[3] .. " and flown outside of the landing zone.", + self.MSG.TIME, Mission.Name .. "/STAGELANDING.VALIDATE.4." .. Task.HostUnitName, Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")" .. ":" ) + Task.MissionTask:AddGoalCompletion( Task.MissionTask.GoalVerb, Task.Cargo.CargoName, 1 ) + return 1 + end + end + + end + + + return 0 +end + + STAGEDONE = { ClassName = "STAGEDONE", MSG = { ID = "Done", TIME = 10 }, @@ -579,20 +631,19 @@ STAGEDONE = { } function STAGEDONE:New() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, STAGE:New() ) - Child.StageType = 'AI' - return Child + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'AI' + return self end function STAGEDONE:Execute( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() end function STAGEDONE:Validate( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() Task:Done() @@ -606,24 +657,23 @@ STAGEARRIVE = { } function STAGEARRIVE:New() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, STAGE:New() ) - Child.StageType = 'CLIENT' - return Child + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'CLIENT' + return self end function STAGEARRIVE:Execute( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() - Client:Message( 'We have arrived at ' .. Task.LandingZones[Task.CurrentLandingZoneID] .. ".", self.MSG.TIME, Mission.Name .. "/Stage", "Co-Pilot: Arrived" ) + Client:Message( 'We have arrived at ' .. Task.CurrentLandingZoneName .. ".", self.MSG.TIME, Mission.Name .. "/Stage", "Co-Pilot: Arrived" ) end function STAGEARRIVE:Validate( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() - Task.CurrentLandingZoneID = routines.IsUnitInZones( Client:ClientGroup():getUnits()[1], Task.LandingZones ) + Task.CurrentLandingZoneID = routines.IsUnitInZones( Client:GetClientGroupUnit(), Task.LandingZones ) if ( Task.CurrentLandingZoneID ) then else return -1 @@ -641,11 +691,10 @@ STAGEGROUPSDESTROYED = { } function STAGEGROUPSDESTROYED:New() -trace.f(self.ClassName) - -- Arrange meta tables - local Child = BASE:Inherit( self, STAGE:New() ) - Child.StageType = 'AI' - return Child + local self = BASE:Inherit( self, STAGE:New() ) + self:T() + self.StageType = 'AI' + return self end --function STAGEGROUPSDESTROYED:Execute( Mission, Client, Task ) @@ -655,7 +704,7 @@ end --end function STAGEGROUPSDESTROYED:Validate( Mission, Client, Task ) -trace.f(self.ClassName) +self:T() if Task.MissionTask:IsGoalReached() then return 1 @@ -665,8 +714,8 @@ trace.f(self.ClassName) end function STAGEGROUPSDESTROYED:Execute( Mission, Client, Task ) -trace.f(self.ClassName) - trace.i( self.ClassName, { Task.ClassName, Task.Destroyed } ) +self:T() + self:T( { Task.ClassName, Task.Destroyed } ) --env.info( 'Event Table Task = ' .. tostring(Task) ) end diff --git a/Moose/Task.lua b/Moose/Task.lua index 9ab902f20..d34ffeed1 100644 --- a/Moose/Task.lua +++ b/Moose/Task.lua @@ -9,29 +9,37 @@ Include.File( "Stage" ) TASK = { - -- Defines the different signal types with a Task. - SIGNAL = { - COLOR = { - RED = { ID = 1, COLOR = trigger.smokeColor.Red, TEXT = "A red" }, - GREEN = { ID = 2, COLOR = trigger.smokeColor.Green, TEXT = "A green" }, - BLUE = { ID = 3, COLOR = trigger.smokeColor.Blue, TEXT = "A blue" }, - WHITE = { ID = 4, COLOR = trigger.smokeColor.White, TEXT = "A white" }, - ORANGE = { ID = 5, COLOR = trigger.smokeColor.Orange, TEXT = "An orange" } - }, - TYPE = { - SMOKE = { ID = 1, TEXT = "smoke" }, - FLARE = { ID = 2, TEXT = "flare" } - } - }, - ClassName = "TASK", - Mission = {}, -- Owning mission of the Task - Name = '', - Stages = {}, - Stage = {}, - ActiveStage = 0, - TaskDone = false, - TaskFailed = false, - GoalTasks = {} + -- Defines the different signal types with a Task. + SIGNAL = { + COLOR = { + RED = { ID = 1, COLOR = trigger.smokeColor.Red, TEXT = "A red" }, + GREEN = { ID = 2, COLOR = trigger.smokeColor.Green, TEXT = "A green" }, + BLUE = { ID = 3, COLOR = trigger.smokeColor.Blue, TEXT = "A blue" }, + WHITE = { ID = 4, COLOR = trigger.smokeColor.White, TEXT = "A white" }, + ORANGE = { ID = 5, COLOR = trigger.smokeColor.Orange, TEXT = "An orange" } + }, + TYPE = { + SMOKE = { ID = 1, TEXT = "smoke" }, + FLARE = { ID = 2, TEXT = "flare" } + } + }, + ClassName = "TASK", + Mission = {}, -- Owning mission of the Task + Name = '', + Stages = {}, + Stage = {}, + Cargos = { + InitCargos = {}, + LoadCargos = {} + }, + LandingZones = { + LandingZoneNames = {}, + LandingZones = {} + }, + ActiveStage = 0, + TaskDone = false, + TaskFailed = false, + GoalTasks = {} } --- Instantiates a new TASK Base. Should never be used. Interface Class. @@ -120,7 +128,9 @@ trace.f(self.ClassName) end end - Client:Message( GoalsText, 10, "/TASKPROGRESS" .. self.ClassName, "Mission Command: Task Status", 30 ) + if Mission.MissionReportFlash or Mission.MissionReportShow then + Client:Message( GoalsText, 10, "/TASKPROGRESS" .. self.ClassName, "Mission Command: Task Status", 30 ) + end end --- Sets a TASK to status Done. @@ -132,7 +142,7 @@ end --- Returns if a TASK is done. -- @treturn bool function TASK:IsDone() -trace.f(self.ClassName) + trace.i( self.ClassName, self.TaskDone ) return self.TaskDone end @@ -145,7 +155,7 @@ end --- Returns if a TASk has failed. -- @return bool function TASK:IsFailed() -trace.f(self.ClassName) + trace.i( self.ClassName, self.TaskFailed ) return self.TaskFailed end @@ -268,7 +278,6 @@ end --- Returns if all the Goals of the TASK were achieved. -- @treturn bool function TASK:IsGoalReached( ) -trace.f(self.ClassName) local GoalReached = true @@ -287,6 +296,7 @@ trace.f(self.ClassName) end end + trace.i( self.ClassName, GoalReached ) return GoalReached end @@ -319,10 +329,9 @@ end function TASK.MenuAction( Parameter ) trace.menu("TASK","MenuAction") - trace.l( "TASK", "MenuAction", { Parameter } ) + trace.l( "TASK", "MenuAction" ) Parameter.ReferenceTask.ExecuteStage = _TransportExecuteStage.EXECUTING - Parameter.ReferenceTask.CargoName = Parameter.CargoName - + Parameter.ReferenceTask.Cargo = Parameter.CargoTask end function TASK:StageExecute() diff --git a/Moose/Trace.lua b/Moose/Trace.lua index 2d3477242..876afb947 100644 --- a/Moose/Trace.lua +++ b/Moose/Trace.lua @@ -62,6 +62,7 @@ trace.names.FollowPlayers = false trace.names.AddPlayerFromUnit = false trace.names.FromCarrier = false trace.names.OnDeadOrCrash = false + trace.classes.CLEANUP = false trace.cache = {} @@ -249,8 +250,9 @@ end trace.i = function(object, variable) local info = debug.getinfo( 2, "nl" ) - if info.name ~= trace.nametrace then - trace.nametrace = info.name + trace.nametrace = info.name + if trace.nametrace == nil then + trace.nametrace = "function" end if trace.names.all or trace.tracefunction( trace.nametrace ) or trace.classes[ object ] then local objecttrace = "" diff --git a/Moose/Unit.lua b/Moose/Unit.lua new file mode 100644 index 000000000..eb5eb9713 --- /dev/null +++ b/Moose/Unit.lua @@ -0,0 +1,58 @@ +--- UNIT Classes +-- @classmod UNIT + +Include.File( "Routines" ) +Include.File( "Base" ) +Include.File( "Message" ) + +UNITS = {} + + +UNIT = { + ClassName="UNIT", + } + +function UNIT:New( _Unit ) + local self = BASE:Inherit( self, BASE:New() ) + self:T( _Unit:getName() ) + + self._Unit = _Unit + self.UnitName = _Unit:getName() + self.UnitID = _Unit:getID() + + return self +end + +function UNIT:GetCallSign() + self:T( self.UnitName ) + + return self._Unit:getCallsign() +end + +function UNIT:GetPositionVec3() + self:T( self.UnitName ) + + local UnitPos = self._Unit:getPosition().p + + self:T( UnitPos ) + return UnitPos +end + +function UNIT:OtherUnitInRadius( AwaitUnit, Radius ) + self:T( { self.UnitName, AwaitUnit.UnitName, Radius } ) + + local UnitPos = self:GetPositionVec3() + local AwaitUnitPos = AwaitUnit:GetPositionVec3() + + if (((UnitPos.x - AwaitUnitPos.x)^2 + (UnitPos.z - AwaitUnitPos.z)^2)^0.5 <= Radius) then + self:T( "true" ) + return true + else + self:T( "false" ) + return false + end + + self:T( "false" ) + return false +end + diff --git a/Moose/Zone.lua b/Moose/Zone.lua new file mode 100644 index 000000000..a4f5fe113 --- /dev/null +++ b/Moose/Zone.lua @@ -0,0 +1,57 @@ +--- ZONE Classes +-- @classmod ZONE + +Include.File( "Routines" ) +Include.File( "Base" ) +Include.File( "Message" ) + +ZONES = {} + + +ZONE = { + ClassName="ZONE", + } + +function ZONE:New( ZoneName ) +trace.f( self.ClassName, ZoneName ) + + local self = BASE:Inherit( self, BASE:New() ) + + local Zone = trigger.misc.getZone( ZoneName ) + + if not Zone then + error( "Zone " .. ZoneName .. " does not exist." ) + return nil + end + + self.Zone = Zone + self.ZoneName = ZoneName + + return self +end + +function ZONE:GetRandomPoint() +trace.f( self.ClassName, self.ZoneName ) + + local Point = {} + + local Zone = trigger.misc.getZone( self.ZoneName ) + + Point.x = Zone.point.x + math.random( Zone.radius * -1, Zone.radius ) + Point.y = Zone.point.z + math.random( Zone.radius * -1, Zone.radius ) + + trace.i( self.ClassName, { Zone } ) + trace.i( self.ClassName, { Point } ) + + return Point +end + +function ZONE:GetRadius() +trace.f( self.ClassName, self.ZoneName ) + + local Zone = trigger.misc.getZone( self.ZoneName ) + + trace.i( self.ClassName, { Zone } ) + + return Zone.radius +end diff --git a/Test Missions/MOOSE_Pickup_Test.lua b/Test Missions/MOOSE_Pickup_Test.lua new file mode 100644 index 000000000..dfe4ff840 --- /dev/null +++ b/Test Missions/MOOSE_Pickup_Test.lua @@ -0,0 +1,126 @@ +Include.File( "Mission" ) +Include.File( "Client" ) +Include.File( "DeployTask" ) +Include.File( "PickupTask" ) +Include.File( "DestroyGroupsTask" ) +Include.File( "DestroyRadarsTask" ) +Include.File( "DestroyUnitTypesTask" ) +Include.File( "GoHomeTask" ) +Include.File( "Spawn" ) +Include.File( "Movement" ) +Include.File( "Sead" ) +Include.File( "CleanUp" ) + +do + local Mission = MISSION:New( 'Pickup', 'Operational', 'Pickup Troops', 'NATO' ) + + Mission:AddClient( CLIENT:New( 'DE Pickup Test 1' ):Transport() ) + Mission:AddClient( CLIENT:New( 'DE Pickup Test 2' ):Transport() ) + + local CargoTable = {} + + local EngineerNames = { "Alpha", "Beta", "Gamma", "Delta", "Theta" } + + Cargo_Pickup_Zone_1 = CARGO_ZONE:New( 'Pickup Zone 1', 'DE Communication Center 1' ):BlueSmoke() + Cargo_Pickup_Zone_2 = CARGO_ZONE:New( 'Pickup Zone 2', 'DE Communication Center 2' ):RedSmoke() + + for CargoItem = 1, 2 do + CargoTable[CargoItem] = CARGO_GROUP:New( 'Engineers', 'Team ' .. EngineerNames[CargoItem], math.random( 70, 100 ) * 3, 'DE Infantry', Cargo_Pickup_Zone_1 ) + end + + for CargoItem = 3, 5 do + CargoTable[CargoItem] = CARGO_GROUP:New( 'Engineers', 'Team ' .. EngineerNames[CargoItem], math.random( 70, 100 ) * 3, 'DE Infantry', Cargo_Pickup_Zone_2 ) + end + + --Cargo_Package = CARGO_INVISIBLE:New( 'Letter', 0.1, 'DE Secret Agent', 'Pickup Zone Package' ) + --Cargo_Goods = CARGO_STATIC:New( 'Goods', 20, 'Goods', 'Pickup Zone Goods', 'DE Collection Point' ) + --Cargo_SlingLoad = CARGO_SLING:New( 'Basket', 40, 'Basket', 'Pickup Zone Sling Load', 'DE Cargo Guard' ) + + + -- Assign the Pickup Task + local PickupTask = PICKUPTASK:New( 'Engineers', CLIENT.ONBOARDSIDE.LEFT ) + PickupTask:FromZone( Cargo_Pickup_Zone_1 ) + PickupTask:FromZone( Cargo_Pickup_Zone_2 ) + PickupTask:InitCargo( CargoTable ) + PickupTask:SetGoalTotal( 3 ) + Mission:AddTask( PickupTask, 1 ) + + + Cargo_Deploy_Zone_1 = CARGO_ZONE:New( 'Deploy Zone 1', 'DE Communication Center 3' ):RedFlare() + Cargo_Deploy_Zone_2 = CARGO_ZONE:New( 'Deploy Zone 2', 'DE Communication Center 4' ):WhiteFlare() + + -- Assign the Pickup Task + local DeployTask = DEPLOYTASK:New( 'Engineers' ) + DeployTask:ToZone( Cargo_Deploy_Zone_1 ) + DeployTask:ToZone( Cargo_Deploy_Zone_2 ) + DeployTask:SetGoalTotal( 3 ) + Mission:AddTask( DeployTask, 2 ) + + MISSIONSCHEDULER.AddMission( Mission ) +end + +do + local Mission = MISSION:New( 'Deliver secret letter', 'Operational', 'Pickup letter to the commander.', 'NATO' ) + + Mission:AddClient( CLIENT:New( 'BE Package Test 1' ):Transport() ) + Mission:AddClient( CLIENT:New( 'BE Package Test 2' ):Transport() ) + Mission:AddClient( CLIENT:New( 'DE Pickup Test 1' ):Transport() ) + Mission:AddClient( CLIENT:New( 'DE Pickup Test 2' ):Transport() ) + + Package_Pickup_Zone = CARGO_ZONE:New( 'Package Pickup Zone', 'DE Guard' ):GreenSmoke() + + Cargo_Package = CARGO_PACKAGE:New( 'Letter', 'Letter to Command', 0.1, 'DE Guard' ) + --Cargo_Goods = CARGO_STATIC:New( 'Goods', 20, 'Goods', 'Pickup Zone Goods', 'DE Collection Point' ) + --Cargo_SlingLoad = CARGO_SLING:New( 'Basket', 40, 'Basket', 'Pickup Zone Sling Load', 'DE Cargo Guard' ) + + + -- Assign the Pickup Task + local PickupTask = PICKUPTASK:New( 'Letter', CLIENT.ONBOARDSIDE.FRONT ) + PickupTask:FromZone( Package_Pickup_Zone ) + PickupTask:InitCargo( { Cargo_Package } ) + PickupTask:SetGoalTotal( 1 ) + Mission:AddTask( PickupTask, 1 ) + + + Package_Deploy_Zone = CARGO_ZONE:New( 'Package Deploy Zone', 'DE Secret Car' ):GreenFlare() + + -- Assign the Pickup Task + local DeployTask = DEPLOYTASK:New( 'Letter' ) + DeployTask:ToZone( Package_Deploy_Zone ) + DeployTask:SetGoalTotal( 1 ) + Mission:AddTask( DeployTask, 2 ) + + MISSIONSCHEDULER.AddMission( Mission ) +end + +do + local Mission = MISSION:New( 'Sling load Cargo', 'Operational', 'Sling Load Cargo to Deploy Zone.', 'NATO' ) + + Mission:AddClient( CLIENT:New( 'Sling Load Test Client 1' ):Transport() ) + Mission:AddClient( CLIENT:New( 'Sling Load Test Client 2' ):Transport() ) + + Sling_Load_Pickup_Zone = CARGO_ZONE:New( 'Sling Load Pickup Zone', 'Sling Load Guard' ):RedSmoke() + + Cargo_Sling_Load = CARGO_SLINGLOAD:New( 'Sling', 'Food Boxes', 200, 'Sling Load Pickup Zone', 'Sling Load Guard', country.id.USA ) + --Cargo_Goods = CARGO_STATIC:New( 'Goods', 20, 'Goods', 'Pickup Zone Goods', 'DE Collection Point' ) + --Cargo_SlingLoad = CARGO_SLING:New( 'Basket', 40, 'Basket', 'Pickup Zone Sling Load', 'DE Cargo Guard' ) + + + -- Assign the Pickup Task + local PickupTask = PICKUPTASK:New( 'Sling', CLIENT.ONBOARDSIDE.FRONT ) + PickupTask:FromZone( Sling_Load_Pickup_Zone ) + PickupTask:InitCargo( { Cargo_Sling_Load } ) + PickupTask:SetGoalTotal( 1 ) + Mission:AddTask( PickupTask, 1 ) + + MISSIONSCHEDULER.AddMission( Mission ) +end + + + +-- MISSION SCHEDULER STARTUP +MISSIONSCHEDULER.Start() +MISSIONSCHEDULER.ReportMenu() +MISSIONSCHEDULER.ReportMissionsHide() + +env.info( "Test Mission loaded" ) diff --git a/Test Missions/MOOSE_Pickup_Test.miz b/Test Missions/MOOSE_Pickup_Test.miz new file mode 100644 index 000000000..b200f5ef2 Binary files /dev/null and b/Test Missions/MOOSE_Pickup_Test.miz differ diff --git a/Test Missions/MOOSE_Spawn_Test.lua b/Test Missions/MOOSE_Spawn_Test.lua new file mode 100644 index 000000000..e0ef01e5b --- /dev/null +++ b/Test Missions/MOOSE_Spawn_Test.lua @@ -0,0 +1,15 @@ +Include.File( "Spawn" ) + +SpawnTest = SPAWN:New( 'TEST' ):Schedule( 1, 1, 15, 0.4 ):Repeat() + +SpawnTestPlane = SPAWN:New( 'TESTPLANE' ):Schedule( 1, 1, 15, 0.4 ):RepeatOnLanding() + +SpawnTestShipPlane = SPAWN:New( 'SHIPPLANE' ):Schedule( 1, 1, 15, 0.4 ):RepeatOnLanding() + +SpawnTestShipHeli = SPAWN:New( 'SHIPHELI' ):Schedule( 1, 1, 15, 0.4 ):RepeatOnLanding() + +SpawnCH53E = SPAWN:New( 'VEHICLE' ) + + +SpawnTestHelicopterCleanUp = SPAWN:New( "TEST_HELI_CLEANUP" ):Limit( 3, 100 ):Schedule( 10, 0 ):RandomizeRoute( 1, 1, 1000 ):CleanUp( 180 ) +SpawnTestVehiclesCleanUp = SPAWN:New( "TEST_AAA_CLEANUP" ):Limit( 3, 100 ):Schedule( 10, 0 ):RandomizeRoute( 1, 1, 1000 ) \ No newline at end of file diff --git a/Test Missions/MOOSE_Spawn_Test.miz b/Test Missions/MOOSE_Spawn_Test.miz new file mode 100644 index 000000000..fc2b4022b Binary files /dev/null and b/Test Missions/MOOSE_Spawn_Test.miz differ