From d6aa7ec17ca3957cc821c953b6956134b30ab205 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 10 Feb 2023 10:45:23 +0100 Subject: [PATCH 1/6] #Net * Initial Release --- Moose Development/Moose/Modules.lua | 1 + Moose Development/Moose/Wrapper/Net.lua | 298 ++++++++++++++++++++++++ Moose Setup/Moose.files | 1 + 3 files changed, 300 insertions(+) create mode 100644 Moose Development/Moose/Wrapper/Net.lua diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 46458f046..f2e5568c2 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -47,6 +47,7 @@ __Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Unit.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Weapon.lua' ) +__Moose.Include( 'Scripts/Moose/Wrapper/Net.lua' ) __Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' ) __Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' ) diff --git a/Moose Development/Moose/Wrapper/Net.lua b/Moose Development/Moose/Wrapper/Net.lua new file mode 100644 index 000000000..e822a176b --- /dev/null +++ b/Moose Development/Moose/Wrapper/Net.lua @@ -0,0 +1,298 @@ +--- **Wrapper** - DCS net functions. +-- +-- Encapsules **multiplayer** environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) +-- +-- === +-- +-- ### Author: **applevangelist** +-- +-- === +-- +-- @module Wrapper.Net +-- @image Utils_Profiler.jpg + +do +--- The NET class +-- @type NET +-- @field #string ClassName +-- @field #string Version +-- @extends Core.Base#BASE + +--- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) +-- +-- @field #NET +NET = { + ClassName = "NET", + Version = "0.0.2" +} + +--- Instantiate a new NET object. +-- @param #NET self +-- @return #NET self +function NET:New() + -- Inherit base. + local self = BASE:Inherit(self, BASE:New()) -- #NET + return self +end + +--- Send chat message. +-- @param #NET self +-- @param #string Message Message to send +-- @param #boolean ToAll (Optional) +-- @return #NET self +function NET:SendChat(Message,ToAll) + if Message then + net.send_chat(Message, ToAll) + end + return self +end + +--- Find the PlayerID by name +-- @param #NET self +-- @param #string Name The player name whose ID to find +-- @return #number PlayerID or nil +function NET:GetPlayerIdByName(Name) + local playerList = self:GetPlayerList() + for i=1,#playerList do + local playerName = net.get_name(i) + if playerName == Name then + return playerList[i] + end + end + return nil +end + +--- Find the PlayerID from a CLIENT object. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @return #number PlayerID or nil +function NET:GetPlayerIDFromClient(Client) + local name = Client:GetPlayerName() + local id = self:GetPlayerIdByName(name) + return id +end + +--- Send chat message to a specific player. +-- @param #NET self +-- @param #string Message The text message +-- @param Wrapper.Client#CLIENT ToClient Client receiving the message +-- @param Wrapper.Client#CLIENT FromClient (Optional) Client sending the message +-- @return #NET self +function NET:SendChatToPlayer(Message, ToClient, FromClient) + local PlayerId = self:GetPlayerIDFromClient(ToClient) + local FromId = self:GetPlayerIDFromClient(FromClient) + if Message and PlayerId and FromId then + net.send_chat_to(Message, tonumber(PlayerId) , tonumber(FromId)) + elseif Message and PlayerId then + net.send_chat_to(Message, tonumber(PlayerId)) + end + return self +end + +--- Load a specific mission. +-- @param #NET self +-- @param #string Path and Mission +-- @return #boolean success +-- @usage +-- mynet:LoadMission(lfs.writeDir() .. 'Missions\\' .. 'MyTotallyAwesomeMission.miz') +function NET:LoadMission(Path) + local outcome = false + if Path then + outcome = net.load_mission(Path) + end + return outcome +end + +--- Load next mission. Returns false if at the end of list. +-- @param #NET self +-- @return #boolean success +function NET:LoadNextMission() + local outcome = false + outcome = net.load_next_mission() + return outcome +end + +--- Return a table of players currently connected to the server. +-- @param #NET self +-- @return #table PlayerList +function NET:GetPlayerList() + local plist = nil + plist = net.get_player_list() + return plist +end + +--- Returns the playerID of the local player. Always returns 1 for server. +-- @param #NET self +-- @return #number ID +function NET:GetMyPlayerID() + return net.get_my_player_id() +end + +--- Returns the playerID of the server. Currently always returns 1. +-- @param #NET self +-- @return #number ID +function NET:GetServerID() + return net.get_server_id() +end + +--- Return a table of attributes for a given client. If optional attribute is present, only that value is returned. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client. +-- @param #string Attribute (Optional) The attribute to obtain. List see below. +-- @return #table PlayerInfo or nil if it cannot be found +-- @usage +-- Table holds these attributes: +-- +-- 'id' : playerID +-- 'name' : player name +-- 'side' : 0 - spectators, 1 - red, 2 - blue +-- 'slot' : slotID of the player or +-- 'ping' : ping of the player in ms +-- 'ipaddr': IP address of the player, SERVER ONLY +-- 'ucid' : Unique Client Identifier, SERVER ONLY +-- +function NET:GetPlayerInfo(Client,Attribute) + local PlayerID = self:GetPlayerIDFromClient(Client) + if PlayerID then + return net.get_player_info(tonumber(PlayerID), Attribute) + else + return nil + end +end + +--- Kicks a player from the server. Can display a message to the user. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @param #string Message (Optional) The message to send. +-- @return #boolean success +function NET:Kick(Client,Message) + local PlayerID = self:GetPlayerIDFromClient(Client) + if PlayerID and tonumber(PlayerID) ~= 1 then + return net.kick(tonumber(PlayerID), Message) + else + return false + end +end + +--- Return a statistic for a given client. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @param #number StatisticID The statistic to obtain +-- @return #number Statistic or nil +-- @usage +-- StatisticIDs are: +-- +-- net.PS_PING (0) - ping (in ms) +-- net.PS_CRASH (1) - number of crashes +-- net.PS_CAR (2) - number of destroyed vehicles +-- net.PS_PLANE (3) - ... planes/helicopters +-- net.PS_SHIP (4) - ... ships +-- net.PS_SCORE (5) - total score +-- net.PS_LAND (6) - number of landings +-- net.PS_EJECT (7) - of ejects +-- +-- mynet:GetPlayerStatistic(Client,7) -- return number of ejects +function NET:GetPlayerStatistic(Client,StatisticID) + local PlayerID = self:GetPlayerIDFromClient(Client) + local stats = StatisticID or 0 + if stats > 7 or stats < 0 then stats = 0 end + if PlayerID then + return net.get_stat(tonumber(PlayerID),stats) + else + return nil + end +end + +--- Return the name of a given client. Same a CLIENT:GetPlayerName(). +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @return #string Name or nil if not obtainable +function NET:GetName(Client) + local PlayerID = self:GetPlayerIDFromClient(Client) + if PlayerID then + return net.get_name(tonumber(PlayerID)) + else + return nil + end +end + +--- Returns the SideId and SlotId of a given client. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @return #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue +-- @return #number SlotID +function NET:GetSlot(Client) + local PlayerID = self:GetPlayerIDFromClient(Client) + if PlayerID then + local side,slot = net.get_slot(tonumber(PlayerID)) + return side,slot + else + return nil,nil + end +end + +--- Force the slot for a specific client. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @param #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue +-- @param #number SlotID Slot number +-- @return #boolean Success +function NET:ForceSlot(Client,SideID,SlotID) + local PlayerID = self:GetPlayerIDFromClient(Client) + if PlayerID then + return net.force_player_slot(tonumber(PlayerID), SideID, SlotID ) + else + return false + end +end + +--- Force a client back to spectators. +-- @param #NET self +-- @param Wrapper.Client#CLIENT Client The client +-- @return #boolean Succes +function NET:ReturnToSpectators(Client) + local outcome = self:ForceSlot(Client,0) + return outcome +end + +--- Converts a lua value to a JSON string. +-- @param #string Lua Anything lua +-- @return #table Json +function NET.Lua2Json(Lua) + return net.lua2json(Lua) +end + +--- Converts a JSON string to a lua value. +-- @param #string Json Anything JSON +-- @return #table Lua +function NET.Lua2Json(Json) + return net.json2lua(Json) +end + +--- Executes a lua string in a given lua environment in the game. +-- @param #NET self +-- @param #string State The state in which to execute - see below. +-- @param #string DoString The lua string to be executed. +-- @return #string Output +-- @usage +-- States are: +-- 'config': the state in which $INSTALL_DIR/Config/main.cfg is executed, as well as $WRITE_DIR/Config/autoexec.cfg - used for configuration settings +-- 'mission': holds current mission +-- 'export': runs $WRITE_DIR/Scripts/Export.lua and the relevant export API +function NET:DoStringIn(State,DoString) + return net.dostring_in(State,DoString) +end + +--- Write an "INFO" entry to the DCS log file, with the message Message. +-- @param #NET self +-- @param #string Message The message to be logged. +-- @return #NET self +function NET:Log(Message) + net.log(Message) + return self +end + +------------------------------------------------------------------------------- +-- End of NET +------------------------------------------------------------------------------- +end diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 6cd862e94..4b0666443 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -44,6 +44,7 @@ Wrapper/Airbase.lua Wrapper/Scenery.lua Wrapper/Marker.lua Wrapper/Weapon.lua +Wrapper/Net.lua Cargo/Cargo.lua Cargo/CargoUnit.lua From 713a5b067f4521b583d48b01dde8907348ac0a34 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 10 Feb 2023 11:40:15 +0100 Subject: [PATCH 2/6] #PLAYERTASKCONTROLLER * Added `AddAgentSet()` --- Moose Development/Moose/Ops/PlayerTask.lua | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index bc8bd2bb8..94901b479 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -96,7 +96,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.12" +PLAYERTASK.version="0.1.14" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -3526,6 +3526,23 @@ function PLAYERTASKCONTROLLER:AddAgent(Recce) return self end +--- [User] Add agent SET_GROUP to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Set#SET_GROUP RecceSet SET_GROUP of agents. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:AddAgentSet(RecceSet) + self:T(self.lid.."AddAgentSet") + if self.Intel then + local Set = RecceSet:GetAliveSet() + for _,_Recce in pairs(Set) do + self.Intel:AddAgent(_Recce) + end + else + self:E(self.lid.."*****NO detection has been set up (yet)!") + end + return self +end + --- [User] Set up detection of STATIC objects. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff Set to `true`for on and `false`for off. From 960f261ddde52a1db89ea9b35f2dc4f424ba1c11 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Sat, 11 Feb 2023 22:17:24 +0100 Subject: [PATCH 3/6] Master merge (#1920) * #NET * Initial release * Update MarkerOps_Base.lua (#1919) --- Moose Development/Moose/Core/MarkerOps_Base.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/MarkerOps_Base.lua b/Moose Development/Moose/Core/MarkerOps_Base.lua index 2cdb2afb6..b39250c8c 100644 --- a/Moose Development/Moose/Core/MarkerOps_Base.lua +++ b/Moose Development/Moose/Core/MarkerOps_Base.lua @@ -17,7 +17,7 @@ -- ### Author: **Applevangelist** -- -- Date: 5 May 2021 --- Last Update: Sep 2022 +-- Last Update: Feb 2023 -- -- === --- @@ -50,7 +50,7 @@ MARKEROPS_BASE = { ClassName = "MARKEROPS", Tag = "mytag", Keywords = {}, - version = "0.1.0", + version = "0.1.1", debug = false, Casesensitive = true, } @@ -124,7 +124,8 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive) -- @param #string Text The text on the marker -- @param #table Keywords Table of matching keywords found in the Event text -- @param Core.Point#COORDINATE Coord Coordinate of the marker. - + -- @param #number idx DCS Marker ID + --- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map. -- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted -- @param #MARKEROPS_BASE self @@ -172,7 +173,7 @@ function MARKEROPS_BASE:OnEventMark(Event) if Eventtext~=nil then if self:_MatchTag(Eventtext) then local matchtable = self:_MatchKeywords(Eventtext) - self:MarkChanged(Eventtext,matchtable,coord) + self:MarkChanged(Eventtext,matchtable,coord,Event.idx) end end elseif Event.id==world.event.S_EVENT_MARK_REMOVED then From 77a39364f4c42d0304ea77cafa993ae7771b815a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 14 Feb 2023 12:58:23 +0100 Subject: [PATCH 4/6] #PLAYERTASKCONTROLLER * Added ship detail types --- Moose Development/Moose/Ops/PlayerTask.lua | 37 +++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 94901b479..e4afe4b7a 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1369,6 +1369,13 @@ PLAYERTASKCONTROLLER.Messages = { AIRDEFENSE = "Airdefense", SAM = "SAM", GROUP = "Group", + UNARMEDSHIP = "Merchant", + LIGHTARMEDSHIP = "Light Boat", + CORVETTE = "Corvette", + FRIGATE = "Frigate", + CRUISER = "Cruiser", + DESTROYER = "Destroyer", + CARRIER = "Aircraft Carrier", }, DE = { TASKABORT = "Auftrag abgebrochen!", @@ -1441,12 +1448,19 @@ PLAYERTASKCONTROLLER.Messages = { AIRDEFENSE = "Flak", SAM = "Luftabwehr", GROUP = "Einheit", + UNARMEDSHIP = "Handelsschiff", + LIGHTARMEDSHIP = "Tender", + CORVETTE = "Korvette", + FRIGATE = "Fregatte", + CRUISER = "Kreuzer", + DESTROYER = "Zerstörer", + CARRIER = "Flugzeugträger", }, } --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.56" +PLAYERTASKCONTROLLER.version="0.1.57" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2262,6 +2276,27 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue() --self:T(self.lid.."Target TypeName = "..target.TypeName) end + if self.UseTypeNames and object:IsShip() then + local threat = object:GetThreatLevel() + local typekey = "UNARMEDSHIP" + if threat == 1 then + typekey = "LIGHTARMEDSHIP" + elseif threat == 2 then + typekey = "CORVETTE" + elseif threat == 3 or threat == 4 then + typekey = "FRIGATE" + elseif threat == 5 or threat == 6 then + typekey = "CRUISER" + elseif threat == 7 or threat == 8 then + typekey = "DESTROYER" + elseif threat >= 9 then + typekey = "CARRIER" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + target.TypeName = typename + --self:T(self.lid.."Target TypeName = "..target.TypeName) + end + self:_AddTask(target) end return self From ef0ddddb46b9602ac35d3686c2f71e25456a21fc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 15 Feb 2023 10:32:30 +0100 Subject: [PATCH 5/6] #PLAYERTASK * added group/unit check for UseTypeNames --- Moose Development/Moose/Ops/PlayerTask.lua | 102 +++++++++++---------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index e4afe4b7a..b5ee9e527 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1460,7 +1460,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.57" +PLAYERTASKCONTROLLER.version="0.1.58" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -2245,56 +2245,60 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue() end end - if self.UseTypeNames and object:IsGround() then - -- * Threat level 0: Unit is unarmed. - -- * Threat level 1: Unit is infantry. - -- * Threat level 2: Unit is an infantry vehicle. - -- * Threat level 3: Unit is ground artillery. - -- * Threat level 4: Unit is a tank. - -- * Threat level 5: Unit is a modern tank or ifv with ATGM. - -- * Threat level 6: Unit is a AAA. - -- * Threat level 7: Unit is a SAM or manpad, IR guided. - -- * Threat level 8: Unit is a Short Range SAM, radar guided. - -- * Threat level 9: Unit is a Medium Range SAM, radar guided. - -- * Threat level 10: Unit is a Long Range SAM, radar guided. - local threat = object:GetThreatLevel() - local typekey = "INFANTRY" - if threat == 0 or threat == 2 then - typekey = "TECHNICAL" - elseif threat == 3 then - typekey = "ARTILLERY" - elseif threat == 4 or threat == 5 then - typekey = "TANKS" - elseif threat == 6 or threat == 7 then - typekey = "AIRDEFENSE" - elseif threat >= 8 then - typekey = "SAM" - end - local typename = self.gettext:GetEntry(typekey,self.locale) - local gname = self.gettext:GetEntry("GROUP",self.locale) - target.TypeName = string.format("%s %s",typename,gname) - --self:T(self.lid.."Target TypeName = "..target.TypeName) - end + if object:IsInstanceOf("UNIT") or object:IsInstanceOf("GROUP") then - if self.UseTypeNames and object:IsShip() then - local threat = object:GetThreatLevel() - local typekey = "UNARMEDSHIP" - if threat == 1 then - typekey = "LIGHTARMEDSHIP" - elseif threat == 2 then - typekey = "CORVETTE" - elseif threat == 3 or threat == 4 then - typekey = "FRIGATE" - elseif threat == 5 or threat == 6 then - typekey = "CRUISER" - elseif threat == 7 or threat == 8 then - typekey = "DESTROYER" - elseif threat >= 9 then - typekey = "CARRIER" + if self.UseTypeNames and object:IsGround() then + -- * Threat level 0: Unit is unarmed. + -- * Threat level 1: Unit is infantry. + -- * Threat level 2: Unit is an infantry vehicle. + -- * Threat level 3: Unit is ground artillery. + -- * Threat level 4: Unit is a tank. + -- * Threat level 5: Unit is a modern tank or ifv with ATGM. + -- * Threat level 6: Unit is a AAA. + -- * Threat level 7: Unit is a SAM or manpad, IR guided. + -- * Threat level 8: Unit is a Short Range SAM, radar guided. + -- * Threat level 9: Unit is a Medium Range SAM, radar guided. + -- * Threat level 10: Unit is a Long Range SAM, radar guided. + local threat = object:GetThreatLevel() + local typekey = "INFANTRY" + if threat == 0 or threat == 2 then + typekey = "TECHNICAL" + elseif threat == 3 then + typekey = "ARTILLERY" + elseif threat == 4 or threat == 5 then + typekey = "TANKS" + elseif threat == 6 or threat == 7 then + typekey = "AIRDEFENSE" + elseif threat >= 8 then + typekey = "SAM" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + local gname = self.gettext:GetEntry("GROUP",self.locale) + target.TypeName = string.format("%s %s",typename,gname) + --self:T(self.lid.."Target TypeName = "..target.TypeName) end - local typename = self.gettext:GetEntry(typekey,self.locale) - target.TypeName = typename - --self:T(self.lid.."Target TypeName = "..target.TypeName) + + if self.UseTypeNames and object:IsShip() then + local threat = object:GetThreatLevel() + local typekey = "UNARMEDSHIP" + if threat == 1 then + typekey = "LIGHTARMEDSHIP" + elseif threat == 2 then + typekey = "CORVETTE" + elseif threat == 3 or threat == 4 then + typekey = "FRIGATE" + elseif threat == 5 or threat == 6 then + typekey = "CRUISER" + elseif threat == 7 or threat == 8 then + typekey = "DESTROYER" + elseif threat >= 9 then + typekey = "CARRIER" + end + local typename = self.gettext:GetEntry(typekey,self.locale) + target.TypeName = typename + --self:T(self.lid.."Target TypeName = "..target.TypeName) + end + end self:_AddTask(target) From 6ec43b72cee6c64e6893ec4ff0e4bf413a824a21 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 16 Feb 2023 18:22:06 +0100 Subject: [PATCH 6/6] #AICSAR * Added PlayerName to `OnAfterPilotRescued(From,Event,To,PilotName)` if available --- Moose Development/Moose/Functional/AICSAR.lua | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index b1ee470ca..0780b6814 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -52,6 +52,7 @@ -- @field Core.Set#SET_CLIENT playerset Track if alive heli pilots are available. -- @field #boolean limithelos limit available number of helos going on mission (defaults to true) -- @field #number helonumber number of helos available (default: 3) +-- @field Utilities.FiFo#FIFO PilotStore -- @extends Core.Fsm#FSM @@ -186,7 +187,7 @@ -- @field #AICSAR AICSAR = { ClassName = "AICSAR", - version = "0.1.9", + version = "0.1.10", lid = "", coalition = coalition.side.BLUE, template = "", @@ -226,6 +227,7 @@ AICSAR = { SRSPilotVoice = false, SRSOperator = nil, SRSOperatorVoice = false, + PilotStore = nil, } -- TODO Messages @@ -369,6 +371,9 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) -- Set some string id for output to DCS.log file. self.lid=string.format("%s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") + --Pilot Store + self.PilotStore = FIFO:New() + -- Start State. self:SetStartState("Stopped") @@ -385,6 +390,7 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:HandleEvent(EVENTS.LandingAfterEjection) + self:HandleEvent(EVENTS.Ejection) self:__Start(math.random(2,5)) @@ -438,7 +444,8 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone) -- @param #AICSAR self -- @param #string From From state. -- @param #string Event Event. - -- @param #string To To state. + -- @param #string To To state. + -- @param #string PilotName --- On after "PilotUnloaded" event. -- @function [parent=#AICSAR] OnAfterPilotUnloaded @@ -648,6 +655,19 @@ function AICSAR:DCSRadioBroadcast(Soundfile,Duration,Subtitle) return self end +--- [Internal] Catch the ejection and save the pilot name +-- @param #AICSAR self +-- @param Core.Event#EVENTDATA EventData +-- @return #AICSAR self +function AICSAR:OnEventEjection(EventData) + local _event = EventData -- Core.Event#EVENTDATA + if _event.IniPlayerName then + self.PilotStore:Push(_event.IniPlayerName) + self:T(self.lid.."Pilot Ejected: ".._event.IniPlayerName) + end + return self +end + --- [Internal] Catch the landing after ejection and spawn a pilot in situ. -- @param #AICSAR self -- @param Core.Event#EVENTDATA EventData @@ -667,7 +687,7 @@ function AICSAR:OnEventLandingAfterEjection(EventData) local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p) local _country = _event.initiator:getCountry() local _coalition = coalition.getCountryCoalition( _country ) - + -- DONE: add distance check local distancetofarp = _LandingPos:Get2DDistance(self.farp:GetCoordinate()) @@ -858,6 +878,7 @@ function AICSAR:_CheckQueue(OpsGroup) for _index, _pilot in pairs(self.pilotqueue) do local classname = _pilot.ClassName and _pilot.ClassName or "NONE" local name = _pilot.GroupName and _pilot.GroupName or "NONE" + local playername = "John Doe" local helocount = self:_CountHelos() --self:T("Looking at " .. classname .. " " .. name) -- find one w/o mission @@ -873,7 +894,10 @@ function AICSAR:_CheckQueue(OpsGroup) end self.pilotqueue[_index] = nil self.rescued[_index] = true - self:__PilotRescued(2) + if self.PilotStore:Count() > 0 then + playername = self.PilotStore:Pull() + end + self:__PilotRescued(2,playername) if flightgroup then flightgroup.AICSARReserved = false end @@ -1095,8 +1119,9 @@ end -- @param #string From -- @param #string Event -- @param #string To +-- @param #string PilotName -- @return #AICSAR self -function AICSAR:onafterPilotRescued(From, Event, To) +function AICSAR:onafterPilotRescued(From, Event, To, PilotName) self:T({From, Event, To}) local text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTRESCUED",self.locale) if self.verbose then