diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index e3fbf16d2..39ac33044 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -67,7 +67,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.0.6" +PLAYERTASK.version="0.0.7" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -151,7 +151,7 @@ function PLAYERTASK:_SetController(Controller) return self end ---- [User] Check is task is done +--- [User] Check if task is done -- @param #PLAYERTASK self -- @return #boolean done function PLAYERTASK:IsDone() @@ -164,6 +164,24 @@ function PLAYERTASK:IsDone() return IsDone end +--- [User] Get clients assigned list as table +-- @param #PLAYERTASK self +-- @return #table clients +function PLAYERTASK:GetClients() + self:I(self.lid.."GetClients?") + local clientlist = self.Clients:GetIDStackSorted() or {} + return clientlist +end + +--- [User] Check if a player name is assigned to this task +-- @param #PLAYERTASK self +-- @param #string Name +-- @return #boolean HasName +function PLAYERTASK:HasPlayerName(Name) + self:I(self.lid.."HasPlayerName?") + return self.Clients:HasUniqueID(Name) +end + --- [User] Add a client to this task -- @param #PLAYERTASK self -- @param Wrapper.Client#CLIENT Client @@ -187,6 +205,9 @@ function PLAYERTASK:RemoveClient(Client) local name = Client:GetPlayerName() if self.Clients:HasUniqueID(name) then self.Clients:PullByID(name) + if self.verbose then + self.Clients:Flush() + end self:__ClientRemoved(-2,Client) if self.Clients:Count() == 0 then self:__Failed(-1) @@ -555,6 +576,7 @@ end -- @field #string Name -- @field #string Type -- @field #boolean UseGroupNames +-- @field #table PlayerMenu -- @@ -567,6 +589,7 @@ PLAYERTASKCONTROLLER = { TargetQueue = nil, ClientSet = nil, UseGroupNames = true, + PlayerMenu = {}, } --- @@ -579,7 +602,7 @@ PLAYERTASKCONTROLLER.Type = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.0.4" +PLAYERTASKCONTROLLER.version="0.0.7" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -602,6 +625,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.TargetQueue = FIFO:New() -- Utilities.FiFo#FIFO self.TaskQueue = FIFO:New() -- Utilities.FiFo#FIFO self.TasksPerPlayer = FIFO:New() -- Utilities.FiFo#FIFO + self.PlayerMenu = {} -- #table self.repeatonfailed = true self.repeattimes = 5 @@ -620,6 +644,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self:AddTransition("Stopped", "Start", "Running") self:AddTransition("*", "Status", "*") + self:AddTransition("*", "TaskAdded", "*") self:AddTransition("*", "TaskDone", "*") self:AddTransition("*", "TaskCancelled", "*") self:AddTransition("*", "TaskSuccess", "*") @@ -630,11 +655,47 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self:__Start(-1) self:__Status(-2) + -- Player leaves + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) + self:HandleEvent(EVENTS.Ejection, self._EventHandler) + self:HandleEvent(EVENTS.Crash, self._EventHandler) + self:HandleEvent(EVENTS.PilotDead, self._EventHandler) + self:I(self.lid.."Started.") return self end +--- [internal] Event handling +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Event#EVENTDATA EventData +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:_EventHandler(EventData) + self:I(self.lid.."_EventHandler: "..EventData.id) + if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then + if EventData.IniPlayerName then + self:I(self.lid.."Event for player: "..EventData.IniPlayerName) + if self.PlayerMenu[EventData.IniPlayerName] then + self.PlayerMenu[EventData.IniPlayerName]:Remove() + self.PlayerMenu[EventData.IniPlayerName] = nil + end + local text = "" + if self.TasksPerPlayer:HasUniqueID(EventData.IniPlayerName) then + local task = self.TasksPerPlayer:PullByID(EventData.IniPlayerName) -- Ops.PlayerTask#PLAYERTASK + local Client = _DATABASE:FindClient( EventData.IniPlayerName ) + if Client then + task:RemoveClient(Client) + text = "Task aborted!" + end + else + text = "No active task!" + end + self:I(self.lid..text) + end + end + return self +end + function PLAYERTASKCONTROLLER:_DummyMenu(group) self:I(self.lid.."_DummyMenu") return self @@ -718,7 +779,7 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue() self:I("Looking at Task: "..data.PlayerTaskNr.." Type: "..data.Type.." State: "..data:GetState()) if data:GetState() == "Done" or data:GetState() == "Stopped" then local task = self.TaskQueue:ReadByID(_id) -- Ops.PlayerTask#PLAYERTASK - -- TODO: Remove clients from the task + -- DEBUG: Remove clients from the task local clientsattask = task.Clients:GetIDStackSorted() for _,_id in pairs(clientsattask) do self:I("*****Removing player " .. _id) @@ -814,6 +875,7 @@ function PLAYERTASKCONTROLLER:_AddTask(Target) local task = PLAYERTASK:New(type,Target,self.repeatonfailed,self.repeattimes) task:_SetController(self) self.TaskQueue:Push(task) + self:__TaskAdded(-1,task) return self end @@ -841,7 +903,10 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task) self:I(self.lid..text) local m=MESSAGE:New(text,"10","Info"):ToAll() self.TasksPerPlayer:Push(Task,playername) - Task.TaskMenu:Remove() + -- clear menu + if self.PlayerMenu[playername] then + self.PlayerMenu[playername]:RemoveSubMenus() + end end return self end @@ -864,8 +929,15 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client) local CoordText = Coordinate:ToStringA2G(Client) local ThreatLevel = task.Target:GetThreatLevelMax() local targets = task.Target:CountTargets() or 0 + local clientlist = task:GetClients() local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel - text = string.format("%s\nThreat: %s\nTargets left: %d\nCoord: %s", taskname, ThreatGraph, targets, CoordText) + text = string.format("%s\nThreat: %s\nTargets left: %d\nCoord: %s", taskname, ThreatGraph, targets, CoordText) + local clienttxt = "\nPilot(s): " + for _,_name in pairs(clientlist) do + clienttxt = clienttxt .. _name .. ", " + end + clienttxt=string.gsub(clienttxt,", $",".") + text = text .. clienttxt else text = "No active task!" end @@ -963,16 +1035,24 @@ function PLAYERTASKCONTROLLER:_BuildMenus() if _client then local client = _client -- Wrapper.Client#CLIENT local group = client:GetGroup() - if group then + local playername = client:GetPlayerName() or "Unknown" + if group and client then local topmenu = MENU_GROUP:New(group,self.Name.." Tasking "..self.Type,nil) local active = MENU_GROUP:New(group,"Active Task",topmenu) local info = MENU_GROUP_COMMAND:New(group,"Info",active,self._ActiveTaskInfo,self,group,client) local mark = MENU_GROUP_COMMAND:New(group,"Mark on map",active,self._MarkTask,self,group,client) - local smoke = MENU_GROUP_COMMAND:New(group,"Smoke",active,self._SmokeTask,self,group,client) - local flare = MENU_GROUP_COMMAND:New(group,"Flare",active,self._FlareTask,self,group,client) + if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then + -- no smoking/flaring here if A2A + local smoke = MENU_GROUP_COMMAND:New(group,"Smoke",active,self._SmokeTask,self,group,client) + local flare = MENU_GROUP_COMMAND:New(group,"Flare",active,self._FlareTask,self,group,client) + end local abort = MENU_GROUP_COMMAND:New(group,"Abort",active,self._AbortTask,self,group,client) - local join = MENU_GROUP:New(group,"Join Task",topmenu) + if self.PlayerMenu[playername] then + self.PlayerMenu[playername]:RemoveSubMenus() + else + self.PlayerMenu[playername] = MENU_GROUP:New(group,"Join Task",topmenu) + end local tasktypes = self:_GetAvailableTaskTypes() local taskpertype = self:_GetTasksPerType() @@ -980,9 +1060,10 @@ function PLAYERTASKCONTROLLER:_BuildMenus() local ttypes = {} local taskmenu = {} for _tasktype,_data in pairs(tasktypes) do - ttypes[_tasktype] = MENU_GROUP:New(group,_tasktype,join) + ttypes[_tasktype] = MENU_GROUP:New(group,_tasktype,self.PlayerMenu[playername]) local tasks = taskpertype[_tasktype] or {} for _,_task in pairs(tasks) do + _task = _task -- Ops.PlayerTask#PLAYERTASK local text = string.format("TaskNo %03d",_task.PlayerTaskNr) if self.UseGroupNames then local name = _task.Target:GetName() @@ -990,13 +1071,14 @@ function PLAYERTASKCONTROLLER:_BuildMenus() text = string.format("%s (%03d)",name,_task.PlayerTaskNr) end end - local taskentry = MENU_GROUP_COMMAND:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task) - taskentry:SetTag(client:GetPlayerName()) - taskmenu[#taskmenu+1] = taskentry - _task.TaskMenu = taskentry + if _task:GetState() == "Planned" or (not _task:HasPlayerName(playername)) then + local taskentry = MENU_GROUP_COMMAND:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task) + taskentry:SetTag(playername) + taskmenu[#taskmenu+1] = taskentry + end end end - join:Refresh() + self.PlayerMenu[playername]:Refresh() end end end @@ -1101,6 +1183,21 @@ function PLAYERTASKCONTROLLER:onafterTaskRepeatOnFailed(From, Event, To, Task) return self end +--- [Internal] On after task added +-- @param #PLAYERTASKCONTROLLER self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Ops.PlayerTask#PLAYERTASK Task +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:onafterTaskAdded(From, Event, To, Task) + self:I({From, Event, To}) + self:I(self.lid.."TaskAdded") + local taskname = string.format("%s has a new Task %s", self.Name, tostring(Task.Type)) + local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition) + return self +end + --- [Internal] On after Stop call -- @param #PLAYERTASKCONTROLLER self -- @param #string From @@ -1110,6 +1207,11 @@ end function PLAYERTASKCONTROLLER:onafterStop(From, Event, To) self:I({From, Event, To}) self:I(self.lid.."Stopped.") + -- Player leaves + self:UnHandleEvent(EVENTS.PlayerLeaveUnit) + self:UnHandleEvent(EVENTS.Ejection) + self:UnHandleEvent(EVENTS.Crash) + self:UnHandleEvent(EVENTS.PilotDead) return self end