From 66c7ebf7867a106ec8f27e02c32ae081062b22e6 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 20 Apr 2016 21:59:37 +0200 Subject: [PATCH] Further optimization of the Event Dispatcher. Also fixed a problem with DESTROYGROUPTASK --- Moose/CleanUp.lua | 131 ++++++------ Moose/Database.lua | 10 +- Moose/DestroyBaseTask.lua | 43 ++-- Moose/DestroyGroupsTask.lua | 39 ++-- Moose/DestroyRadarsTask.lua | 4 +- Moose/DestroyUnitTypesTask.lua | 2 +- Moose/Event.lua | 194 +++++++++++++++--- Moose/Movement.lua | 42 ++-- Moose/Sead.lua | 18 +- Moose/Spawn.lua | 35 ++-- Moose/Task.lua | 43 ++-- .../Moose_Test_DESTROY/MOOSE_Test_DESTROY.miz | Bin 0 -> 29167 bytes .../Moose_Test_DESTROY/Moose_Test_DESTROY.lua | 41 ++++ .../Moose_Test_SEAD/MOOSE_Test_SEAD.miz | Bin 0 -> 20851 bytes .../Moose_Test_SEAD/Moose_Test_SEAD.lua | 11 + 15 files changed, 396 insertions(+), 217 deletions(-) create mode 100644 Test Missions/Moose_Test_DESTROY/MOOSE_Test_DESTROY.miz create mode 100644 Test Missions/Moose_Test_DESTROY/Moose_Test_DESTROY.lua create mode 100644 Test Missions/Moose_Test_SEAD/MOOSE_Test_SEAD.miz create mode 100644 Test Missions/Moose_Test_SEAD/Moose_Test_SEAD.lua diff --git a/Moose/CleanUp.lua b/Moose/CleanUp.lua index 4c05fadc6..ac28d7738 100644 --- a/Moose/CleanUp.lua +++ b/Moose/CleanUp.lua @@ -40,15 +40,8 @@ function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, self.TimeInterval = TimeInterval end - self:AddEvent( world.event.S_EVENT_ENGINE_SHUTDOWN, self._EventAddForCleanUp ) - self:AddEvent( world.event.S_EVENT_ENGINE_STARTUP, self._EventAddForCleanUp ) - self:AddEvent( world.event.S_EVENT_HIT, self._EventAddForCleanUp ) -- , self._EventHitCleanUp ) - self:AddEvent( world.event.S_EVENT_CRASH, self._EventCrash ) -- , self._EventHitCleanUp ) - --self:AddEvent( world.event.S_EVENT_DEAD, self._EventCrash ) - self:AddEvent( world.event.S_EVENT_SHOT, self._EventShot ) + _EventDispatcher:OnBirth( self._OnEventBirth, self ) - self:EnableEvents() - self.CleanUpScheduler = routines.scheduleFunction( self._CleanUpScheduler, { self }, timer.getTime() + 1, TimeInterval ) return self @@ -111,6 +104,33 @@ function CLEANUP:_DestroyMissile( MissileObject ) end end +function CLEANUP:_OnEventBirth( Event ) + self:F( { Event } ) + + self.CleanUpList[Event.IniDCSUnitName] = {} + self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit + self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup + self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName + self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName + + _EventDispatcher:OnEngineShutDownForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) + _EventDispatcher:OnEngineStartUpForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) + _EventDispatcher:OnHitForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) + _EventDispatcher:OnCrashForUnit( Event.IniDCSUnitName, self._EventCrash, self ) + _EventDispatcher:OnShotForUnit( Event.IniDCSUnitName, self._EventShot, self ) + + --self:AddEvent( world.event.S_EVENT_ENGINE_SHUTDOWN, self._EventAddForCleanUp ) + --self:AddEvent( world.event.S_EVENT_ENGINE_STARTUP, self._EventAddForCleanUp ) +-- self:AddEvent( world.event.S_EVENT_HIT, self._EventAddForCleanUp ) -- , self._EventHitCleanUp ) +-- self:AddEvent( world.event.S_EVENT_CRASH, self._EventCrash ) -- , self._EventHitCleanUp ) +-- --self:AddEvent( world.event.S_EVENT_DEAD, self._EventCrash ) +-- self:AddEvent( world.event.S_EVENT_SHOT, self._EventShot ) +-- +-- self:EnableEvents() + + +end + --- Detects if a crash event occurs. -- Crashed units go into a CleanUpList for removal. -- @param #CLEANUP self @@ -126,44 +146,27 @@ function CLEANUP:_EventCrash( event ) -- self:T("after deactivateGroup") -- event.initiator:destroy() - local CleanUpUnit = event.initiator -- the Unit - local CleanUpUnitName = CleanUpUnit:getName() -- return the name of the Unit - local CleanUpGroup = Unit.getGroup(CleanUpUnit)-- Identify the Group - local CleanUpGroupName = "" - if CleanUpGroup and CleanUpGroup:isExist() then - CleanUpGroupName = CleanUpGroup:getName() -- return the name of the Group - end - - self.CleanUpList[CleanUpUnitName] = {} - self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit - self.CleanUpList[CleanUpUnitName].CleanUpGroup = CleanUpGroup - self.CleanUpList[CleanUpUnitName].CleanUpGroupName = CleanUpGroupName - self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName + self.CleanUpList[Event.IniDCSUnitName] = {} + self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit + self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup + self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName + self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName end --- Detects if a unit shoots a missile. -- If this occurs within one of the zones, then the weapon used must be destroyed. -- @param #CLEANUP self -- @param DCSTypes#Event event -function CLEANUP:_EventShot( event ) - self:F( { event } ) +function CLEANUP:_EventShot( Event ) + self:F( { 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 - -- Start of the 2nd loop - --self:T( "Missile Launched = " .. _SEADmissileName ) - -- Test if the missile was fired within one of the CLEANUP.ZoneNames. local CurrentLandingZoneID = 0 - CurrentLandingZoneID = routines.IsUnitInZones( event.initiator, self.ZoneNames ) + CurrentLandingZoneID = routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) if ( CurrentLandingZoneID ) then -- Okay, the missile was fired within the CLEANUP.ZoneNames, destroy the fired weapon. --_SEADmissile:destroy() - routines.scheduleFunction( CLEANUP._DestroyMissile, {self, _SEADmissile}, timer.getTime() + 0.1) + routines.scheduleFunction( CLEANUP._DestroyMissile, { self, Event.Weapon }, timer.getTime() + 0.1) end end @@ -171,38 +174,28 @@ end --- Detects if the Unit has an S_EVENT_HIT within the given ZoneNames. If this is the case, destroy the unit. -- @param #CLEANUP self -- @param DCSTypes#Event event -function CLEANUP:_EventHitCleanUp( event ) - self:F( { event } ) +function CLEANUP:_EventHitCleanUp( Event ) + self:F( { Event } ) - local CleanUpUnit = event.initiator -- the Unit - if CleanUpUnit and CleanUpUnit:isExist() and Object.getCategory(CleanUpUnit) == Object.Category.UNIT then - local CleanUpUnitName = event.initiator:getName() -- return the name of the Unit - - if routines.IsUnitInZones( CleanUpUnit, self.ZoneNames ) ~= nil then - self:T( "Life: " .. CleanUpUnitName .. ' = ' .. CleanUpUnit:getLife() .. "/" .. CleanUpUnit:getLife0() ) - if CleanUpUnit:getLife() < CleanUpUnit:getLife0() then - self:T( "CleanUp: Destroy: " .. CleanUpUnitName ) - routines.scheduleFunction( CLEANUP._DestroyUnit, {self, CleanUpUnit}, timer.getTime() + 0.1) + if Event.IniDCSUnit then + if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then + self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniDCSUnit:getLife(), "/", Event.IniDCSUnit:getLife0() } ) + if Event.IniDCSUnit:getLife() < Event.IniDCSUnit:getLife0() then + self:T( "CleanUp: Destroy: " .. Event.IniDCSUnitName ) + routines.scheduleFunction( CLEANUP._DestroyUnit, { self, Event.IniDCSUnit }, timer.getTime() + 0.1) end end end - local CleanUpTgtUnit = event.target -- the target Unit - if CleanUpTgtUnit and CleanUpTgtUnit:isExist() and Object.getCategory(CleanUpTgtUnit) == Object.Category.UNIT then - local CleanUpTgtUnitName = event.target:getName() -- return the name of the target Unit - local CleanUpTgtGroup = Unit.getGroup(event.target)-- Identify the target Group - local CleanUpTgtGroupName = CleanUpTgtGroup:getName() -- return the name of the target Group - - - if routines.IsUnitInZones( CleanUpTgtUnit, self.ZoneNames ) ~= nil then - self:T( "Life: " .. CleanUpTgtUnitName .. ' = ' .. CleanUpTgtUnit:getLife() .. "/" .. CleanUpTgtUnit:getLife0() ) - if CleanUpTgtUnit:getLife() < CleanUpTgtUnit:getLife0() then - self:T( "CleanUp: Destroy: " .. CleanUpTgtUnitName ) - routines.scheduleFunction( CLEANUP._DestroyUnit, {self, CleanUpTgtUnit}, timer.getTime() + 0.1) + if Event.TgtDCSUnit then + if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then + self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtDCSUnit:getLife(), "/", Event.TgtDCSUnit:getLife0() } ) + if Event.TgtDCSUnit:getLife() < Event.TgtDCSUnit:getLife0() then + self:T( "CleanUp: Destroy: " .. Event.TgtDCSUnitName ) + routines.scheduleFunction( CLEANUP._DestroyUnit, { self, Event.TgtDCSUnit }, timer.getTime() + 0.1 ) end end end - end --- Add the @{DCSUnit#Unit} to the CleanUpList for CleanUp. @@ -224,24 +217,20 @@ end --- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given ZoneNames. If this is the case, add the Group to the CLEANUP List. -- @param #CLEANUP self -- @param DCSTypes#Event event -function CLEANUP:_EventAddForCleanUp( event ) +function CLEANUP:_EventAddForCleanUp( Event ) - local CleanUpUnit = event.initiator -- the Unit - if CleanUpUnit and Object.getCategory(CleanUpUnit) == Object.Category.UNIT then - local CleanUpUnitName = CleanUpUnit:getName() -- return the name of the Unit - if self.CleanUpList[CleanUpUnitName] == nil then - if routines.IsUnitInZones( CleanUpUnit, self.ZoneNames ) ~= nil then - self:_AddForCleanUp( CleanUpUnit, CleanUpUnitName ) + if Event.IniDCSUnit then + if self.CleanUpList[Event.IniDCSUnitName] == nil then + if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then + self:_AddForCleanUp( Event.IniDCSUnit, Event.IniDCSUnitName ) end end end - local CleanUpTgtUnit = event.target -- the target Unit - if CleanUpTgtUnit and Object.getCategory(CleanUpTgtUnit) == Object.Category.UNIT then - local CleanUpTgtUnitName = CleanUpTgtUnit:getName() -- return the name of the target Unit - if self.CleanUpList[CleanUpTgtUnitName] == nil then - if routines.IsUnitInZones( CleanUpTgtUnit, self.ZoneNames ) ~= nil then - self:_AddForCleanUp( CleanUpTgtUnit, CleanUpTgtUnitName ) + if Event.TgtDCSUnit then + if self.CleanUpList[Event.TgtDCSUnitName] == nil then + if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then + self:_AddForCleanUp( Event.TgtDCSUnit, Event.TgtDCSUnitName ) end end end diff --git a/Moose/Database.lua b/Moose/Database.lua index eef7fb002..a05b31fea 100644 --- a/Moose/Database.lua +++ b/Moose/Database.lua @@ -7,6 +7,7 @@ Include.File( "Routines" ) Include.File( "Base" ) Include.File( "Menu" ) Include.File( "Group" ) +Include.File( "Event" ) --- The DATABASE class -- @type DATABASE @@ -110,13 +111,10 @@ function DATABASE:New() end --for coa_name, coa_data in pairs(mission.coalition) do --self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) - self:AddEvent( world.event.S_EVENT_DEAD, self.OnDeadOrCrash ) - self:AddEvent( world.event.S_EVENT_CRASH, self.OnDeadOrCrash ) + _EventDispatcher:OnDead( self.OnDeadOrCrash, self ) + _EventDispatcher:OnCrash( self.OnDeadOrCrash, self ) + _EventDispatcher:OnHit( self.OnHit, self ) - self:AddEvent( world.event.S_EVENT_HIT, self.OnHit) - - self:EnableEvents() - self.SchedulerId = routines.scheduleFunction( DATABASE._FollowPlayers, { self }, 0, 5 ) self:ScoreMenu() diff --git a/Moose/DestroyBaseTask.lua b/Moose/DestroyBaseTask.lua index 6d73b0036..3cef73cd5 100644 --- a/Moose/DestroyBaseTask.lua +++ b/Moose/DestroyBaseTask.lua @@ -16,10 +16,11 @@ DESTROYBASETASK = { } --- Creates a new DESTROYBASETASK. --- @param string DestroyGroupType Text describing the group to be destroyed. f.e. "Radar Installations", "Ships", "Vehicles", "Command Centers". --- @param string DestroyUnitType Text describing the unit types to be destroyed. f.e. "SA-6", "Row Boats", "Tanks", "Tents". --- @param table{string,...} DestroyGroupPrefixes Table of Prefixes of the Groups to be destroyed before task is completed. --- @param ?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}. +-- @param #DESTROYBASETASK self +-- @param #string DestroyGroupType Text describing the group to be destroyed. f.e. "Radar Installations", "Ships", "Vehicles", "Command Centers". +-- @param #string DestroyUnitType Text describing the unit types to be destroyed. f.e. "SA-6", "Row Boats", "Tanks", "Tents". +-- @param #list<#string> DestroyGroupPrefixes Table of Prefixes of the Groups to be destroyed before task is completed. +-- @param #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}. -- @return DESTROYBASETASK function DESTROYBASETASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupPrefixes, DestroyPercentage ) local self = BASE:Inherit( self, TASK:New() ) @@ -30,34 +31,31 @@ function DESTROYBASETASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupPre self.DestroyGroupPrefixes = DestroyGroupPrefixes self.DestroyGroupType = DestroyGroupType self.DestroyUnitType = DestroyUnitType + if DestroyPercentage then + self.DestroyPercentage = DestroyPercentage + end self.TaskBriefing = "Task: Destroy " .. DestroyGroupType .. "." self.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEGROUPSDESTROYED:New(), STAGEDONE:New() } self.SetStage( self, 1 ) - --self.AddEvent( self, world.event.S_EVENT_DEAD, self.EventDead ) - - --env.info( 'New Table self = ' .. tostring(self) ) - --env.info( 'New Table self = ' .. tostring(self) ) - 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 ) - self:F( { 'EventDead', event } ) +-- @param #DESTROYBASETASK self +-- @param Event#EVENTDATA Event structure of MOOSE. +function DESTROYBASETASK:EventDead( Event ) + self:F( { Event } ) - 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 + if Event.IniDCSUnit then + local DestroyUnit = Event.IniDCSUnit + local DestroyUnitName = Event.IniDCSUnitName + local DestroyGroup = Event.IniDCSGroup + local DestroyGroupName = Event.IniDCSGroupName + + --TODO: I need to fix here if 2 groups in the mission have a similar name with GroupPrefix equal, then i should differentiate for which group the goal was reached! + --I may need to test if for the goalverb that group goal was reached or something. Need to think about it a bit more ... local UnitsDestroyed = 0 - self:T( DestroyGroupName ) - self:T( DestroyUnitName ) for DestroyGroupPrefixID, DestroyGroupPrefix in pairs( self.DestroyGroupPrefixes ) do self:T( DestroyGroupPrefix ) if string.find( DestroyGroupName, DestroyGroupPrefix, 1, true ) then @@ -70,6 +68,7 @@ function DESTROYBASETASK:EventDead( event ) self:T( { UnitsDestroyed } ) self:IncreaseGoalCount( UnitsDestroyed, self.GoalVerb ) end + end --- Validate task completeness of DESTROYBASETASK. diff --git a/Moose/DestroyGroupsTask.lua b/Moose/DestroyGroupsTask.lua index 44ab501ff..81c3fbca0 100644 --- a/Moose/DestroyGroupsTask.lua +++ b/Moose/DestroyGroupsTask.lua @@ -11,10 +11,11 @@ DESTROYGROUPSTASK = { } --- Creates a new DESTROYGROUPSTASK. --- @param string DestroyGroupType String describing the group to be destroyed. --- @param string DestroyUnitType String describing the unit to be destroyed. --- @param table{string,...} DestroyGroupNames Table of string containing the name of the groups to be destroyed before task is completed. --- @param ?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}. +-- @param #DESTROYGROUPSTASK self +-- @param #string DestroyGroupType String describing the group to be destroyed. +-- @param #string DestroyUnitType String describing the unit to be destroyed. +-- @param #list<#string> DestroyGroupNames Table of string containing the name of the groups to be destroyed before task is completed. +-- @param #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}. ---@return DESTROYGROUPSTASK function DESTROYGROUPSTASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupNames, DestroyPercentage ) local self = BASE:Inherit( self, DESTROYBASETASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupNames, DestroyPercentage ) ) @@ -23,34 +24,34 @@ function DESTROYGROUPSTASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupN self.Name = 'Destroy Groups' self.GoalVerb = "Destroy " .. DestroyGroupType - self:AddEvent( world.event.S_EVENT_DEAD, self.EventDead ) - self:AddEvent( world.event.S_EVENT_CRASH, self.EventDead ) - --Child.AddEvent( Child, world.event.S_EVENT_PILOT_DEAD, Child.EventDead ) + _EventDispatcher:OnDead( self.EventDead , self ) + _EventDispatcher:OnCrash( self.EventDead , self ) return self end --- Report Goal Progress. --- @param Group DestroyGroup Group structure describing the group to be evaluated. --- @param Unit DestroyUnit Unit structure describing the Unit to be evaluated. +-- @param #DESTROYGROUPSTASK self +-- @param DCSGroup#Group DestroyGroup Group structure describing the group to be evaluated. +-- @param DCSUnit#Unit DestroyUnit Unit structure describing the Unit to be evaluated. +-- @return #number The DestroyCount reflecting the amount of units destroyed within the group. function DESTROYGROUPSTASK:ReportGoalProgress( DestroyGroup, DestroyUnit ) - self:F( { DestroyGroup, DestroyUnit } ) - self:T( DestroyGroup:getSize() ) + self:F( { DestroyGroup, DestroyUnit, self.DestroyPercentage } ) + + local DestroyGroupSize = DestroyGroup:getSize() - 1 -- When a DEAD event occurs, the getSize is still one larger than the destroyed unit. + local DestroyGroupInitialSize = DestroyGroup:getInitialSize() + self:T( { DestroyGroupSize, DestroyGroupInitialSize - ( DestroyGroupInitialSize * self.DestroyPercentage / 100 ) } ) local DestroyCount = 0 if DestroyGroup then - if ( ( DestroyGroup:getInitialSize() * self.DestroyPercentage ) / 100 ) - DestroyGroup:getSize() <= 0 then + if DestroyGroupSize <= DestroyGroupInitialSize - ( DestroyGroupInitialSize * self.DestroyPercentage / 100 ) then DestroyCount = 1 ---[[ else - if DestroyGroup:getSize() == 1 then - if DestroyUnit and DestroyUnit:getLife() <= 1.0 then - DestroyCount = 1 - end - end - ]] end + end else DestroyCount = 1 end + self:T( DestroyCount ) + return DestroyCount end diff --git a/Moose/DestroyRadarsTask.lua b/Moose/DestroyRadarsTask.lua index be28006d8..9c799d586 100644 --- a/Moose/DestroyRadarsTask.lua +++ b/Moose/DestroyRadarsTask.lua @@ -18,8 +18,8 @@ function DESTROYRADARSTASK:New( DestroyGroupNames ) self:F() self.Name = 'Destroy Radars' - - self:AddEvent( world.event.S_EVENT_DEAD, self.EventDead ) + + _EventDispatcher:OnDead( self.EventDead , self ) return self end diff --git a/Moose/DestroyUnitTypesTask.lua b/Moose/DestroyUnitTypesTask.lua index 17e5af1bf..27426ed15 100644 --- a/Moose/DestroyUnitTypesTask.lua +++ b/Moose/DestroyUnitTypesTask.lua @@ -29,7 +29,7 @@ function DESTROYUNITTYPESTASK:New( DestroyGroupType, DestroyUnitType, DestroyGro self.Name = 'Destroy Unit Types' self.GoalVerb = "Destroy " .. DestroyGroupType - self:AddEvent( world.event.S_EVENT_DEAD, self.EventDead ) + _EventDispatcher:OnDead( self.EventDead , self ) return self end diff --git a/Moose/Event.lua b/Moose/Event.lua index 6346dbf4f..6ad91e3cf 100644 --- a/Moose/Event.lua +++ b/Moose/Event.lua @@ -40,6 +40,23 @@ local EVENTCODES = { "S_EVENT_MAX", } +--- The Event structure +-- @type EVENTDATA +-- @field id +-- @field initiator +-- @field target +-- @field weapon +-- @field IniDCSUnit +-- @field IniDCSUnitName +-- @field IniDCSGroup +-- @field IniDCSGroupName +-- @field TgtDCSUnit +-- @field TgtDCSUnitName +-- @field TgtDCSGroup +-- @field TgtDCSGroupName +-- @field Weapon +-- @field WeaponName +-- @field WeaponTgtDCSUnit --- The Events structure -- @type EVENT.Events @@ -59,7 +76,7 @@ end -- @param #string EventClass -- @return #EVENT.Events function EVENT:Init( EventID, EventClass ) - self:F( EventID, EventClass ) + self:F( { EventID, EventClass } ) if not self.Events[EventID] then self.Events[EventID] = {} end @@ -89,6 +106,21 @@ function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, OnEv return self end +--- Set a new listener for an S_EVENT_X event independent from a unit or a weapon. +-- @param #EVENT self +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf The self instance of the class for which the event is. +-- @param EventID +-- @return #EVENT +function EVENT:OnEventGeneric( EventFunction, EventSelf, EventID ) + self:F( { EventID } ) + + local Event = self:Init( EventID, EventSelf:GetClassNameAndID() ) + Event.EventFunction = EventFunction + Event.EventSelf = EventSelf + return self +end + --- Set a new listener for an S_EVENT_X event -- @param #EVENT self @@ -117,7 +149,18 @@ end function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventSelf ) self:F( { EventTemplate } ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnBirth ) + return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnBirthForUnit ) +end + +--- Set a new listener for an S_EVENT_BIRTH event, and registers the unit born. +-- @param #EVENT self +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf +-- @return #EVENT +function EVENT:OnBirth( EventFunction, EventSelf ) + self:F() + + return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_BIRTH ) end --- Set a new listener for an S_EVENT_BIRTH event. @@ -126,7 +169,7 @@ end -- @param #function EventFunction The function to be called when the event occurs for the unit. -- @param Base#BASE EventSelf -- @return #EVENT -function EVENT:OnBirth( EventDCSUnitName, EventFunction, EventSelf ) +function EVENT:OnBirthForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_BIRTH ) @@ -141,7 +184,18 @@ end function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventSelf ) self:F( EventTemplate ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnCrash ) + return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnCrashForUnit ) +end + +--- Set a new listener for an S_EVENT_CRASH event. +-- @param #EVENT self +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf +-- @return #EVENT +function EVENT:OnCrash( EventFunction, EventSelf ) + self:F() + + return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_CRASH ) end --- Set a new listener for an S_EVENT_CRASH event. @@ -150,7 +204,7 @@ end -- @param #function EventFunction The function to be called when the event occurs for the unit. -- @param Base#BASE EventSelf The self instance of the class for which the event is. -- @return #EVENT -function EVENT:OnCrash( EventDCSUnitName, EventFunction, EventSelf ) +function EVENT:OnCrashForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_CRASH ) @@ -165,16 +219,28 @@ end function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventSelf ) self:F( EventTemplate ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnDead ) + return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnDeadForUnit ) end +--- Set a new listener for an S_EVENT_DEAD event. +-- @param #EVENT self +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf +-- @return #EVENT +function EVENT:OnDead( EventFunction, EventSelf ) + self:F() + + return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_DEAD ) +end + + --- Set a new listener for an S_EVENT_DEAD event. -- @param #EVENT self -- @param #string EventDCSUnitName -- @param #function EventFunction The function to be called when the event occurs for the unit. -- @param Base#BASE EventSelf The self instance of the class for which the event is. -- @return #EVENT -function EVENT:OnDead( EventDCSUnitName, EventFunction, EventSelf ) +function EVENT:OnDeadForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_DEAD ) @@ -189,7 +255,7 @@ end function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventSelf ) self:F( EventTemplate ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnLand ) + return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnLandForUnit ) end --- Set a new listener for an S_EVENT_LAND event. @@ -198,7 +264,7 @@ end -- @param #function EventFunction The function to be called when the event occurs for the unit. -- @param Base#BASE EventSelf The self instance of the class for which the event is. -- @return #EVENT -function EVENT:OnLand( EventDCSUnitName, EventFunction, EventSelf ) +function EVENT:OnLandForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_LAND ) @@ -213,16 +279,16 @@ end function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventSelf ) self:F( EventTemplate ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnTakeOff ) + return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnTakeOffForUnit ) end ---- Set a new listener for an S_EVENT_LAND event. +--- Set a new listener for an S_EVENT_TAKEOFF event. -- @param #EVENT self -- @param #string EventDCSUnitName -- @param #function EventFunction The function to be called when the event occurs for the unit. -- @param Base#BASE EventSelf The self instance of the class for which the event is. -- @return #EVENT -function EVENT:OnTakeOff( EventDCSUnitName, EventFunction, EventSelf ) +function EVENT:OnTakeOffForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_TAKEOFF ) @@ -237,39 +303,119 @@ end function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventSelf ) self:F( EventTemplate ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnEngineShutDown ) + return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnEngineShutDownForUnit ) end ---- Set a new listener for an S_EVENT_LAND event. +--- Set a new listener for an S_EVENT_ENGINE_SHUTDOWN event. -- @param #EVENT self -- @param #string EventDCSUnitName -- @param #function EventFunction The function to be called when the event occurs for the unit. -- @param Base#BASE EventSelf The self instance of the class for which the event is. -- @return #EVENT -function EVENT:OnEngineShutDown( EventDCSUnitName, EventFunction, EventSelf ) +function EVENT:OnEngineShutDownForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_SHUTDOWN ) end +--- Set a new listener for an S_EVENT_ENGINE_STARTUP event. +-- @param #EVENT self +-- @param #string EventDCSUnitName +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnEngineStartUpForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_STARTUP ) +end + +--- Set a new listener for an S_EVENT_SHOT event. +-- @param #EVENT self +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnShot( EventFunction, EventSelf ) + self:F() + + return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_SHOT ) +end + +--- Set a new listener for an S_EVENT_SHOT event for a unit. +-- @param #EVENT self +-- @param #string EventDCSUnitName +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnShotForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_SHOT ) +end + +--- Set a new listener for an S_EVENT_HIT event. +-- @param #EVENT self +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnHit( EventFunction, EventSelf ) + self:F() + + return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_HIT ) +end + +--- Set a new listener for an S_EVENT_HIT event. +-- @param #EVENT self +-- @param #string EventDCSUnitName +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnHitForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_HIT ) +end + function EVENT:onEvent( Event ) self:F( { EVENTCODES[Event.id], Event } ) if self and self.Events and self.Events[Event.id] then - local IniDCSUnit = Event.initiator - if IniDCSUnit and IniDCSUnit:getCategory() == Object.Category.UNIT then - Event.IniUnitName = IniDCSUnit:getName() + if Event.initiator and Event.initiator:getCategory() == Object.Category.UNIT then + Event.IniDCSUnit = Event.initiator + Event.IniDCSGroup = Event.IniDCSUnit:getGroup() + Event.IniDCSUnitName = Event.IniDCSUnit:getName() + Event.IniDCSGroupName = "" + if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then + Event.IniDCSGroupName = Event.IniDCSGroup:getName() + end end - local TgtDCSUnit = Event.target - if TgtDCSUnit and TgtDCSUnit:isExist() and TgtDCSUnit:getCategory() == Object.Category.UNIT then - Event.TgtUnitName = TgtDCSUnit:getName() + if Event.target then + if Event.target and Event.target:getCategory() == Object.Category.UNIT then + Event.TgtDCSUnit = Event.target + Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup() + Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() + Event.TgtDCSGroupName = "" + if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then + Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() + end + end + end + if Event.weapon then + Event.Weapon = Event.weapon + Event.WeaponName = Event.Weapon:getTypeName() + Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end for ClassName, EventData in pairs( self.Events[Event.id] ) do - if Event.IniUnitName and EventData.IniUnit and EventData.IniUnit[Event.IniUnitName] then - self:T( { "Calling event function for class ", ClassName, " unit ", Event.IniUnitName } ) - EventData.IniUnit[Event.IniUnitName].EventFunction( EventData.IniUnit[Event.IniUnitName].EventSelf, Event ) + if Event.IniDCSUnitName and EventData.IniUnit and EventData.IniUnit[Event.IniDCSUnitName] then + self:T( { "Calling event function for class ", ClassName, " unit ", Event.IniDCSUnitName } ) + EventData.IniUnit[Event.IniDCSUnitName].EventFunction( EventData.IniUnit[Event.IniDCSUnitName].EventSelf, Event ) + else + if Event.IniDCSUnit and not EventData.IniUnit then + self:T( { "Calling event function for class ", ClassName } ) + EventData.EventFunction( EventData.EventSelf, Event ) + end end end end diff --git a/Moose/Movement.lua b/Moose/Movement.lua index 727b4f36e..8f809a20d 100644 --- a/Moose/Movement.lua +++ b/Moose/Movement.lua @@ -35,11 +35,11 @@ function MOVEMENT:New( MovePrefixes, MoveMaximum ) 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. - self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) - self:AddEvent( world.event.S_EVENT_DEAD, self.OnDeadOrCrash ) - self:AddEvent( world.event.S_EVENT_CRASH, self.OnDeadOrCrash ) + _EventDispatcher:OnBirth( self.OnBirth, self ) - self:EnableEvents() +-- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) +-- +-- self:EnableEvents() self:ScheduleStart() @@ -61,43 +61,39 @@ 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 ) - self:F( { event } ) +function MOVEMENT:OnBirth( Event ) + self:F( { 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 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() + if Event.IniDCSUnit then + self:T( "Birth object : " .. Event.IniDCSUnitName ) + if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( MovementUnitName, MovePrefix, 1, true ) then + if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then self.AliveUnits = self.AliveUnits + 1 - self.MoveUnits[MovementUnitName] = MovementGroupName + self.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName self:T( self.AliveUnits ) end end end end + _EventDispatcher:OnCrashForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self ) + _EventDispatcher:OnDeadForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self ) end 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 ) - self:F( { event } ) +function MOVEMENT:OnDeadOrCrash( Event ) + self:F( { Event } ) - 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 ) + if Event.IniDCSUnit then + self:T( "Dead object : " .. Event.IniDCSUnitName ) for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( MovementUnitName, MovePrefix, 1, true ) then + if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then self.AliveUnits = self.AliveUnits - 1 - self.MoveUnits[MovementUnitName] = nil + self.MoveUnits[Event.IniDCSUnitName] = nil self:T( self.AliveUnits ) end end diff --git a/Moose/Sead.lua b/Moose/Sead.lua index 78d8f0d37..2bbfd31b8 100644 --- a/Moose/Sead.lua +++ b/Moose/Sead.lua @@ -4,6 +4,7 @@ -- @author (co) Flightcontrol (Modified and enriched with functionality) Include.File( "Routines" ) +Include.File( "Event" ) Include.File( "Base" ) Include.File( "Mission" ) Include.File( "Client" ) @@ -42,27 +43,26 @@ function SEAD:New( SEADGroupPrefixes ) else self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes end - self:AddEvent( world.event.S_EVENT_SHOT, self.EventShot ) - self:EnableEvents() + _EventDispatcher:OnShot( self.EventShot, self ) 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 ) - self:F( { event } ) +function SEAD:EventShot( Event ) + self:F( { Event } ) - local SEADUnit = event.initiator - local SEADUnitName = SEADUnit:getName() - local SEADWeapon = event.weapon -- Identify the weapon fired - local SEADWeaponName = SEADWeapon:getTypeName() -- return weapon type + local SEADUnit = Event.IniDCSUnit + local SEADUnitName = Event.IniDCSUnitName + local SEADWeapon = Event.Weapon -- Identify the weapon fired + local SEADWeaponName = Event.WeaponName -- return weapon type --trigger.action.outText( string.format("Alerte, depart missile " ..string.format(SEADWeaponName)), 20) --debug message -- Start of the 2nd loop 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(SEADWeapon) -- Identify target + local _targetMim = Event.WeaponTgtDCSUnit -- Identify target local _targetMimname = Unit.getName(_targetMim) local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroupName = _targetMimgroup:getName() diff --git a/Moose/Spawn.lua b/Moose/Spawn.lua index f61f351ad..f6659d7e1 100644 --- a/Moose/Spawn.lua +++ b/Moose/Spawn.lua @@ -122,12 +122,6 @@ function SPAWN:New( SpawnTemplatePrefix ) else error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) end - - --self:AddEvent( world.event.S_EVENT_BIRTH, self._OnBirth ) - --self:AddEvent( world.event.S_EVENT_DEAD, self._OnDeadOrCrash ) - --self:AddEvent( world.event.S_EVENT_CRASH, self._OnDeadOrCrash ) - - self:EnableEvents() return self end @@ -166,12 +160,6 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) end - --self:AddEvent( world.event.S_EVENT_BIRTH, self._OnBirth ) - --self:AddEvent( world.event.S_EVENT_DEAD, self._OnDeadOrCrash ) - --self:AddEvent( world.event.S_EVENT_CRASH, self._OnDeadOrCrash ) - - --self:EnableEvents() - return self end @@ -289,11 +277,6 @@ function SPAWN:Repeat() self.RepeatOnEngineShutDown = false self.RepeatOnLanding = true - --self:AddEvent( world.event.S_EVENT_LAND, self._OnLand ) - --self:AddEvent( world.event.S_EVENT_TAKEOFF, self._OnTakeOff ) - --self:AddEvent( world.event.S_EVENT_ENGINE_SHUTDOWN, self._OnEngineShutDown ) - --self:EnableEvents() - return self end @@ -386,10 +369,22 @@ function SPAWN:Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) self.SpawnGroups[SpawnGroupID].SpawnTemplate.visible = true self.SpawnGroups[SpawnGroupID].Visible = true + + _EventDispatcher:OnBirthForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnBirth, self ) + _EventDispatcher:OnCrashForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self ) + _EventDispatcher:OnDeadForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self ) + + if self.SpawnRepeat then + _EventDispatcher:OnTakeOffForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnTakeOff, self ) + end + if self.RepeatOnLanding then + _EventDispatcher:OnLandForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnLand, self ) + end + if self.RepeatOnEngineShutDown then + _EventDispatcher:OnEngineShutDownForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnEngineShutDown, self ) + end + self.SpawnGroups[SpawnGroupID].Group = _Database:Spawn( self.SpawnGroups[SpawnGroupID].SpawnTemplate ) - self:Event():OnBirthForGroup( self.SpawnGroups[SpawnGroupID].Group, self._OnBirth, self ) - self:Event():OnCrashForGroup( self.SpawnGroups[SpawnGroupID].Group, self._OnBirth, self ) - self:Event():OnDeadForGroup ( self.SpawnGroups[SpawnGroupID].Group, self._OnBirth, self ) SpawnX = SpawnXIndex * SpawnDeltaX SpawnY = SpawnYIndex * SpawnDeltaY diff --git a/Moose/Task.lua b/Moose/Task.lua index 3481963a2..68cd199e1 100644 --- a/Moose/Task.lua +++ b/Moose/Task.lua @@ -91,7 +91,7 @@ end --- Get progress of a TASK. -- @return string GoalsText function TASK:GetGoalProgress() - self:F() + self:F2() local GoalsText = "" for GoalVerb, GoalVerbData in pairs( self.GoalTasks ) do @@ -115,7 +115,7 @@ end -- @param MISSION Mission Group structure describing the Mission. -- @param CLIENT Client Group structure describing the Client. function TASK:ShowGoalProgress( Mission, Client ) - self:F() + self:F2() local GoalsText = "" for GoalVerb, GoalVerbData in pairs( self.GoalTasks ) do @@ -137,14 +137,14 @@ end --- Sets a TASK to status Done. function TASK:Done() - self:F() + self:F2() self.TaskDone = true end --- Returns if a TASK is done. -- @return bool function TASK:IsDone() - self:F( self.TaskDone ) + self:F2( self.TaskDone ) return self.TaskDone end @@ -157,12 +157,12 @@ end --- Returns if a TASk has failed. -- @return bool function TASK:IsFailed() - self:F( self.TaskFailed ) + self:F2( self.TaskFailed ) return self.TaskFailed end function TASK:Reset( Mission, Client ) - self:F() + self:F2() self.ExecuteStage = _TransportExecuteStage.NONE end @@ -173,13 +173,15 @@ function TASK:GetGoals() end --- Returns if a TASK has Goal(s). --- @param ?string GoalVerb is the name of the Goal of the TASK. +-- @param #TASK self +-- @param #string GoalVerb is the name of the Goal of the TASK. -- @return bool function TASK:Goal( GoalVerb ) - self:F() + self:F2( { GoalVerb } ) if not GoalVerb then GoalVerb = self.GoalVerb end + self:T2( {self.GoalTasks[GoalVerb] } ) if self.GoalTasks[GoalVerb] and self.GoalTasks[GoalVerb].GoalTotal > 0 then return true else @@ -191,7 +193,7 @@ end -- @param number GoalTotal is the number of times the GoalVerb needs to be achieved. -- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used. function TASK:SetGoalTotal( GoalTotal, GoalVerb ) - self:F( { GoalTotal, GoalVerb } ) + self:F2( { GoalTotal, GoalVerb } ) if not GoalVerb then GoalVerb = self.GoalVerb @@ -206,7 +208,7 @@ end --- Gets the total of Goals to be achieved within the TASK of the GoalVerb. -- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used. function TASK:GetGoalTotal( GoalVerb ) - self:F() + self:F2( { GoalVerb } ) if not GoalVerb then GoalVerb = self.GoalVerb end @@ -222,7 +224,7 @@ end -- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used. -- @return TASK function TASK:SetGoalCount( GoalCount, GoalVerb ) - self:F() + self:F2() if not GoalVerb then GoalVerb = self.GoalVerb end @@ -237,7 +239,7 @@ end -- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used. -- @return TASK function TASK:IncreaseGoalCount( GoalCountIncrease, GoalVerb ) - self:F() + self:F2( { GoalCountIncrease, GoalVerb } ) if not GoalVerb then GoalVerb = self.GoalVerb end @@ -251,7 +253,7 @@ end -- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used. -- @return TASK function TASK:GetGoalCount( GoalVerb ) - self:F() + self:F2() if not GoalVerb then GoalVerb = self.GoalVerb end @@ -266,7 +268,7 @@ end -- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used. -- @return TASK function TASK:GetGoalPercentage( GoalVerb ) - self:F() + self:F2() if not GoalVerb then GoalVerb = self.GoalVerb end @@ -279,15 +281,16 @@ end --- Returns if all the Goals of the TASK were achieved. -- @return bool -function TASK:IsGoalReached( ) +function TASK:IsGoalReached() + self:F2() local GoalReached = true for GoalVerb, Goals in pairs( self.GoalTasks ) do - self:T( { "GoalVerb", GoalVerb } ) + self:T2( { "GoalVerb", GoalVerb } ) if self:Goal( GoalVerb ) then local GoalToDo = self:GetGoalTotal( GoalVerb ) - self:GetGoalCount( GoalVerb ) - self:T( "GoalToDo = " .. GoalToDo ) + self:T2( "GoalToDo = " .. GoalToDo ) if GoalToDo <= 0 then else GoalReached = false @@ -298,7 +301,7 @@ function TASK:IsGoalReached( ) end end - self:T( GoalReached ) + self:T( { GoalReached, self.GoalTasks } ) return GoalReached end @@ -307,7 +310,7 @@ end -- @param string GoalTask is a text describing the Goal of the TASK to be achieved. -- @param number GoalIncrease is a number by which the Goal achievement is increasing. function TASK:AddGoalCompletion( GoalVerb, GoalTask, GoalIncrease ) - self:F( { GoalVerb, GoalTask, GoalIncrease } ) + self:F2( { GoalVerb, GoalTask, GoalIncrease } ) if self:Goal( GoalVerb ) then self.GoalTasks[GoalVerb].Goals[#self.GoalTasks[GoalVerb].Goals+1] = GoalTask @@ -320,7 +323,7 @@ end -- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used. -- @return string Goals function TASK:GetGoalCompletion( GoalVerb ) - self:F( { GoalVerb } ) + self:F2( { GoalVerb } ) if self:Goal( GoalVerb ) then local Goals = "" diff --git a/Test Missions/Moose_Test_DESTROY/MOOSE_Test_DESTROY.miz b/Test Missions/Moose_Test_DESTROY/MOOSE_Test_DESTROY.miz new file mode 100644 index 0000000000000000000000000000000000000000..af1132ad39a04542af7ebde70d78516cc7dca145 GIT binary patch literal 29167 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