From 6019a2a170a2d60ad8970964d65be6c03272d2c4 Mon Sep 17 00:00:00 2001 From: svenvandevelde Date: Sun, 14 Feb 2016 12:03:17 +0100 Subject: [PATCH] Got now back a good version working with CARGO now as a separate object. - Sling load working but dealing with the DCS bugs. - Normal CARGO pickup and deployment is working now, but needs to be further optimized... --- Moose/Cargo.lua | 292 ++++++++++++++++++++++++++++++++++-------- Moose/Client.lua | 4 +- Moose/Message.lua | 36 ++++++ Moose/Mission.lua | 8 +- Moose/PickupTask.lua | 7 +- Moose/Routines.lua | 192 +++++++++++++++------------- Moose/Spawn.lua | 12 +- Moose/Stage.lua | 294 +++++++++++++++++++++++++++++-------------- Moose/Task.lua | 4 +- 9 files changed, 600 insertions(+), 249 deletions(-) diff --git a/Moose/Cargo.lua b/Moose/Cargo.lua index 53912c207..d7ed635f3 100644 --- a/Moose/Cargo.lua +++ b/Moose/Cargo.lua @@ -33,39 +33,85 @@ CARGO_ZONE = { } } -function CARGO_ZONE:New( CargoZoneName, CargoHostGroupName ) -trace.f( self.ClassName, { CargoZoneName, CargoHostGroupName } ) +function CARGO_ZONE:New( CargoZoneName, CargoHostName ) +trace.f( self.ClassName, { CargoZoneName, CargoHostName } ) local self = BASE:Inherit( self, BASE:New() ) self.CargoZoneName = CargoZoneName self.CargoZone = trigger.misc.getZone( CargoZoneName ) - if CargoHostGroupName then - self.CargoHostGroupName = CargoHostGroupName - self.CargoHostSpawn = SPAWN:New( CargoHostGroupName ) + if CargoHostName then + self.CargoHostName = CargoHostName + self.CargoHostSpawn = SPAWN:New( CargoHostName ) end return self end function CARGO_ZONE:Spawn() -trace.f( self.ClassName ) +trace.f( self.ClassName, CargoHostSpawn ) if self.CargoHostSpawn then local CargoHostGroup = Group.getByName( self.CargoHostSpawn:SpawnGroupName() ) if CargoHostGroup then if not CargoHostGroup:isExist() then - self.CargoHostSpawn:ReSpawn( self.CargoHostSpawn:SpawnGroupName() ) + self.CargoHostSpawn:ReSpawn() end else - self.CargoHostSpawn:ReSpawn( self.CargoHostSpawn:SpawnGroupName() ) + 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 ) +trace.f( self.ClassName ) + + 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 + HostMessage = HostMessage .. "\n - " .. Cargo.CargoName + IsCargo = true + end + end + + if not IsCargo then + HostMessage = HostMessage .. "No Cargo Available." + end + + Client:Message( RouteMessage, self.MSG.TIME, Mission.Name .. "/StageHosts." .. SignalUnitTypeName, SignalUnitTypeName .. ": Reporting Cargo", 10 ) + end +end + function CARGO_ZONE:Signal() trace.f( self.ClassName ) @@ -73,21 +119,17 @@ trace.f( self.ClassName ) if self.SignalType then - if self.CargoHostGroupName then + if self.CargoHostName then -- A Host has been given, signal the host - local SignalUnit = Group.getByName( self.CargoHostSpawn:SpawnGroupName() ) - if SignalUnit == nil then - SignalUnit = StaticObject.getByName( self.CargoHostGroupName ) - else - SignalUnit = SignalUnit:getUnits()[1] - end - if SignalUnit ~= nil then + local SignalUnit = self:GetHostUnit() + + if SignalUnit then trace.i( self.ClassName, 'Signalling Unit' ) local SignalVehiclePos = SignalUnit:getPosition().p - SignalVehiclePos.y = SignalVehiclePos.y + 10 + SignalVehiclePos.y = SignalVehiclePos.y + 2 if self.SignalType.ID == CARGO_ZONE.SIGNAL.TYPE.SMOKE.ID then @@ -105,7 +147,7 @@ trace.f( self.ClassName ) else local CurrentPosition = { x = self.CargoZone.point.x, y = self.CargoZone.point.z } - self.CargoZone.point.y = land.getHeight( CurrentPosition ) + 10 + self.CargoZone.point.y = land.getHeight( CurrentPosition ) + 2 if self.SignalType.ID == CARGO_ZONE.SIGNAL.TYPE.SMOKE.ID then @@ -210,9 +252,9 @@ end function CARGO_ZONE:GetCargoHostGroup() trace.f( self.ClassName ) - local CargoHost = Group.getByName( self.CargoHostSpawn:SpawnGroupName() ) - if CargoHost and CargoHost:isExist() then - return CargoHost + local CargoHostGroup = Group.getByName( self.CargoHostSpawn:SpawnGroupName() ) + if CargoHostGroup and CargoHostGroup:isExist() then + return CargoHostGroup end return nil @@ -318,6 +360,17 @@ trace.f(self.ClassName ) return OnBoarded end +function CARGO:IsLandingRequired() +trace.f( self.ClassName ) + return true +end + +function CARGO:IsSlingLoad() +trace.f( self.ClassName ) + return false +end + + function CARGO:StatusNone() trace.f(self.ClassName ) @@ -541,7 +594,7 @@ trace.f( self.ClassName ) local Cargo = Client:RemoveCargo( self ) - return Cargo + return self end @@ -550,13 +603,15 @@ CARGO_PACKAGE = { } -function CARGO_PACKAGE:New( CargoType, CargoName, CargoWeight, InitClientGroupName ) -trace.f( self.ClassName, { CargoType, CargoName, CargoWeight, InitClientGroupName } ) +function CARGO_PACKAGE:New( CargoType, CargoName, CargoWeight, CargoHostName ) +trace.f( self.ClassName, { CargoType, CargoName, CargoWeight, CargoHostName } ) -- Arrange meta tables local self = BASE:Inherit( self, CARGO:New( CargoType, CargoName, CargoWeight ) ) - self.InitClientGroupName = InitClientGroupName .. '#' -- to fix + self.CargoHostName = CargoHostName + + self.CargoHostSpawn = SPAWN:New( self.CargoHostName ) CARGOS[self.CargoName] = self @@ -572,6 +627,8 @@ trace.f( self.ClassName ) local SpawnCargo = true + trace.i( self.ClassName, self.CargoHostName ) + if self.Client and self.Client:ClientGroup() then trace.i( self.ClassName, 'There is a Client ' .. self.Client.ClientName ) if self.Client:FindCargo( self.CargoName ) then @@ -581,10 +638,14 @@ trace.f( self.ClassName ) end end else - if self.InitClientGroupName then - local ClientGroup = Group.getByName( self.InitClientGroupName ) - if ClientGroup and ClientGroup:isExist() then - self.Client = CLIENT:New( self.InitClientGroupName, '' ) + if self.CargoHostName then + local CargoHostGroup = Group.getByName( self.CargoHostName ) + if not CargoHostGroup then + self.CargoHostSpawn:ReSpawn() + end + local CargoHostGroup = Group.getByName( self.CargoHostName ) + if CargoHostGroup and CargoHostGroup:isExist() then + self.Client = CLIENT:New( self.CargoHostGroup, '' ) end end end @@ -601,8 +662,8 @@ trace.f( self.ClassName ) local Near = false - trace.i( self.ClassName, self.Client.ClientName ) if self.Client and self.Client:ClientGroup():getName() then + trace.i( self.ClassName, self.Client.ClientName ) trace.i( self.ClassName, 'Client Exists.' ) trace.i( self.ClassName, 'self.Client:ClientGroup():getName() = ' .. self.Client:ClientGroup():getName() ) @@ -633,7 +694,7 @@ trace.f(self.ClassName ) local CarrierPosMoveAway = ClientUnit:getPoint() local CargoHostGroup = self.Client:ClientGroup() - local CargoHostGroupName = self.Client:ClientGroup():getName() + local CargoHostName = self.Client:ClientGroup():getName() local CargoHostUnits = CargoHostGroup:getUnits() local CargoPos = CargoHostUnits[1]:getPoint() @@ -702,9 +763,9 @@ trace.f(self.ClassName ) Points[#Points+1] = routines.ground.buildWP( CarrierPosMoveAway, "Cone", 10 ) end - trace.i( self.ClassName, "Routing " .. CargoHostGroupName ) + trace.i( self.ClassName, "Routing " .. CargoHostName ) - routines.scheduleFunction( routines.goRoute, { CargoHostGroupName, Points}, timer.getTime() + 4 ) + routines.scheduleFunction( routines.goRoute, { CargoHostName, Points}, timer.getTime() + 4 ) return Valid @@ -750,29 +811,154 @@ trace.f( self.ClassName ) end +CARGO_SLINGLOAD = { + ClassName = "CARGO_SLINGLOAD" +} + + +function CARGO_SLINGLOAD:New( CargoType, CargoName, CargoWeight, CargoZone, CargoHostName, CargoCountryID ) +trace.f( self.ClassName, { CargoType, CargoName, CargoWeight, CargoZone, CargoHostName, CargoCountryID } ) + + -- Arrange meta tables + local self = BASE:Inherit( self, CARGO:New( CargoType, CargoName, CargoWeight ) ) + + 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() +trace.f( self.ClassName ) + return false +end + +function CARGO_SLINGLOAD:IsSlingLoad() +trace.f( self.ClassName ) + return true +end + + +function CARGO_SLINGLOAD:Spawn() +trace.f( self.ClassName ) + + 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 ) + + trace.i( self.ClassName, "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:IsInLandingZone( Client, LandingZone ) +trace.f( self.ClassName ) + + 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 ) +trace.f(self.ClassName ) + + local Valid = true + + + return Valid + +end + + +function CARGO_SLINGLOAD:OnBoarded( Client, LandingZone ) +trace.f(self.ClassName ) + + 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 ) +trace.f( self.ClassName ) + + trace.i( self.ClassName, 'self.CargoName = ' .. self.CargoName ) + trace.i( self.ClassName, 'self.CargoGroupName = ' .. self.CargoGroupName ) + + self:StatusUnLoaded() + local Cargo = Client:RemoveCargo( self ) + + + return Cargo +end + --[[-- Internal Table to understand the form of the CARGO. @table CARGO_TRANSPORT --]] CARGO_TRANSPORT = { UNIT = 1, SLING = 2, STATIC = 3, INVISIBLE = 4 } ---[[-- - 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.STATIC }, - INFANTRY = { ID = 4, TEXT = "Infantry", TRANSPORT = CARGO_TRANSPORT.UNIT }, - ENGINEERS = { ID = 5, TEXT = "Engineers", TRANSPORT = CARGO_TRANSPORT.UNIT }, - PACKAGE = { ID = 6, TEXT = "Package", TRANSPORT = CARGO_TRANSPORT.INVISIBLE }, - CARGO = { ID = 7, TEXT = "Cargo", TRANSPORT = CARGO_TRANSPORT.STATIC }, -} diff --git a/Moose/Client.lua b/Moose/Client.lua index eb6131080..f667d43e5 100644 --- a/Moose/Client.lua +++ b/Moose/Client.lua @@ -207,11 +207,11 @@ trace.f(self.ClassName, { Cargo.CargoName } ) if Valid then trace.i( "CLIENT", "RemoveCargo: CargoName = " .. Cargo.CargoName ) - local CargoNew = self._Cargos[Cargo.CargoName] + --local CargoNew = self._Cargos[Cargo.CargoName] self._Cargos[Cargo.CargoName] = nil end - return CargoNew + return Cargo end diff --git a/Moose/Message.lua b/Moose/Message.lua index f10c54fdd..44c128c69 100644 --- a/Moose/Message.lua +++ b/Moose/Message.lua @@ -84,6 +84,42 @@ trace.f(self.ClassName ) 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 a5a5b671a..fcb0d8683 100644 --- a/Moose/Mission.lua +++ b/Moose/Mission.lua @@ -102,7 +102,7 @@ end function MISSION:Ongoing() trace.f(self.ClassName) self.MissionStatus = "ONGOING" - self:StatusToClients() + --self:StatusToClients() end --- Returns if a Mission is pending. @@ -136,7 +136,7 @@ end --- Send the status of the MISSION to all Clients. function MISSION:StatusToClients() trace.f(self.ClassName) - if timer.getTime() >= self.MissionReportTrigger then + 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 @@ -414,7 +414,7 @@ trace.scheduled("MISSIONSCHEDULER","Scheduler") -- loop through the missions in the TransportTasks for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do - env.info( "Mission: " .. MissionName ) + trace.i( "MISSIONSCHEDULER", MissionName ) if not Mission:IsCompleted() then @@ -423,7 +423,7 @@ trace.scheduled("MISSIONSCHEDULER","Scheduler") for ClientID, Client in pairs( Mission._Clients ) do - env.info( "Client: " .. Client.ClientName ) + 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 diff --git a/Moose/PickupTask.lua b/Moose/PickupTask.lua index a3623580f..2b4e1b3ee 100644 --- a/Moose/PickupTask.lua +++ b/Moose/PickupTask.lua @@ -28,9 +28,10 @@ trace.f(self.ClassName) Child.CargoType = CargoType Child.GoalVerb = CargoType .. " " .. Child.GoalVerb Child.OnBoardSide = OnBoardSide + Child.IsLandingRequired = false -- required to decide whether the client needs to land or not + Child.IsSlingLoad = false -- Indicates whether the cargo is a sling load cargo Child.Stages = { STAGE_CARGO_INIT:New(), STAGE_CARGO_LOAD:New(), STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGELANDING:New(), STAGELANDED:New(), STAGELOAD:New(), STAGEDONE:New() } Child.SetStage( Child, 1 ) - end return Child @@ -91,10 +92,10 @@ trace.f( self.ClassName ) if not Client._Menus[Cargo.CargoType].PickupMenu then Client._Menus[Cargo.CargoType].PickupMenu = missionCommands.addSubMenuForGroup( Client:ClientGroup():getID(), - self.TEXT[1], + self.TEXT[1] .. " " .. Cargo.CargoType, nil ) - trace.i( self.ClassName, 'Added PickupMenu' .. self.TEXT[1] ) + trace.i( self.ClassName, 'Added PickupMenu: ' .. self.TEXT[1] .. " " .. Cargo.CargoType ) end if Client._Menus[Cargo.CargoType].PickupSubMenus == nil then diff --git a/Moose/Routines.lua b/Moose/Routines.lua index 427447b7c..a40a8c9df 100644 --- a/Moose/Routines.lua +++ b/Moose/Routines.lua @@ -25,6 +25,91 @@ 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 + 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 routines.utils.toDegree = function(angle) @@ -137,91 +222,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 +280,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 @@ -1829,6 +1832,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 = {} } @@ -2362,7 +2382,7 @@ trace.f() --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - return CarrierHeight - LandHeight + return UnitHeight - LandHeight end diff --git a/Moose/Spawn.lua b/Moose/Spawn.lua index f728d108e..e6d06bd53 100644 --- a/Moose/Spawn.lua +++ b/Moose/Spawn.lua @@ -332,8 +332,8 @@ trace.f( self.ClassName, { CarrierGroup, TargetZonePrefix, NewGroupName, LateAct 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) + 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 ) trace.i( self.ClassName, "RouteCount = " .. RouteCount ) @@ -379,11 +379,13 @@ end -- @treturn string SpawnGroupName function SPAWN:SpawnGroupName( SpawnNumber ) trace.f("Spawn", SpawnNumber ) - + if SpawnNumber then - return string.format( self.SpawnPrefix .. '#%03d', SpawnNumber ) + trace.i( self.ClassName, string.format( '%s#%03d', self.SpawnPrefix, SpawnNumber ) ) + return string.format( '%s#%03d', self.SpawnPrefix, SpawnNumber ) else - return string.format( self.SpawnPrefix .. '#' ) + trace.i( self.ClassName, self.SpawnPrefix ) + return self.SpawnPrefix end end diff --git a/Moose/Stage.lua b/Moose/Stage.lua index 9b6516a13..7dfc90f99 100644 --- a/Moose/Stage.lua +++ b/Moose/Stage.lua @@ -112,9 +112,9 @@ function STAGESTART:Execute( Mission, Client, Task ) trace.f(self.ClassName) 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, 15, 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 .. '.', 15, Mission.Name .. "/Stage", "Mission Command: Tasking" ) end self.StageStartTime = timer.getTime() return Valid @@ -155,7 +155,7 @@ trace.f(self.ClassName) LoadCargo:Load( Client ) end - if Client:IsTransport() then + if Mission.MissionReportFlash and Client:IsTransport() then Client:ShowCargo() end @@ -212,7 +212,7 @@ end STAGEROUTE = { ClassName = "STAGEROUTE", - MSG = { ID = "Route", TIME = 1 }, + MSG = { ID = "Route", TIME = 5 }, Frequency = STAGE.FREQUENCY.REPEAT, Name = "Route" } @@ -235,9 +235,9 @@ trace.f(self.ClassName) for LandingZoneID, LandingZoneName in pairs( Task.LandingZones.LandingZoneNames ) 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 ) + Client:Message( RouteMessage, self.MSG.TIME, Mission.Name .. "/StageRoute", "Co-Pilot: Route", 20 ) - if Client:IsTransport() then + if Mission.MissionReportFlash and Client:IsTransport() then Client:ShowCargo() end @@ -269,6 +269,8 @@ trace.f(self.ClassName) return 0 end + + STAGELANDING = { ClassName = "STAGELANDING", MSG = { ID = "Landing", TIME = 10 }, @@ -287,45 +289,83 @@ end function STAGELANDING:Execute( Mission, Client, Task ) trace.f(self.ClassName) - Client:Message( 'We have arrived at ' .. Task.CurrentLandingZoneName .. '. Land the helicopter to ' .. Task.TEXT[1] .. ' the ' .. Task.CargoType .. '.', - self.MSG.TIME, Mission.Name .. "/StageLanding", "Co-Pilot: Landing", 10 ) - + 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 + trace.i( self.ClassName, "Task for cargo " .. Cargo.CargoType .. " requires landing.") + Task.IsLandingRequired = true + end + + if Cargo:IsSlingLoad() then + trace.i( self.ClassName, "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) - if routines.IsUnitInZones( Client:ClientUnit(), Task.CurrentLandingZoneName ) then - -- check if the Client is in the landing zone - trace.i( self.ClassName, Task.LandingZones.LandingZoneNames ) - Task.CurrentLandingZoneName = routines.IsUnitInZones( Client:ClientUnit(), Task.LandingZones.LandingZoneNames ) + Task.CurrentLandingZoneName = routines.IsUnitInZones( Client:ClientUnit(), Task.LandingZones.LandingZoneNames ) + if Task.CurrentLandingZoneName then + + -- Client is in de landing zone. + trace.i( self.ClassName, Task.CurrentLandingZoneName ) - if Task.CurrentLandingZoneName then + Task.CurrentLandingZone = Task.LandingZones.LandingZones[Task.CurrentLandingZoneName].CargoZone + Task.CurrentCargoZone = Task.LandingZones.LandingZones[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 + 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:ClientUnit():inAir() then - else + if Task.IsLandingRequired and Client:ClientUnit():inAir() then return 0 end @@ -349,10 +389,15 @@ 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 .. '.', self.MSG.TIME, Mission.Name .. "/StageLanded", "Co-Pilot: Landed" ) - if not self.MenusAdded then - Task:RemoveCargoMenus( Client ) - Task:AddCargoMenus( Client, CARGOS, 250 ) + + 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 @@ -361,25 +406,27 @@ end function STAGELANDED:Validate( Mission, Client, Task ) trace.f(self.ClassName) - if routines.IsUnitInZones( Client:ClientUnit(), Task.CurrentLandingZoneName ) then - else - Task.Signalled = false - Task:RemoveCargoMenus( Client ) - return -2 - end + if not routines.IsUnitInZones( Client:ClientUnit(), Task.CurrentLandingZoneName ) then + trace.i( self.ClassName, "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:ClientUnit():inAir() then - else - Task.Signalled = false - return -1 - end + if Task.IsLandingRequired and Client:ClientUnit():inAir() then + trace.i( self.ClassName, "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 = { @@ -417,7 +464,9 @@ trace.f(self.ClassName) if Task.Cargo:UnLoad( Client, TargetZoneName ) then Task.ExecuteStage = _TransportExecuteStage.SUCCESS - Client:ShowCargo() + if Mission.MissionReportFlash then + Client:ShowCargo() + end end end @@ -470,67 +519,122 @@ end function STAGELOAD:Execute( Mission, Client, Task ) trace.f(self.ClassName) - 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", "Co-Pilot: Load" ) + + 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 - Task.Cargo:OnBoard( Client, Task.CurrentCargoZone, Task.OnBoardSide ) - Task.ExecuteStage = _TransportExecuteStage.EXECUTING + -- Route the cargo to the Carrier + Task.Cargo:OnBoard( Client, Task.CurrentCargoZone, Task.OnBoardSide ) + Task.ExecuteStage = _TransportExecuteStage.EXECUTING + else + Task.ExecuteStage = _TransportExecuteStage.EXECUTING + end end function STAGELOAD:Executing( Mission, Client, Task ) trace.f(self.ClassName) - -- If the Cargo is ready to be loaded, load it into the Client. - - trace.i(self.ClassName, Task.Cargo.CargoName) - - if Task.Cargo:OnBoarded( Client, Task.CurrentCargoZone ) then + -- If the Cargo is ready to be loaded, load it into the Client. - -- 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 .. "/StageLoad", "Co-Pilot: Load" ) - Task.ExecuteStage = _TransportExecuteStage.SUCCESS - Client:ShowCargo() + if not Task.IsSlingLoad then + trace.i(self.ClassName, Task.Cargo.CargoName) + + if Task.Cargo:OnBoarded( Client, Task.CurrentCargoZone ) 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 + + if Mission.MissionReportFlash then + Client:ShowCargo() + end + end + else + 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 + trace.i( self.ClassName, "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 + trace.i( self.ClassName, "Cargo not found in the DCS simulator." ) + end + end + end end end function STAGELOAD:Validate( Mission, Client, Task ) trace.f(self.ClassName) - if routines.IsUnitInZones( Client:ClientUnit(), Task.CurrentLandingZoneName ) then + + trace.i( self.ClassName, "Task.CurrentLandingZoneName = " .. Task.CurrentLandingZoneName ) + + if not Task.IsSlingLoad then + if not routines.IsUnitInZones( Client:ClientUnit(), 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:ClientUnit():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.Cargo:StatusLoaded() + 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 - 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. ", - _TransportStageMsgTime.DONE, Mission.Name .. "/StageSuccess", "Co-Pilot: Load" ) - return 1 - end - - if not Client:ClientUnit():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. 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 - Task.Cargo:StatusLoaded() - Task:RemoveCargoMenus( Client ) - Client:Message( 'Co-Pilot: The ' .. Task.CargoType .. ' have been sucessfully ' .. Task.TEXT[3] .. ' within the landing zone.', - _TransportStageMsgTime.DONE, Mission.Name .. "/StageLoaded", "Co-Pilot: Load" ) - Task.MissionTask:AddGoalCompletion( Task.MissionTask.GoalVerb, Task.CargoName, 1 ) - return 1 + 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 diff --git a/Moose/Task.lua b/Moose/Task.lua index 2d0f753f8..1dce0aab6 100644 --- a/Moose/Task.lua +++ b/Moose/Task.lua @@ -128,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.