diff --git a/Embedded/Moose_Create_Embedded.bat b/Embedded/Moose_Create_Embedded.bat index 7211a207d..7dfa51470 100644 --- a/Embedded/Moose_Create_Embedded.bat +++ b/Embedded/Moose_Create_Embedded.bat @@ -2,6 +2,7 @@ rem Generate Moose_Embedded.lua copy /b ..\Moose\Routines.lua ^ + ..\Moose\Base.lua ^ + + ..\Moose\Event.lua ^ + ..\Moose\Menu.lua ^ + ..\Moose\Group.lua ^ + ..\Moose\Unit.lua ^ diff --git a/Embedded/Moose_Embedded.lua b/Embedded/Moose_Embedded.lua index 5f2fa13cb..7ab06e4d4 100644 --- a/Embedded/Moose_Embedded.lua +++ b/Embedded/Moose_Embedded.lua @@ -2624,6 +2624,8 @@ FORMATION = { Cone = "Cone" } + + --- The base constructor. This is the top top class of all classed defined within the MOOSE. -- Any new class needs to be derived from this class for proper inheritance. -- @param #BASE self @@ -2718,6 +2720,18 @@ function BASE:AddEvent( Event, EventFunction ) return self end +--- Returns the event dispatcher +-- @param #BASE self +-- @return Event#EVENT +function BASE:Event() + + return _EventDispatcher +end + + + + + --- Enable the event listeners for the class. -- @param #BASE self -- @return #BASE @@ -2831,8 +2845,8 @@ end -- @param #BASE self -- @param DCSTypes#Event event function BASE:onEvent(event) + --self:F( { BaseEventCodes[event.id], event } ) - --env.info( 'onEvent Table self = ' .. tostring(self) ) if self then for EventID, EventObject in pairs( self.Events ) do if EventObject.EventEnabled then @@ -2847,8 +2861,8 @@ function BASE:onEvent(event) if event.target and event.target:isExist() then event.TgtUnitName = event.target:getName() end - self:T( { BaseEventCodes[event.id], event } ) - EventObject.EventFunction( self, event ) + --self:T( { BaseEventCodes[event.id], event } ) + --EventObject.EventFunction( self, event ) end end end @@ -2909,7 +2923,7 @@ function BASE:F( Arguments ) if DebugInfoFrom then LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d\(%6d\)/%1s:%20s%05d.%s\(%s\)" , LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) end end end @@ -2957,7 +2971,7 @@ function BASE:T( Arguments ) if DebugInfoFrom then LineFrom = DebugInfoFrom.currentline end - env.info( string.format( "%6d\(%6d\)/%1s:%20s%05d.%s" , LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s" , LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) ) end end end @@ -3000,11 +3014,500 @@ function BASE:E( Arguments ) local LineCurrent = DebugInfoCurrent.currentline local LineFrom = DebugInfoFrom.currentline - env.info( string.format( "%6d\(%6d\)/%1s:%20s%05d.%s\(%s\)" , LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) + env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) end +--- The EVENT class models an efficient event handling process between other classes and its units, weapons. +-- @module Event +-- @author FlightControl + +Include.File( "Routines" ) +Include.File( "Base" ) + +--- The EVENT structure +-- @type EVENT +-- @field #EVENT.Events Events +EVENT = { + ClassName = "EVENT", + ClassID = 0, +} + +local _EVENTCODES = { + "S_EVENT_SHOT", + "S_EVENT_HIT", + "S_EVENT_TAKEOFF", + "S_EVENT_LAND", + "S_EVENT_CRASH", + "S_EVENT_EJECTION", + "S_EVENT_REFUELING", + "S_EVENT_DEAD", + "S_EVENT_PILOT_DEAD", + "S_EVENT_BASE_CAPTURED", + "S_EVENT_MISSION_START", + "S_EVENT_MISSION_END", + "S_EVENT_TOOK_CONTROL", + "S_EVENT_REFUELING_STOP", + "S_EVENT_BIRTH", + "S_EVENT_HUMAN_FAILURE", + "S_EVENT_ENGINE_STARTUP", + "S_EVENT_ENGINE_SHUTDOWN", + "S_EVENT_PLAYER_ENTER_UNIT", + "S_EVENT_PLAYER_LEAVE_UNIT", + "S_EVENT_PLAYER_COMMENT", + "S_EVENT_SHOOTING_START", + "S_EVENT_SHOOTING_END", + "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 +-- @field #number IniUnit + +function EVENT:New() + local self = BASE:Inherit( self, BASE:New() ) + self:F() + self.EventHandler = world.addEventHandler( self ) + return self +end + +function EVENT:EventText( EventID ) + + local EventText = _EVENTCODES[EventID] + + return EventText +end + + +--- Initializes the Events structure for the event +-- @param #EVENT self +-- @param DCSWorld#world.event EventID +-- @param #string EventClass +-- @return #EVENT.Events +function EVENT:Init( EventID, EventClass ) + self:F3( { _EVENTCODES[EventID], EventClass } ) + if not self.Events[EventID] then + self.Events[EventID] = {} + end + if not self.Events[EventID][EventClass] then + self.Events[EventID][EventClass] = {} + end + return self.Events[EventID][EventClass] +end + + +--- Create an OnDead event handler for a group +-- @param #EVENT self +-- @param #table EventTemplate +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param EventSelf The self instance of the class for which the event is. +-- @param #function OnEventFunction +-- @return #EVENT +function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, OnEventFunction ) + self:F2( EventTemplate.name ) + + for EventUnitID, EventUnit in pairs( EventTemplate.units ) do + OnEventFunction( self, EventUnit.name, EventFunction, EventSelf ) + end + 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:F2( { 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 +-- @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. +-- @param EventID +-- @return #EVENT +function EVENT:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, EventID ) + self:F2( EventDCSUnitName ) + + local Event = self:Init( EventID, EventSelf:GetClassNameAndID() ) + if not Event.IniUnit then + Event.IniUnit = {} + end + Event.IniUnit[EventDCSUnitName] = {} + Event.IniUnit[EventDCSUnitName].EventFunction = EventFunction + Event.IniUnit[EventDCSUnitName].EventSelf = EventSelf + return self +end + + +--- Create an OnBirth event handler for a group +-- @param #EVENT self +-- @param Group#GROUP EventGroup +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventSelf ) + self:F( EventTemplate.name ) + + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnBirthForUnit ) + + return self +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() + + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_BIRTH ) + + return self +end + +--- Set a new listener for an S_EVENT_BIRTH event. +-- @param #EVENT self +-- @param #string EventDCSUnitName The id of the unit for the event to be handled. +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf +-- @return #EVENT +function EVENT:OnBirthForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_BIRTH ) + + return self +end + +--- Create an OnCrash event handler for a group +-- @param #EVENT self +-- @param Group#GROUP EventGroup +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventSelf ) + self:F( EventTemplate.name ) + + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnCrashForUnit ) + + return self +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() + + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_CRASH ) + + return self +end + +--- Set a new listener for an S_EVENT_CRASH 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:OnCrashForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_CRASH ) + + return self +end + +--- Create an OnDead event handler for a group +-- @param #EVENT self +-- @param Group#GROUP EventGroup +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventSelf ) + self:F( EventTemplate.name ) + + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnDeadForUnit ) + + return self +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() + + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_DEAD ) + + return self +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:OnDeadForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_DEAD ) + + return self +end + +--- Set a new listener for an S_EVENT_PILOT_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:OnPilotDeadForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_PILOT_DEAD ) + + return self +end + +--- Create an OnDead event handler for a group +-- @param #EVENT self +-- @param #table EventTemplate +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventSelf ) + self:F( EventTemplate.name ) + + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnLandForUnit ) + + return self +end + +--- Set a new listener for an S_EVENT_LAND 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:OnLandForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_LAND ) + + return self +end + +--- Create an OnDead event handler for a group +-- @param #EVENT self +-- @param #table EventTemplate +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventSelf ) + self:F( EventTemplate.name ) + + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnTakeOffForUnit ) + + return self +end + +--- 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:OnTakeOffForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_TAKEOFF ) + + return self +end + +--- Create an OnDead event handler for a group +-- @param #EVENT self +-- @param #table EventTemplate +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param EventSelf The self instance of the class for which the event is. +-- @return #EVENT +function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventSelf ) + self:F( EventTemplate.name ) + + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnEngineShutDownForUnit ) + + return self +end + +--- 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:OnEngineShutDownForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_SHUTDOWN ) + + return self +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 ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_STARTUP ) + + return self +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() + + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_SHOT ) + + return self +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 ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_SHOT ) + + return self +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() + + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_HIT ) + + return self +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 ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_HIT ) + + return self +end + + + +function EVENT:onEvent( Event ) + self:F( { _EVENTCODES[Event.id], Event } ) + + if self and self.Events and self.Events[Event.id] then + 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 + 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.IniDCSUnitName and EventData.IniUnit and EventData.IniUnit[Event.IniDCSUnitName] then + self:T2( { "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:T2( { "Calling event function for class ", ClassName } ) + EventData.EventFunction( EventData.EventSelf, Event ) + end + end + end + end +end + + + +--- Declare the event dispatcher based on the EVENT class +_EventDispatcher = EVENT:New() -- #EVENT + --- Encapsulation of DCS World Menu system in a set of MENU classes. -- @module Menu @@ -4993,36 +5496,37 @@ Include.File( "Routines" ) Include.File( "Base" ) Include.File( "Menu" ) Include.File( "Group" ) +Include.File( "Event" ) --- The DATABASE class -- @type DATABASE -- @extends Base#BASE DATABASE = { - ClassName = "DATABASE", - Units = {}, - Groups = {}, - NavPoints = {}, - Statics = {}, - Players = {}, - ActivePlayers = {}, - ClientsByName = {}, - ClientsByID = {}, + ClassName = "DATABASE", + Units = {}, + Groups = {}, + NavPoints = {}, + Statics = {}, + Players = {}, + ActivePlayers = {}, + ClientsByName = {}, + ClientsByID = {}, } -DATABASECoalition = -{ - [1] = "Red", - [2] = "Blue", -} +local _DATABASECoalition = + { + [1] = "Red", + [2] = "Blue", + } -DATABASECategory = -{ - [Unit.Category.AIRPLANE] = "Plane", - [Unit.Category.HELICOPTER] = "Helicopter", - [Unit.Category.GROUND_UNIT] = "Vehicle", - [Unit.Category.SHIP] = "Ship", - [Unit.Category.STRUCTURE] = "Structure", -} +local _DATABASECategory = + { + [Unit.Category.AIRPLANE] = "Plane", + [Unit.Category.HELICOPTER] = "Helicopter", + [Unit.Category.GROUND_UNIT] = "Vehicle", + [Unit.Category.SHIP] = "Ship", + [Unit.Category.STRUCTURE] = "Structure", + } --- Creates a new DATABASE Object to administer the Groups defined and alive within the DCSRTE. @@ -5032,83 +5536,79 @@ DATABASECategory = -- DBObject = DATABASE:New() function DATABASE:New() - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) - - self.Navpoints = {} - self.Units = {} - --Build routines.db.units and self.Navpoints - for coa_name, coa_data in pairs(env.mission.coalition) do + -- Inherits from BASE + local self = BASE:Inherit( self, BASE:New() ) - if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then - self.Units[coa_name] = {} - - ---------------------------------------------- - -- build nav points DB - self.Navpoints[coa_name] = {} - if coa_data.nav_points then --navpoints - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - self.Navpoints[coa_name][nav_ind] = routines.utils.deepCopy(nav_data) + self.Navpoints = {} + self.Units = {} + --Build routines.db.units and self.Navpoints + for coa_name, coa_data in pairs(env.mission.coalition) do - self.Navpoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. - self.Navpoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. - self.Navpoints[coa_name][nav_ind]['point']['x'] = nav_data.x - self.Navpoints[coa_name][nav_ind]['point']['y'] = 0 - self.Navpoints[coa_name][nav_ind]['point']['z'] = nav_data.y - end - end - end - ------------------------------------------------- - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local countryName = string.lower(cntry_data.name) - self.Units[coa_name][countryName] = {} - self.Units[coa_name][countryName]["countryId"] = cntry_data.id + if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then + self.Units[coa_name] = {} - if type(cntry_data) == 'table' then --just making sure - - for obj_type_name, obj_type_data in pairs(cntry_data) do - - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check - - local category = obj_type_name - - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - - self.Units[coa_name][countryName][category] = {} - - for group_num, GroupTemplate in pairs(obj_type_data.group) do - - if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group - self:_RegisterGroup( GroupTemplate ) - end --if GroupTemplate and GroupTemplate.units then - end --for group_num, GroupTemplate in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --if type(cntry_data) == 'table' then - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do + ---------------------------------------------- + -- build nav points DB + self.Navpoints[coa_name] = {} + if coa_data.nav_points then --navpoints + for nav_ind, nav_data in pairs(coa_data.nav_points) 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 ) - - self:AddEvent( world.event.S_EVENT_HIT, self.OnHit) + if type(nav_data) == 'table' then + self.Navpoints[coa_name][nav_ind] = routines.utils.deepCopy(nav_data) - self:EnableEvents() + self.Navpoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. + self.Navpoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. + self.Navpoints[coa_name][nav_ind]['point']['x'] = nav_data.x + self.Navpoints[coa_name][nav_ind]['point']['y'] = 0 + self.Navpoints[coa_name][nav_ind]['point']['z'] = nav_data.y + end + end + end + ------------------------------------------------- + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do - self.SchedulerId = routines.scheduleFunction( DATABASE._FollowPlayers, { self }, 0, 5 ) - - self:ScoreMenu() - - - return self + local countryName = string.lower(cntry_data.name) + self.Units[coa_name][countryName] = {} + self.Units[coa_name][countryName]["countryId"] = cntry_data.id + + if type(cntry_data) == 'table' then --just making sure + + for obj_type_name, obj_type_data in pairs(cntry_data) do + + if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check + + local category = obj_type_name + + if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! + + self.Units[coa_name][countryName][category] = {} + + for group_num, GroupTemplate in pairs(obj_type_data.group) do + + if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group + self:_RegisterGroup( GroupTemplate ) + end --if GroupTemplate and GroupTemplate.units then + end --for group_num, GroupTemplate in pairs(obj_type_data.group) do + end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then + end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then + end --for obj_type_name, obj_type_data in pairs(cntry_data) do + end --if type(cntry_data) == 'table' then + end --for cntry_id, cntry_data in pairs(coa_data.country) do + end --if coa_data.country then --there is a country table + end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then + end --for coa_name, coa_data in pairs(mission.coalition) do + + --self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) + _EventDispatcher:OnDead( self._EventOnDeadOrCrash, self ) + _EventDispatcher:OnCrash( self._EventOnDeadOrCrash, self ) + _EventDispatcher:OnHit( self._EventOnHit, self ) + + self.SchedulerId = routines.scheduleFunction( DATABASE._FollowPlayers, { self }, 0, 5 ) + + self:ScoreMenu() + + return self end @@ -5118,49 +5618,49 @@ end -- This method is used by the SPAWN class. function DATABASE:Spawn( SpawnTemplate ) - self:T( { SpawnTemplate.SpawnCountryID, SpawnTemplate.SpawnCategoryID, SpawnTemplate.name } ) - - -- Copy the spawn variables of the template in temporary storage, nullify, and restore the spawn variables. - local SpawnCoalitionID = SpawnTemplate.SpawnCoalitionID - local SpawnCountryID = SpawnTemplate.SpawnCountryID - local SpawnCategoryID = SpawnTemplate.SpawnCategoryID - - -- Nullify - SpawnTemplate.SpawnCoalitionID = nil - SpawnTemplate.SpawnCountryID = nil - SpawnTemplate.SpawnCategoryID = nil - - self:_RegisterGroup( SpawnTemplate ) - coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate ) + self:T( { SpawnTemplate.SpawnCountryID, SpawnTemplate.SpawnCategoryID, SpawnTemplate.name } ) - -- Restore - SpawnTemplate.SpawnCoalitionID = SpawnCoalitionID - SpawnTemplate.SpawnCountryID = SpawnCountryID - SpawnTemplate.SpawnCategoryID = SpawnCategoryID + -- Copy the spawn variables of the template in temporary storage, nullify, and restore the spawn variables. + local SpawnCoalitionID = SpawnTemplate.SpawnCoalitionID + local SpawnCountryID = SpawnTemplate.SpawnCountryID + local SpawnCategoryID = SpawnTemplate.SpawnCategoryID - - local SpawnGroup = GROUP:New( Group.getByName( SpawnTemplate.name ) ) - return SpawnGroup + -- Nullify + SpawnTemplate.SpawnCoalitionID = nil + SpawnTemplate.SpawnCountryID = nil + SpawnTemplate.SpawnCategoryID = nil + + self:_RegisterGroup( SpawnTemplate ) + coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate ) + + -- Restore + SpawnTemplate.SpawnCoalitionID = SpawnCoalitionID + SpawnTemplate.SpawnCountryID = SpawnCountryID + SpawnTemplate.SpawnCategoryID = SpawnCategoryID + + + local SpawnGroup = GROUP:New( Group.getByName( SpawnTemplate.name ) ) + return SpawnGroup end --- Set a status to a Group within the Database, this to check crossing events for example. function DATABASE:SetStatusGroup( GroupName, Status ) - self:F( Status ) + self:F( Status ) - self.Groups[GroupName].Status = Status + self.Groups[GroupName].Status = Status end --- Get a status to a Group within the Database, this to check crossing events for example. function DATABASE:GetStatusGroup( GroupName ) - self:F( Status ) + self:F( Status ) - if self.Groups[GroupName] then - return self.Groups[GroupName].Status - else - return "" - end + if self.Groups[GroupName] then + return self.Groups[GroupName].Status + else + return "" + end end @@ -5171,35 +5671,35 @@ end --- Registers new Group Templates within the DATABASE Object. function DATABASE:_RegisterGroup( GroupTemplate ) - local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name) + local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name) - if not self.Groups[GroupTemplateName] then - self.Groups[GroupTemplateName] = {} - self.Groups[GroupTemplateName].Status = nil - end - self.Groups[GroupTemplateName].GroupName = GroupTemplateName - self.Groups[GroupTemplateName].Template = GroupTemplate - self.Groups[GroupTemplateName].groupId = GroupTemplate.groupId - self.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units - self.Groups[GroupTemplateName].Units = GroupTemplate.units - - self:T( { "Group", self.Groups[GroupTemplateName].GroupName, self.Groups[GroupTemplateName].UnitCount } ) - - for unit_num, UnitTemplate in pairs(GroupTemplate.units) do - - local UnitTemplateName = env.getValueDictByKey(UnitTemplate.name) - self.Units[UnitTemplateName] = {} - self.Units[UnitTemplateName].UnitName = UnitTemplateName - self.Units[UnitTemplateName].Template = UnitTemplate - self.Units[UnitTemplateName].GroupName = GroupTemplateName - self.Units[UnitTemplateName].GroupTemplate = GroupTemplate - self.Units[UnitTemplateName].GroupId = GroupTemplate.groupId - if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then - self.ClientsByName[UnitTemplateName] = UnitTemplate - self.ClientsByID[UnitTemplate.unitId] = UnitTemplate - end - self:T( { "Unit", self.Units[UnitTemplateName].UnitName } ) - end + if not self.Groups[GroupTemplateName] then + self.Groups[GroupTemplateName] = {} + self.Groups[GroupTemplateName].Status = nil + end + self.Groups[GroupTemplateName].GroupName = GroupTemplateName + self.Groups[GroupTemplateName].Template = GroupTemplate + self.Groups[GroupTemplateName].groupId = GroupTemplate.groupId + self.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units + self.Groups[GroupTemplateName].Units = GroupTemplate.units + + self:T( { "Group", self.Groups[GroupTemplateName].GroupName, self.Groups[GroupTemplateName].UnitCount } ) + + for unit_num, UnitTemplate in pairs(GroupTemplate.units) do + + local UnitTemplateName = env.getValueDictByKey(UnitTemplate.name) + self.Units[UnitTemplateName] = {} + self.Units[UnitTemplateName].UnitName = UnitTemplateName + self.Units[UnitTemplateName].Template = UnitTemplate + self.Units[UnitTemplateName].GroupName = GroupTemplateName + self.Units[UnitTemplateName].GroupTemplate = GroupTemplate + self.Units[UnitTemplateName].GroupId = GroupTemplate.groupId + if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then + self.ClientsByName[UnitTemplateName] = UnitTemplate + self.ClientsByID[UnitTemplate.unitId] = UnitTemplate + end + self:T( { "Unit", self.Units[UnitTemplateName].UnitName } ) + end end @@ -5208,96 +5708,94 @@ end --- Track DCSRTE DEAD or CRASH events for the internal scoring. -function DATABASE:OnDeadOrCrash( event ) - self:F( { event } ) +-- @param #DATABASE self +-- @param Event#EVENTDATA Event +function DATABASE:_EventOnDeadOrCrash( Event ) + self:F( { Event } ) - local TargetUnit = nil - local TargetGroup = nil - local TargetUnitName = "" - local TargetGroupName = "" - local TargetPlayerName = "" - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil + local TargetUnit = nil + local TargetGroup = nil + local TargetUnitName = "" + local TargetGroupName = "" + local TargetPlayerName = "" + local TargetCoalition = nil + local TargetCategory = nil + local TargetType = nil + local TargetUnitCoalition = nil + local TargetUnitCategory = nil + local TargetUnitType = nil - if event.initiator and Object.getCategory(event.initiator) == Object.Category.UNIT then - - TargetUnit = event.initiator - TargetGroup = Unit.getGroup( TargetUnit ) - TargetUnitDesc = TargetUnit:getDesc() - - TargetUnitName = TargetUnit:getName() - if TargetGroup and TargetGroup:isExist() then - TargetGroupName = TargetGroup:getName() - end - TargetPlayerName = TargetUnit:getPlayerName() + if Event.IniDCSUnit then - TargetCoalition = TargetUnit:getCoalition() - --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnitDesc.category -- Workaround - TargetType = TargetUnit:getTypeName() + TargetUnit = Event.IniDCSUnit + TargetUnitName = Event.IniDCSUnitName + TargetGroup = Event.IniDCSGroup + TargetGroupName = Event.IniDCSGroupName + TargetPlayerName = TargetUnit:getPlayerName() - TargetUnitCoalition = DATABASECoalition[TargetCoalition] - TargetUnitCategory = DATABASECategory[TargetCategory] - TargetUnitType = TargetType + TargetCoalition = TargetUnit:getCoalition() + --TargetCategory = TargetUnit:getCategory() + TargetCategory = TargetUnit:getDesc().category -- Workaround + TargetType = TargetUnit:getTypeName() - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) - end + TargetUnitCoalition = _DATABASECoalition[TargetCoalition] + TargetUnitCategory = _DATABASECategory[T1argetCategory] + TargetUnitType = TargetType - for PlayerName, PlayerData in pairs( self.Players ) do - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got killed" ) + self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) + end - -- Some variables - local InitUnitName = PlayerData.UnitName - local InitUnitType = PlayerData.UnitType - local InitCoalition = PlayerData.UnitCoalition - local InitCategory = PlayerData.UnitCategory - local InitUnitCoalition = DATABASECoalition[InitCoalition] - local InitUnitCategory = DATABASECategory[InitCategory] - - self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) + for PlayerName, PlayerData in pairs( self.Players ) do + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Something got killed" ) - -- What is he hitting? - if TargetCategory then - if PlayerData and PlayerData.Hit and PlayerData.Hit[TargetCategory] and PlayerData.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? - if not PlayerData.Kill[TargetCategory] then - PlayerData.Kill[TargetCategory] = {} - end - if not PlayerData.Kill[TargetCategory][TargetType] then - PlayerData.Kill[TargetCategory][TargetType] = {} - PlayerData.Kill[TargetCategory][TargetType].Score = 0 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = 0 - PlayerData.Kill[TargetCategory][TargetType].Penalty = 0 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = 0 - end + -- Some variables + local InitUnitName = PlayerData.UnitName + local InitUnitType = PlayerData.UnitType + local InitCoalition = PlayerData.UnitCoalition + local InitCategory = PlayerData.UnitCategory + local InitUnitCoalition = _DATABASECoalition[InitCoalition] + local InitUnitCategory = _DATABASECategory[InitCategory] - if InitCoalition == TargetCoalition then - PlayerData.Penalty = PlayerData.Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].Penalty = PlayerData.Kill[TargetCategory][TargetType].Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = PlayerData.Kill[TargetCategory][TargetType].PenaltyKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill .. " times. Penalty: -" .. PlayerData.Kill[TargetCategory][TargetType].Penalty .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - "", 5, "/PENALTY" .. PlayerName .. "/" .. InitUnitName ):ToAll() - self:ScoreAdd( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - PlayerData.Score = PlayerData.Score + 10 - PlayerData.Kill[TargetCategory][TargetType].Score = PlayerData.Kill[TargetCategory][TargetType].Score + 10 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = PlayerData.Kill[TargetCategory][TargetType].ScoreKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].ScoreKill .. " times. Score: " .. PlayerData.Kill[TargetCategory][TargetType].Score .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - "", 5, "/SCORE" .. PlayerName .. "/" .. InitUnitName ):ToAll() - self:ScoreAdd( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end - end - end - end + self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) + + -- What is he hitting? + if TargetCategory then + if PlayerData and PlayerData.Hit and PlayerData.Hit[TargetCategory] and PlayerData.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? + if not PlayerData.Kill[TargetCategory] then + PlayerData.Kill[TargetCategory] = {} + end + if not PlayerData.Kill[TargetCategory][TargetType] then + PlayerData.Kill[TargetCategory][TargetType] = {} + PlayerData.Kill[TargetCategory][TargetType].Score = 0 + PlayerData.Kill[TargetCategory][TargetType].ScoreKill = 0 + PlayerData.Kill[TargetCategory][TargetType].Penalty = 0 + PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = 0 + end + + if InitCoalition == TargetCoalition then + PlayerData.Penalty = PlayerData.Penalty + 25 + PlayerData.Kill[TargetCategory][TargetType].Penalty = PlayerData.Kill[TargetCategory][TargetType].Penalty + 25 + PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = PlayerData.Kill[TargetCategory][TargetType].PenaltyKill + 1 + MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + PlayerData.Kill[TargetCategory][TargetType].PenaltyKill .. " times. Penalty: -" .. PlayerData.Kill[TargetCategory][TargetType].Penalty .. + ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, + "", 5, "/PENALTY" .. PlayerName .. "/" .. InitUnitName ):ToAll() + self:ScoreAdd( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + PlayerData.Score = PlayerData.Score + 10 + PlayerData.Kill[TargetCategory][TargetType].Score = PlayerData.Kill[TargetCategory][TargetType].Score + 10 + PlayerData.Kill[TargetCategory][TargetType].ScoreKill = PlayerData.Kill[TargetCategory][TargetType].ScoreKill + 1 + MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + PlayerData.Kill[TargetCategory][TargetType].ScoreKill .. " times. Score: " .. PlayerData.Kill[TargetCategory][TargetType].Score .. + ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, + "", 5, "/SCORE" .. PlayerName .. "/" .. InitUnitName ):ToAll() + self:ScoreAdd( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + end + end + end + end end @@ -5307,20 +5805,20 @@ end --- Follows new players entering Clients within the DCSRTE. function DATABASE:_FollowPlayers() - self:F3( "_FollowPlayers" ) + self:F3( "_FollowPlayers" ) - local ClientUnit = 0 - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } - local unitId - local unitData - local AlivePlayerUnits = {} - - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - self:T3( { "_FollowPlayers", CoalitionData } ) - for UnitId, UnitData in pairs( CoalitionData ) do - self:_AddPlayerFromUnit( UnitData ) - end - end + local ClientUnit = 0 + local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } + local unitId + local unitData + local AlivePlayerUnits = {} + + for CoalitionId, CoalitionData in pairs( CoalitionsData ) do + self:T3( { "_FollowPlayers", CoalitionData } ) + for UnitId, UnitData in pairs( CoalitionData ) do + self:_AddPlayerFromUnit( UnitData ) + end + end end @@ -5330,102 +5828,102 @@ end --- Add a new player entering a Unit. function DATABASE:_AddPlayerFromUnit( UnitData ) - self:F( UnitData ) + self:F( UnitData ) - if UnitData:isExist() then - local UnitName = UnitData:getName() - local PlayerName = UnitData:getPlayerName() - local UnitDesc = UnitData:getDesc() - local UnitCategory = UnitDesc.category - local UnitCoalition = UnitData:getCoalition() - local UnitTypeName = UnitData:getTypeName() + if UnitData:isExist() then + local UnitName = UnitData:getName() + local PlayerName = UnitData:getPlayerName() + local UnitDesc = UnitData:getDesc() + local UnitCategory = UnitDesc.category + local UnitCoalition = UnitData:getCoalition() + local UnitTypeName = UnitData:getTypeName() - self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) + self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) - if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... - self.Players[PlayerName] = {} - self.Players[PlayerName].Hit = {} - self.Players[PlayerName].Kill = {} - self.Players[PlayerName].Mission = {} - - -- for CategoryID, CategoryName in pairs( DATABASECategory ) do - -- self.Players[PlayerName].Hit[CategoryID] = {} - -- self.Players[PlayerName].Kill[CategoryID] = {} - -- end - self.Players[PlayerName].HitPlayers = {} - self.Players[PlayerName].HitUnits = {} + if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... + self.Players[PlayerName] = {} + self.Players[PlayerName].Hit = {} + self.Players[PlayerName].Kill = {} + self.Players[PlayerName].Mission = {} + + -- for CategoryID, CategoryName in pairs( DATABASECategory ) do + -- self.Players[PlayerName].Hit[CategoryID] = {} + -- self.Players[PlayerName].Kill[CategoryID] = {} + -- end + self.Players[PlayerName].HitPlayers = {} + self.Players[PlayerName].HitUnits = {} self.Players[PlayerName].Score = 0 - self.Players[PlayerName].Penalty = 0 - self.Players[PlayerName].PenaltyCoalition = 0 - self.Players[PlayerName].PenaltyWarning = 0 - end + self.Players[PlayerName].Penalty = 0 + self.Players[PlayerName].PenaltyCoalition = 0 + self.Players[PlayerName].PenaltyWarning = 0 + end - if not self.Players[PlayerName].UnitCoalition then - self.Players[PlayerName].UnitCoalition = UnitCoalition - else - if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then - self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 - self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. DATABASECoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. DATABASECoalition[UnitCoalition] .. - "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", - "", - 2, - "/PENALTYCOALITION" .. PlayerName - ):ToAll() - self:ScoreAdd( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, DATABASECoalition[self.Players[PlayerName].UnitCoalition], DATABASECategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, - UnitName, DATABASECoalition[UnitCoalition], DATABASECategory[UnitCategory], UnitData:getTypeName() ) - end - end - self.Players[PlayerName].UnitName = UnitName - self.Players[PlayerName].UnitCoalition = UnitCoalition - self.Players[PlayerName].UnitCategory = UnitCategory - self.Players[PlayerName].UnitType = UnitTypeName + if not self.Players[PlayerName].UnitCoalition then + self.Players[PlayerName].UnitCoalition = UnitCoalition + else + if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then + self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 + self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 + MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. _DATABASECoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _DATABASECoalition[UnitCoalition] .. + "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", + "", + 2, + "/PENALTYCOALITION" .. PlayerName + ):ToAll() + self:ScoreAdd( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _DATABASECoalition[self.Players[PlayerName].UnitCoalition], _DATABASECategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, + UnitName, _DATABASECoalition[UnitCoalition], _DATABASECategory[UnitCategory], UnitData:getTypeName() ) + end + end + self.Players[PlayerName].UnitName = UnitName + self.Players[PlayerName].UnitCoalition = UnitCoalition + self.Players[PlayerName].UnitCategory = UnitCategory + self.Players[PlayerName].UnitType = UnitTypeName if self.Players[PlayerName].Penalty > 100 then if self.Players[PlayerName].PenaltyWarning < 1 then - MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than 150, you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, - "", - 30, - "/PENALTYCOALITION" .. PlayerName - ):ToAll() + MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than 150, you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, + "", + 30, + "/PENALTYCOALITION" .. PlayerName + ):ToAll() self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 end end - + if self.Players[PlayerName].Penalty > 150 then ClientGroup = GROUP:NewFromDCSUnit( UnitData ) ClientGroup:Destroy() - MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", - "", - 10, - "/PENALTYCOALITION" .. PlayerName - ):ToAll() + MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", + "", + 10, + "/PENALTYCOALITION" .. PlayerName + ):ToAll() end - - end + + end end --- Registers Scores the players completing a Mission Task. function DATABASE:_AddMissionTaskScore( PlayerUnit, MissionName, Score ) - self:F( { PlayerUnit, MissionName, Score } ) + self:F( { PlayerUnit, MissionName, Score } ) - local PlayerName = PlayerUnit:getPlayerName() - - if not self.Players[PlayerName].Mission[MissionName] then - self.Players[PlayerName].Mission[MissionName] = {} - self.Players[PlayerName].Mission[MissionName].ScoreTask = 0 - self.Players[PlayerName].Mission[MissionName].ScoreMission = 0 - end - - self:T( PlayerName ) - self:T( self.Players[PlayerName].Mission[MissionName] ) + local PlayerName = PlayerUnit:getPlayerName() - self.Players[PlayerName].Score = self.Players[PlayerName].Score + Score - self.Players[PlayerName].Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score + if not self.Players[PlayerName].Mission[MissionName] then + self.Players[PlayerName].Mission[MissionName] = {} + self.Players[PlayerName].Mission[MissionName].ScoreTask = 0 + self.Players[PlayerName].Mission[MissionName].ScoreMission = 0 + end - MESSAGE:New( "Player '" .. PlayerName .. "' has finished another Task in Mission '" .. MissionName .. "'. " .. - Score .. " Score points added.", + self:T( PlayerName ) + self:T( self.Players[PlayerName].Mission[MissionName] ) + + self.Players[PlayerName].Score = self.Players[PlayerName].Score + Score + self.Players[PlayerName].Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score + + MESSAGE:New( "Player '" .. PlayerName .. "' has finished another Task in Mission '" .. MissionName .. "'. " .. + Score .. " Score points added.", "", 20, "/SCORETASK" .. PlayerName ):ToAll() _Database:ScoreAdd( PlayerName, "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:getName() ) @@ -5434,19 +5932,19 @@ end --- Registers Mission Scores for possible multiple players that contributed in the Mission. function DATABASE:_AddMissionScore( MissionName, Score ) - self:F( { PlayerUnit, MissionName, Score } ) + self:F( { PlayerUnit, MissionName, Score } ) - for PlayerName, PlayerData in pairs( self.Players ) do - - if PlayerData.Mission[MissionName] then - PlayerData.Score = PlayerData.Score + Score - PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score - MESSAGE:New( "Player '" .. PlayerName .. "' has finished Mission '" .. MissionName .. "'. " .. - Score .. " Score points added.", - "", 20, "/SCOREMISSION" .. PlayerName ):ToAll() - _Database:ScoreAdd( PlayerName, "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) - end - end + for PlayerName, PlayerData in pairs( self.Players ) do + + if PlayerData.Mission[MissionName] then + PlayerData.Score = PlayerData.Score + Score + PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score + MESSAGE:New( "Player '" .. PlayerName .. "' has finished Mission '" .. MissionName .. "'. " .. + Score .. " Score points added.", + "", 20, "/SCOREMISSION" .. PlayerName ):ToAll() + _Database:ScoreAdd( PlayerName, "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) + end + end end @@ -5454,373 +5952,367 @@ end -- @section Events -function DATABASE:OnHit( event ) - self:F( { event } ) +--- Handles the OnHit event for the scoring. +-- @param #DATABASE self +-- @param Event#EVENTDATA Event +function DATABASE:_EventOnHit( Event ) + self:F( { Event } ) - local InitUnit = nil - local InitUnitName = "" - local InitGroupName = "" - local InitPlayerName = "dummy" + local InitUnit = nil + local InitUnitName = "" + local InitGroup = nil + local InitGroupName = "" + local InitPlayerName = "dummy" - local InitCoalition = nil - local InitCategory = nil - local InitType = nil - local InitUnitCoalition = nil - local InitUnitCategory = nil - local InitUnitType = nil + local InitCoalition = nil + local InitCategory = nil + local InitType = nil + local InitUnitCoalition = nil + local InitUnitCategory = nil + local InitUnitType = nil - local TargetUnit = nil - local TargetUnitName = "" - local TargetGroupName = "" - local TargetPlayerName = "" + local TargetUnit = nil + local TargetUnitName = "" + local TargetGroup = nil + local TargetGroupName = "" + local TargetPlayerName = "" - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil + local TargetCoalition = nil + local TargetCategory = nil + local TargetType = nil + local TargetUnitCoalition = nil + local TargetUnitCategory = nil + local TargetUnitType = nil - if event.initiator and event.initiator:getName() then - - if event.initiator and Object.getCategory(event.initiator) == Object.Category.UNIT then - - InitUnit = event.initiator - InitGroup = Unit.getGroup( InitUnit ) - InitUnitDesc = InitUnit:getDesc() - - InitUnitName = InitUnit:getName() - if InitGroup and InitGroup:isExist() then - InitGroupName = InitGroup:getName() - end - InitPlayerName = InitUnit:getPlayerName() - - InitCoalition = InitUnit:getCoalition() - --InitCategory = InitUnit:getCategory() - InitCategory = InitUnitDesc.category -- Workaround - InitType = InitUnit:getTypeName() + if Event.IniDCSUnit then - InitUnitCoalition = DATABASECoalition[InitCoalition] - InitUnitCategory = DATABASECategory[InitCategory] - InitUnitType = InitType - - self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } ) - self:T( { InitUnitDesc } ) - end + InitUnit = Event.IniDCSUnit + InitUnitName = Event.IniDCSUnitName + InitGroup = Event.IniDCSGroup + InitGroupName = Event.IniDCSGroupName + InitPlayerName = InitUnit:getPlayerName() - - if event.target and Object.getCategory(event.target) == Object.Category.UNIT then - - TargetUnit = event.target - TargetGroup = Unit.getGroup( TargetUnit ) - TargetUnitDesc = TargetUnit:getDesc() - - TargetUnitName = TargetUnit:getName() - if TargetGroup and TargetGroup:isExist() then - TargetGroupName = TargetGroup:getName() - end - TargetPlayerName = TargetUnit:getPlayerName() + InitCoalition = InitUnit:getCoalition() + --TODO: Workaround Client DCS Bug + --InitCategory = InitUnit:getCategory() + InitCategory = InitUnit:getDesc().category + InitType = InitUnit:getTypeName() - TargetCoalition = TargetUnit:getCoalition() - --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnitDesc.category -- Workaround - TargetType = TargetUnit:getTypeName() + InitUnitCoalition = _DATABASECoalition[InitCoalition] + InitUnitCategory = _DATABASECategory[InitCategory] + InitUnitType = InitType - TargetUnitCoalition = DATABASECoalition[TargetCoalition] - TargetUnitCategory = DATABASECategory[TargetCategory] - TargetUnitType = TargetType - - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } ) - self:T( { TargetUnitDesc } ) - end - - if InitPlayerName ~= nil then -- It is a player that is hitting something - self:_AddPlayerFromUnit( InitUnit ) - if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( TargetUnit ) - self.Players[InitPlayerName].HitPlayers = self.Players[InitPlayerName].HitPlayers + 1 - end - - self:T( "Hitting Something" ) - -- What is he hitting? - if TargetCategory then - if not self.Players[InitPlayerName].Hit[TargetCategory] then - self.Players[InitPlayerName].Hit[TargetCategory] = {} - end - if not self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] then - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] = {} - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = 0 - end - local Score = 0 - if InitCoalition == TargetCoalition then - self.Players[InitPlayerName].Penalty = self.Players[InitPlayerName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: -" .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - "", - 2, - "/PENALTY" .. InitPlayerName .. "/" .. InitUnitName - ):ToAll() - self:ScoreAdd( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - self.Players[InitPlayerName].Score = self.Players[InitPlayerName].Score + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score + 1 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit .. " times. Score: " .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - "", - 2, - "/SCORE" .. InitPlayerName .. "/" .. InitUnitName - ):ToAll() - self:ScoreAdd( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end - end - elseif InitPlayerName == nil then -- It is an AI hitting a player??? - - end - end + self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } ) + end + + + if Event.TgtDCSUnit then + + TargetUnit = Event.TgtDCSUnit + TargetUnitName = Event.TgtDCSUnitName + TargetGroup = Event.TgtDCSGroup + TargetGroupName = Event.TgtDCSGroupName + TargetPlayerName = TargetUnit:getPlayerName() + + TargetCoalition = TargetUnit:getCoalition() + --TODO: Workaround Client DCS Bug + --TargetCategory = TargetUnit:getCategory() + TargetCategory = TargetUnit:getDesc().category + TargetType = TargetUnit:getTypeName() + + TargetUnitCoalition = _DATABASECoalition[TargetCoalition] + TargetUnitCategory = _DATABASECategory[TargetCategory] + TargetUnitType = TargetType + + self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } ) + end + + if InitPlayerName ~= nil then -- It is a player that is hitting something + self:_AddPlayerFromUnit( InitUnit ) + if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + self:_AddPlayerFromUnit( TargetUnit ) + self.Players[InitPlayerName].HitPlayers = self.Players[InitPlayerName].HitPlayers + 1 + end + + self:T( "Hitting Something" ) + -- What is he hitting? + if TargetCategory then + if not self.Players[InitPlayerName].Hit[TargetCategory] then + self.Players[InitPlayerName].Hit[TargetCategory] = {} + end + if not self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] then + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] = {} + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = 0 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = 0 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = 0 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = 0 + end + local Score = 0 + if InitCoalition == TargetCoalition then + self.Players[InitPlayerName].Penalty = self.Players[InitPlayerName].Penalty + 10 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty + 10 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit + 1 + MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: -" .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty .. + ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, + "", + 2, + "/PENALTY" .. InitPlayerName .. "/" .. InitUnitName + ):ToAll() + self:ScoreAdd( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + self.Players[InitPlayerName].Score = self.Players[InitPlayerName].Score + 10 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score + 1 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit + 1 + MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit .. " times. Score: " .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score .. + ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, + "", + 2, + "/SCORE" .. InitPlayerName .. "/" .. InitUnitName + ):ToAll() + self:ScoreAdd( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + end + end + elseif InitPlayerName == nil then -- It is an AI hitting a player??? + + end end function DATABASE:ReportScoreAll() -env.info( "Hello World " ) + env.info( "Hello World " ) - local ScoreMessage = "" - local PlayerMessage = "" - - self:T( "Score Report" ) + local ScoreMessage = "" + local PlayerMessage = "" - for PlayerName, PlayerData in pairs( self.Players ) do - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) + self:T( "Score Report" ) - -- Some variables - local InitUnitCoalition = DATABASECoalition[PlayerData.UnitCoalition] - local InitUnitCategory = DATABASECategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = ":\n" - - local ScoreMessageHits = "" + for PlayerName, PlayerData in pairs( self.Players ) do + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - for CategoryID, CategoryName in pairs( DATABASECategory ) do - self:T( CategoryName ) - if PlayerData.Hit[CategoryID] then - local Score = 0 - local ScoreHit = 0 - local Penalty = 0 - local PenaltyHit = 0 - self:T( "Hit scores exist for player " .. PlayerName ) - for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do - Score = Score + UnitData.Score - ScoreHit = ScoreHit + UnitData.ScoreHit - Penalty = Penalty + UnitData.Penalty - PenaltyHit = UnitData.PenaltyHit - end - local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) - self:T( ScoreMessageHit ) - ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageHits ~= "" then - ScoreMessage = ScoreMessage .. " Hits: " .. ScoreMessageHits .. "\n" - end - - local ScoreMessageKills = "" - for CategoryID, CategoryName in pairs( DATABASECategory ) do - self:T( "Kill scores exist for player " .. PlayerName ) - if PlayerData.Kill[CategoryID] then - local Score = 0 - local ScoreKill = 0 - local Penalty = 0 - local PenaltyKill = 0 - - for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do - Score = Score + UnitData.Score - ScoreKill = ScoreKill + UnitData.ScoreKill - Penalty = Penalty + UnitData.Penalty - PenaltyKill = PenaltyKill + UnitData.PenaltyKill - end - - local ScoreMessageKill = string.format( " %s:%d ", CategoryName, Score - Penalty ) - self:T( ScoreMessageKill ) - ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill + -- Some variables + local InitUnitCoalition = _DATABASECoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _DATABASECategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageKills ~= "" then - ScoreMessage = ScoreMessage .. " Kills: " .. ScoreMessageKills .. "\n" - end - - local ScoreMessageCoalitionChangePenalties = "" - if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) - PlayerPenalty = PlayerPenalty + PlayerData.Penalty - end - if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. " Coalition Penalties: " .. ScoreMessageCoalitionChangePenalties .. "\n" - end + local PlayerScore = 0 + local PlayerPenalty = 0 - local ScoreMessageMission = "" - local ScoreMission = 0 - local ScoreTask = 0 - for MissionName, MissionData in pairs( PlayerData.Mission ) do - ScoreMission = ScoreMission + MissionData.ScoreMission - ScoreTask = ScoreTask + MissionData.ScoreTask - ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " - end - PlayerScore = PlayerScore + ScoreMission + ScoreTask + ScoreMessage = ":\n" - if ScoreMessageMission ~= "" then - ScoreMessage = ScoreMessage .. " Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")\n" - end - - PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score:%d (%d Score -%d Penalties)%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) - end - end - MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() + local ScoreMessageHits = "" + + for CategoryID, CategoryName in pairs( _DATABASECategory ) do + self:T( CategoryName ) + if PlayerData.Hit[CategoryID] then + local Score = 0 + local ScoreHit = 0 + local Penalty = 0 + local PenaltyHit = 0 + self:T( "Hit scores exist for player " .. PlayerName ) + for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do + Score = Score + UnitData.Score + ScoreHit = ScoreHit + UnitData.ScoreHit + Penalty = Penalty + UnitData.Penalty + PenaltyHit = UnitData.PenaltyHit + end + local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageHit ) + ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageHits ~= "" then + ScoreMessage = ScoreMessage .. " Hits: " .. ScoreMessageHits .. "\n" + end + + local ScoreMessageKills = "" + for CategoryID, CategoryName in pairs( _DATABASECategory ) do + self:T( "Kill scores exist for player " .. PlayerName ) + if PlayerData.Kill[CategoryID] then + local Score = 0 + local ScoreKill = 0 + local Penalty = 0 + local PenaltyKill = 0 + + for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do + Score = Score + UnitData.Score + ScoreKill = ScoreKill + UnitData.ScoreKill + Penalty = Penalty + UnitData.Penalty + PenaltyKill = PenaltyKill + UnitData.PenaltyKill + end + + local ScoreMessageKill = string.format( " %s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageKill ) + ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill + + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageKills ~= "" then + ScoreMessage = ScoreMessage .. " Kills: " .. ScoreMessageKills .. "\n" + end + + local ScoreMessageCoalitionChangePenalties = "" + if PlayerData.PenaltyCoalition ~= 0 then + ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) + PlayerPenalty = PlayerPenalty + PlayerData.Penalty + end + if ScoreMessageCoalitionChangePenalties ~= "" then + ScoreMessage = ScoreMessage .. " Coalition Penalties: " .. ScoreMessageCoalitionChangePenalties .. "\n" + end + + local ScoreMessageMission = "" + local ScoreMission = 0 + local ScoreTask = 0 + for MissionName, MissionData in pairs( PlayerData.Mission ) do + ScoreMission = ScoreMission + MissionData.ScoreMission + ScoreTask = ScoreTask + MissionData.ScoreTask + ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " + end + PlayerScore = PlayerScore + ScoreMission + ScoreTask + + if ScoreMessageMission ~= "" then + ScoreMessage = ScoreMessage .. " Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")\n" + end + + PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score:%d (%d Score -%d Penalties)%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) + end + end + MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() end function DATABASE:ReportScorePlayer() -env.info( "Hello World " ) + env.info( "Hello World " ) - local ScoreMessage = "" - local PlayerMessage = "" - - self:T( "Score Report" ) + local ScoreMessage = "" + local PlayerMessage = "" - for PlayerName, PlayerData in pairs( self.Players ) do - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) + self:T( "Score Report" ) - -- Some variables - local InitUnitCoalition = DATABASECoalition[PlayerData.UnitCoalition] - local InitUnitCategory = DATABASECategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = "" - - local ScoreMessageHits = "" + for PlayerName, PlayerData in pairs( self.Players ) do + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - for CategoryID, CategoryName in pairs( DATABASECategory ) do - self:T( CategoryName ) - if PlayerData.Hit[CategoryID] then - local Score = 0 - local ScoreHit = 0 - local Penalty = 0 - local PenaltyHit = 0 - self:T( "Hit scores exist for player " .. PlayerName ) - for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do - Score = Score + UnitData.Score - ScoreHit = ScoreHit + UnitData.ScoreHit - Penalty = Penalty + UnitData.Penalty - PenaltyHit = UnitData.PenaltyHit - end - local ScoreMessageHit = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreHit, PenaltyHit ) - self:T( ScoreMessageHit ) - ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageHits ~= "" then - ScoreMessage = ScoreMessage .. "\n Hits: " .. ScoreMessageHits .. " " - end - - local ScoreMessageKills = "" - for CategoryID, CategoryName in pairs( DATABASECategory ) do - self:T( "Kill scores exist for player " .. PlayerName ) - if PlayerData.Kill[CategoryID] then - local Score = 0 - local ScoreKill = 0 - local Penalty = 0 - local PenaltyKill = 0 - - for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do - Score = Score + UnitData.Score - ScoreKill = ScoreKill + UnitData.ScoreKill - Penalty = Penalty + UnitData.Penalty - PenaltyKill = PenaltyKill + UnitData.PenaltyKill - end - - local ScoreMessageKill = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill ) - self:T( ScoreMessageKill ) - ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill + -- Some variables + local InitUnitCoalition = _DATABASECoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _DATABASECategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageKills ~= "" then - ScoreMessage = ScoreMessage .. "\n Kills: " .. ScoreMessageKills .. " " - end - - local ScoreMessageCoalitionChangePenalties = "" - if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) - PlayerPenalty = PlayerPenalty + PlayerData.Penalty - end - if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. "\n Coalition: " .. ScoreMessageCoalitionChangePenalties .. " " - end + local PlayerScore = 0 + local PlayerPenalty = 0 - local ScoreMessageMission = "" - local ScoreMission = 0 - local ScoreTask = 0 - for MissionName, MissionData in pairs( PlayerData.Mission ) do - ScoreMission = ScoreMission + MissionData.ScoreMission - ScoreTask = ScoreTask + MissionData.ScoreTask - ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " - end - PlayerScore = PlayerScore + ScoreMission + ScoreTask + ScoreMessage = "" - if ScoreMessageMission ~= "" then - ScoreMessage = ScoreMessage .. "\n Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") " - end - - PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties ):%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) - end - end - MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() + local ScoreMessageHits = "" + + for CategoryID, CategoryName in pairs( _DATABASECategory ) do + self:T( CategoryName ) + if PlayerData.Hit[CategoryID] then + local Score = 0 + local ScoreHit = 0 + local Penalty = 0 + local PenaltyHit = 0 + self:T( "Hit scores exist for player " .. PlayerName ) + for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do + Score = Score + UnitData.Score + ScoreHit = ScoreHit + UnitData.ScoreHit + Penalty = Penalty + UnitData.Penalty + PenaltyHit = UnitData.PenaltyHit + end + local ScoreMessageHit = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreHit, PenaltyHit ) + self:T( ScoreMessageHit ) + ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageHits ~= "" then + ScoreMessage = ScoreMessage .. "\n Hits: " .. ScoreMessageHits .. " " + end + + local ScoreMessageKills = "" + for CategoryID, CategoryName in pairs( _DATABASECategory ) do + self:T( "Kill scores exist for player " .. PlayerName ) + if PlayerData.Kill[CategoryID] then + local Score = 0 + local ScoreKill = 0 + local Penalty = 0 + local PenaltyKill = 0 + + for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do + Score = Score + UnitData.Score + ScoreKill = ScoreKill + UnitData.ScoreKill + Penalty = Penalty + UnitData.Penalty + PenaltyKill = PenaltyKill + UnitData.PenaltyKill + end + + local ScoreMessageKill = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill ) + self:T( ScoreMessageKill ) + ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill + + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageKills ~= "" then + ScoreMessage = ScoreMessage .. "\n Kills: " .. ScoreMessageKills .. " " + end + + local ScoreMessageCoalitionChangePenalties = "" + if PlayerData.PenaltyCoalition ~= 0 then + ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) + PlayerPenalty = PlayerPenalty + PlayerData.Penalty + end + if ScoreMessageCoalitionChangePenalties ~= "" then + ScoreMessage = ScoreMessage .. "\n Coalition: " .. ScoreMessageCoalitionChangePenalties .. " " + end + + local ScoreMessageMission = "" + local ScoreMission = 0 + local ScoreTask = 0 + for MissionName, MissionData in pairs( PlayerData.Mission ) do + ScoreMission = ScoreMission + MissionData.ScoreMission + ScoreTask = ScoreTask + MissionData.ScoreTask + ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " + end + PlayerScore = PlayerScore + ScoreMission + ScoreTask + + if ScoreMessageMission ~= "" then + ScoreMessage = ScoreMessage .. "\n Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") " + end + + PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties ):%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) + end + end + MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() end function DATABASE:ScoreMenu() - local ReportScore = SUBMENU:New( 'Scoring' ) - local ReportAllScores = COMMANDMENU:New( 'Score All Active Players', ReportScore, DATABASE.ReportScoreAll, self ) - local ReportPlayerScores = COMMANDMENU:New('Your Current Score', ReportScore, DATABASE.ReportScorePlayer, self ) + local ReportScore = SUBMENU:New( 'Scoring' ) + local ReportAllScores = COMMANDMENU:New( 'Score All Active Players', ReportScore, DATABASE.ReportScoreAll, self ) + local ReportPlayerScores = COMMANDMENU:New('Your Current Score', ReportScore, DATABASE.ReportScorePlayer, self ) end @@ -5829,102 +6321,116 @@ end -- File Logic for tracking the scores function DATABASE:SecondsToClock(sSeconds) -local nSeconds = sSeconds - if nSeconds == 0 then - --return nil; - return "00:00:00"; - else - nHours = string.format("%02.f", math.floor(nSeconds/3600)); - nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60))); - nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60)); - return nHours..":"..nMins..":"..nSecs - end + local nSeconds = sSeconds + if nSeconds == 0 then + --return nil; + return "00:00:00"; + else + nHours = string.format("%02.f", math.floor(nSeconds/3600)); + nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60))); + nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60)); + return nHours..":"..nMins..":"..nSecs + end end function DATABASE:ScoreOpen() - if lfs then - local fdir = lfs.writedir() .. [[Logs\]] .. "Player_Scores_" .. os.date( "%Y-%m-%d_%H-%M-%S" ) .. ".csv" - self.StatFile, self.err = io.open(fdir,"w+") - if not self.StatFile then - error( "Error: Cannot open 'Player Scores.csv' file in " .. lfs.writedir() ) - end - self.StatFile:write( '"RunID";"Time";"PlayerName";"ScoreType";"PlayerUnitCoaltion";"PlayerUnitCategory";"PlayerUnitType";"PlayerUnitName";"TargetUnitCoalition";"TargetUnitCategory";"TargetUnitType";"TargetUnitName";"Times";"Score"\n' ) - - self.RunID = os.date("%y-%m-%d_%H-%M-%S") - end + if lfs then + local fdir = lfs.writedir() .. [[Logs\]] .. "Player_Scores_" .. os.date( "%Y-%m-%d_%H-%M-%S" ) .. ".csv" + self.StatFile, self.err = io.open(fdir,"w+") + if not self.StatFile then + error( "Error: Cannot open 'Player Scores.csv' file in " .. lfs.writedir() ) + end + self.StatFile:write( '"RunID","Time","PlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' ) + + self.RunID = os.date("%y-%m-%d_%H-%M-%S") + end end function DATABASE:ScoreAdd( PlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - --write statistic information to file - local ScoreTime = self:SecondsToClock(timer.getTime()) - PlayerName = PlayerName:gsub( '"', '_' ) + --write statistic information to file + local ScoreTime = self:SecondsToClock(timer.getTime()) + PlayerName = PlayerName:gsub( '"', '_' ) - if PlayerUnitName and PlayerUnitName ~= '' then - local PlayerUnit = Unit.getByName( PlayerUnitName ) - - if PlayerUnit then - if not PlayerUnitCategory then - --PlayerUnitCategory = DATABASECategory[PlayerUnit:getCategory()] - PlayerUnitCategory = DATABASECategory[PlayerUnit:getDesc().category] - end - - if not PlayerUnitCoalition then - PlayerUnitCoalition = DATABASECoalition[PlayerUnit:getCoalition()] - end - - if not PlayerUnitType then - PlayerUnitType = PlayerUnit:getTypeName() - end - else - PlayerUnitName = '' - PlayerUnitCategory = '' - PlayerUnitCoalition = '' - PlayerUnitType = '' - end - else - PlayerUnitName = '' - PlayerUnitCategory = '' - PlayerUnitCoalition = '' - PlayerUnitType = '' - end - - if not TargetUnitCoalition then - TargetUnitCoalition = '' - end - - if not TargetUnitCategory then - TargetUnitCategory = '' - end - - if not TargetUnitType then - TargetUnitType = '' - end - - if not TargetUnitName then - TargetUnitName = '' - end + if PlayerUnitName and PlayerUnitName ~= '' then + local PlayerUnit = Unit.getByName( PlayerUnitName ) - if lfs then - self.StatFile:write( '"' .. self.RunID .. '";' .. ScoreTime .. ';"' .. PlayerName .. '";"' .. ScoreType .. '";"' .. - PlayerUnitCoalition .. '";"' .. PlayerUnitCategory .. '";"' .. PlayerUnitType .. '";"' .. PlayerUnitName .. '";"' .. - TargetUnitCoalition .. '";"' .. TargetUnitCategory .. '";"' .. TargetUnitType .. '";"' .. TargetUnitName .. '";' .. - ScoreTimes .. ';' .. ScoreAmount ) - self.StatFile:write( "\n" ) - end + if PlayerUnit then + if not PlayerUnitCategory then + --PlayerUnitCategory = DATABASECategory[PlayerUnit:getCategory()] + PlayerUnitCategory = _DATABASECategory[PlayerUnit:getDesc().category] + end + + if not PlayerUnitCoalition then + PlayerUnitCoalition = _DATABASECoalition[PlayerUnit:getCoalition()] + end + + if not PlayerUnitType then + PlayerUnitType = PlayerUnit:getTypeName() + end + else + PlayerUnitName = '' + PlayerUnitCategory = '' + PlayerUnitCoalition = '' + PlayerUnitType = '' + end + else + PlayerUnitName = '' + PlayerUnitCategory = '' + PlayerUnitCoalition = '' + PlayerUnitType = '' + end + + if not TargetUnitCoalition then + TargetUnitCoalition = '' + end + + if not TargetUnitCategory then + TargetUnitCategory = '' + end + + if not TargetUnitType then + TargetUnitType = '' + end + + if not TargetUnitName then + TargetUnitName = '' + end + + if lfs then + self.StatFile:write( + '"' .. self.RunID .. '"' .. ',' .. + '' .. ScoreTime .. '' .. ',' .. + '"' .. PlayerName .. '"' .. ',' .. + '"' .. ScoreType .. '"' .. ',' .. + '"' .. PlayerUnitCoalition .. '"' .. ',' .. + '"' .. PlayerUnitCategory .. '"' .. ',' .. + '"' .. PlayerUnitType .. '"' .. ',' .. + '"' .. PlayerUnitName .. '"' .. ',' .. + '"' .. TargetUnitCoalition .. '"' .. ',' .. + '"' .. TargetUnitCategory .. '"' .. ',' .. + '"' .. TargetUnitType .. '"' .. ',' .. + '"' .. TargetUnitName .. '"' .. ',' .. + '' .. ScoreTimes .. '' .. ',' .. + '' .. ScoreAmount + ) + + self.StatFile:write( "\n" ) + end end - + function LogClose() - if lfs then - self.StatFile:close() - end + if lfs then + self.StatFile:close() + end end _Database = DATABASE:New() -- Database#DATABASE _Database:ScoreOpen() + --- CARGO Classes -- @module CARGO @@ -8579,7 +9085,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 @@ -8603,7 +9109,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 @@ -8625,14 +9131,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 @@ -8645,12 +9151,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 @@ -8661,13 +9167,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 @@ -8679,7 +9187,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 @@ -8694,7 +9202,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 @@ -8710,7 +9218,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 @@ -8725,7 +9233,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 @@ -8739,7 +9247,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 @@ -8754,7 +9262,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 @@ -8767,15 +9275,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 @@ -8786,7 +9295,7 @@ function TASK:IsGoalReached( ) end end - self:T( GoalReached ) + self:T( { GoalReached, self.GoalTasks } ) return GoalReached end @@ -8795,7 +9304,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 @@ -8808,7 +9317,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 = "" @@ -9001,10 +9510,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() ) @@ -9015,34 +9525,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 @@ -9055,6 +9562,7 @@ function DESTROYBASETASK:EventDead( event ) self:T( { UnitsDestroyed } ) self:IncreaseGoalCount( UnitsDestroyed, self.GoalVerb ) end + end --- Validate task completeness of DESTROYBASETASK. @@ -9078,10 +9586,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 ) ) @@ -9090,35 +9599,35 @@ 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 --- Task class to destroy radar installations. @@ -9141,8 +9650,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 @@ -9193,7 +9702,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 @@ -10271,6 +10780,7 @@ Include.File( "Task" ) --- The CLEANUP class. -- @type CLEANUP +-- @extends Base#BASE CLEANUP = { ClassName = "CLEANUP", ZoneNames = {}, @@ -10301,15 +10811,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 @@ -10345,9 +10848,8 @@ function CLEANUP:_DestroyUnit( CleanUpUnit, CleanUpUnitName ) local CleanUpGroupUnits = CleanUpGroup:getUnits() if #CleanUpGroupUnits == 1 then local CleanUpGroupName = CleanUpGroup:getName() - local Event = {["initiator"]=CleanUpUnit,["id"]=8} - world.onEvent( Event ) - trigger.action.deactivateGroup( CleanUpGroup ) + --self:CreateEventCrash( timer.getTime(), CleanUpUnit ) + CleanUpGroup:destroy() self:T( { "Destroyed Group:", CleanUpGroupName } ) else CleanUpUnit:destroy() @@ -10372,13 +10874,43 @@ 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:OnPilotDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self ) + _EventDispatcher:OnDeadForUnit( Event.IniDCSUnitName, self._EventCrash, 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 -- @param DCSTypes#Event event -function CLEANUP:_EventCrash( event ) - self:F( { event } ) +function CLEANUP:_EventCrash( Event ) + self:F( { Event } ) + --TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed. --MESSAGE:New( "Crash ", "Crash", 10, "Crash" ):ToAll() -- self:T("before getGroup") -- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired @@ -10387,44 +10919,28 @@ 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 @@ -10432,38 +10948,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. @@ -10485,24 +10991,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 @@ -10520,9 +11022,11 @@ local CleanUpSurfaceTypeText = { --- At the defined time interval, CleanUp the Groups within the CleanUpList. -- @param #CLEANUP self function CLEANUP:_CleanUpScheduler() - self:F( "CleanUp Scheduler" ) + self:F( { "CleanUp Scheduler" } ) + local CleanUpCount = 0 for CleanUpUnitName, UnitData in pairs( self.CleanUpList ) do + CleanUpCount = CleanUpCount + 1 self:T( { CleanUpUnitName, UnitData } ) local CleanUpUnit = Unit.getByName(UnitData.CleanUpUnitName) @@ -10584,6 +11088,7 @@ function CLEANUP:_CleanUpScheduler() self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE end end + self:T(CleanUpCount) end --- Dynamic spawning of groups (and units). @@ -10663,6 +11168,7 @@ Include.File( "Base" ) Include.File( "Database" ) Include.File( "Group" ) Include.File( "Zone" ) +Include.File( "Event" ) --- SPAWN Class -- @type SPAWN @@ -10709,12 +11215,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 @@ -10753,12 +11253,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 @@ -10870,17 +11364,12 @@ end -- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically. -- SpawnRU_SU34 = SPAWN:New( 'TF1 RU Su-34 Krymsk@AI - Attack Ships' ):Schedule( 2, 3, 1800, 0.4 ):SpawnUncontrolled():RandomizeRoute( 1, 1, 3000 ):RepeatOnEngineShutDown() function SPAWN:Repeat() - self:F( { self.SpawnTemplatePrefix } ) + self:F( { self.SpawnTemplatePrefix, self.SpawnIndex } ) self.SpawnRepeat = true 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 @@ -10973,6 +11462,21 @@ 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 ) SpawnX = SpawnXIndex * SpawnDeltaX @@ -11031,6 +11535,20 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) self.SpawnGroups[self.SpawnIndex].Group:Activate() else self:T( self.SpawnGroups[self.SpawnIndex].SpawnTemplate ) + _EventDispatcher:OnBirthForTemplate( self.SpawnGroups[self.SpawnIndex].SpawnTemplate, self._OnBirth, self ) + _EventDispatcher:OnCrashForTemplate( self.SpawnGroups[self.SpawnIndex].SpawnTemplate, self._OnDeadOrCrash, self ) + _EventDispatcher:OnDeadForTemplate( self.SpawnGroups[self.SpawnIndex].SpawnTemplate, self._OnDeadOrCrash, self ) + + if self.SpawnRepeat then + _EventDispatcher:OnTakeOffForTemplate( self.SpawnGroups[self.SpawnIndex].SpawnTemplate, self._OnTakeOff, self ) + end + if self.RepeatOnLanding then + _EventDispatcher:OnLandForTemplate( self.SpawnGroups[self.SpawnIndex].SpawnTemplate, self._OnLand, self ) + end + if self.RepeatOnEngineShutDown then + _EventDispatcher:OnEngineShutDownForTemplate( self.SpawnGroups[self.SpawnIndex].SpawnTemplate, self._OnEngineShutDown, self ) + end + self.SpawnGroups[self.SpawnIndex].Group = _Database:Spawn( self.SpawnGroups[self.SpawnIndex].SpawnTemplate ) -- If there is a SpawnFunction hook defined, call it. @@ -11046,7 +11564,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) self.SpawnGroups[self.SpawnIndex].Spawned = true return self.SpawnGroups[self.SpawnIndex].Group else - self:E( { self.SpawnTemplatePrefix, "No more Groups to Spawn:", SpawnIndex, self.SpawnMaxGroups } ) + --self:E( { self.SpawnTemplatePrefix, "No more Groups to Spawn:", SpawnIndex, self.SpawnMaxGroups } ) end return nil @@ -11885,11 +12403,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() @@ -11911,43 +12429,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 @@ -11986,6 +12500,7 @@ end -- @author (co) Flightcontrol (Modified and enriched with functionality) Include.File( "Routines" ) +Include.File( "Event" ) Include.File( "Base" ) Include.File( "Mission" ) Include.File( "Client" ) @@ -12024,27 +12539,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.Weapon:getTarget() -- Identify target local _targetMimname = Unit.getName(_targetMim) local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroupName = _targetMimgroup:getName() @@ -12120,71 +12634,83 @@ end --- Taking the lead of AI escorting your flight. -- The ESCORT class allows you to interact with escorting AI on your flight and take the lead. -- Each escorting group can be commanded with a whole set of radio commands (radio menu in your flight, and then F10). --- +-- -- The radio commands will vary according the category of the group. The richest set of commands are with Helicopters and AirPlanes. -- Ships and Ground troops will have a more limited set, but they can provide support through the bombing of targets designated by the other escorts. --- +-- -- Find a summary below of the current available commands: --- +-- -- **1. Navigation ...:** Escort group navigation functions: --- --- * **"Hold Position and Stay Low":** Stops the escort group and they will hover 30 meters above the ground at the position they stopped. --- * **"Join-Up and Hold Position NearBy":** The escort group will stop nearby you, and then the group will hover. --- * **"Join-Up and Follow at 100":** The escort group fill follow you at about 100 meters, and they will follow you. --- * **"Join-Up and Follow at 200":** The escort group fill follow you at about 200 meters, and they will follow you. --- * **"Join-Up and Follow at 400":** The escort group fill follow you at about 400 meters, and they will follow you. --- * **"Join-Up and Follow at 800":** The escort group fill follow you at about 800 meters, and they will follow you. +-- +-- * **"Join-Up and Follow at x meters":** The escort group fill follow you at about x meters, and they will follow you. -- * **"Flare":** Provides menu commands to let the escort group shoot a flare in the air in a color. -- * **"Smoke":** Provides menu commands to let the escort group smoke the air in a color. Note that smoking is only available for ground and naval troops. --- --- **2. Report targets ...:** Report targets will make the escort group to report any target that it identifies within a 8km range. Any detected target can be attacked using the 4. Attack nearby targets function. (see below). --- +-- +-- **2. Hold position ...:** Escort group navigation functions: +-- +-- * **"At current location":** Stops the escort group and they will hover 30 meters above the ground at the position they stopped. +-- * **"At client location":** Stops the escort group and they will hover 30 meters above the ground at the position they stopped. +-- +-- **3. Report targets ...:** Report targets will make the escort group to report any target that it identifies within a 8km range. Any detected target can be attacked using the 4. Attack nearby targets function. (see below). +-- -- * **"Report now":** Will report the current detected targets. -- * **"Report targets on":** Will make the escort group to report detected targets and will fill the "Attack nearby targets" menu list. -- * **"Report targets off":** Will stop detecting targets. --- --- **3. Scan targets ...:** Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or defined task. --- +-- +-- **4. Scan targets ...:** Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or defined task. +-- -- * **"Scan targets 30 seconds":** Scan 30 seconds for targets. -- * **"Scan targets 60 seconds":** Scan 60 seconds for targets. --- --- **4. Attack targets ...:** This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed. --- --- **5. Request assistance from ...:** This menu item will list all detected targets within a 15km range, as with the menu item **Attack Targets**. +-- +-- **5. Attack targets ...:** This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed. +-- +-- **6. Request assistance from ...:** This menu item will list all detected targets within a 15km range, as with the menu item **Attack Targets**. -- This menu item allows to request attack support from other escorts supporting the current client group. -- eg. the function allows a player to request support from the Ship escort to attack a target identified by the Plane escort with its Tomahawk missiles. -- eg. the function allows a player to request support from other Planes escorting to bomb the unit with illumination missiles or bombs, so that the main plane escort can attack the area. --- --- **6. ROE ...:** Defines the Rules of Engagement of the escort group when in flight. --- +-- +-- **7. ROE ...:** Defines the Rules of Engagement of the escort group when in flight. +-- -- * **"Hold Fire":** The escort group will hold fire. -- * **"Return Fire":** The escort group will return fire. -- * **"Open Fire":** The escort group will open fire on designated targets. -- * **"Weapon Free":** The escort group will engage with any target. --- --- **7. Evasion ...:** Will define the evasion techniques that the escort group will perform during flight or combat. --- +-- +-- **8. Evasion ...:** Will define the evasion techniques that the escort group will perform during flight or combat. +-- -- * **"Fight until death":** The escort group will have no reaction to threats. -- * **"Use flares, chaff and jammers":** The escort group will use passive defense using flares and jammers. No evasive manoeuvres are executed. -- * **"Evade enemy fire":** The rescort group will evade enemy fire before firing. -- * **"Go below radar and evade fire":** The escort group will perform evasive vertical manoeuvres. --- --- **8. Resume Mission ...:** Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint. +-- +-- **9. Resume Mission ...:** Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint. -- Note that this is really fantastic, as you now have the dynamic of taking control of the escort groups, and allowing them to resume their path or mission. --- --- **9. Abort Current Task:** Cancel the current task and rejoin formation. --- +-- +-- **10. Abort Current Task:** Cancel the current task and rejoin formation. +-- -- 1. ESCORT object construction methods. -- -------------------------------------- -- Create a new SPAWN object with the @{#ESCORT.New} method: --- +-- -- * @{#ESCORT.New}: Creates a new ESCORT object from a @{Group#GROUP} for a @{Client#CLIENT}, with an optional briefing text. --- +-- -- 2. ESCORT object initialization methods. -- ---------------------------------------- --- None. --- --- +-- The following menus can be setup: +-- +-- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client. +-- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position. +-- * @{#ESCORT.MenuHoldAtLeaderPosition}: Creates a menu to hold the escort at the client position. +-- * @{#ESCORT.MenuScanForTargets}: Creates a menu so that the escort scans targets. +-- * @{#ESCORT.MenuFlare}: Creates a menu to disperse flares. +-- * @{#ESCORT.MenuSmoke}: Creates a menu to disparse smoke. +-- * @{#ESCORT.MenuReportTargets}: Creates a menu so that the escort reports targets. +-- * @{#ESCORT.MenuReportPosition}: Creates a menu so that the escort reports its current position from bullseye. +-- * @{#ESCORT.MenuAssistedAttack: Creates a menu so that the escort supportes assisted attack from other escorts with the client. +-- * @{#ESCORT.MenuROE: Creates a menu structure to set the rules of engagement of the escort. +-- * @{#ESCORT.MenuEvasion: Creates a menu structure to set the evasion techniques when the escort is under threat. +-- * @{#ESCORT.MenuResumeMission}: Creates a menu structure so that the escort can resume from a waypoint. +-- -- @module Escort -- @author FlightControl @@ -12201,6 +12727,7 @@ Include.File( "Zone" ) -- @field Client#CLIENT EscortClient -- @field Group#GROUP EscortGroup -- @field #string EscortName +-- @field #ESCORT.MODE EscortMode The mode the escort is in. -- @field #number FollowScheduler The id of the _FollowScheduler function. -- @field #boolean ReportTargets If true, nearby targets are reported. -- @Field DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup. @@ -12211,6 +12738,11 @@ ESCORT = { EscortName = nil, -- The Escort Name EscortClient = nil, EscortGroup = nil, + EscortMode = nil, + MODE = { + FOLLOW = 1, + MISSION = 2, + }, Targets = {}, -- The identified targets FollowScheduler = nil, ReportTargets = true, @@ -12219,6 +12751,11 @@ ESCORT = { TaskPoints = {} } +--- ESCORT.Mode class +-- @type ESCORT.MODE +-- @field #number FOLLOW +-- @field #number MISSION + --- MENUPARAM type -- @type MENUPARAM -- @field #ESCORT ParamSelf @@ -12234,127 +12771,465 @@ ESCORT = { -- @return #ESCORT self function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) local self = BASE:Inherit( self, BASE:New() ) - self:F( { EscortClient, EscortGroup, EscortName } ) - + self:F( { EscortClient, EscortGroup, EscortName } ) + self.EscortClient = EscortClient -- Client#CLIENT self.EscortGroup = EscortGroup -- Group#GROUP self.EscortName = EscortName self.EscortBriefing = EscortBriefing - + self:T( EscortGroup:GetClassNameAndID() ) - + -- Set EscortGroup known at EscortClient. if not self.EscortClient._EscortGroups then - self.EscortClient._EscortGroups = {} + self.EscortClient._EscortGroups = {} end if not self.EscortClient._EscortGroups[EscortGroup:GetName()] then - self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} - self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup - self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName - self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {} - + self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} + self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup + self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName + self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {} + self.EscortMode = ESCORT.MODE.FOLLOW end - - + + self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) - - self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) - if EscortGroup:IsHelicopter() or EscortGroup:IsAirPlane() then - -- Escort Navigation - self.EscortMenuHoldPosition = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Position and Stay Low", self.EscortMenuReportNavigation, ESCORT._HoldPosition, { ParamSelf = self } ) - self.EscortMenuJoinUpAndHoldPosition = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Hold Position NearBy", self.EscortMenuReportNavigation, ESCORT._HoldPositionNearBy, { ParamSelf = self } ) - self.EscortMenuJoinUpAndFollow50Meters = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at 100", self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = 100 } ) - self.EscortMenuJoinUpAndFollow100Meters = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at 200", self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = 200 } ) - self.EscortMenuJoinUpAndFollow150Meters = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at 400", self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = 400 } ) - self.EscortMenuJoinUpAndFollow200Meters = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at 800", self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = 800 } ) - end - self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, "Flare", self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = self } ) - self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = UNIT.FlareColor.Green, ParamMessage = "Released a green flare!" } ) - self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = UNIT.FlareColor.Red, ParamMessage = "Released a red flare!" } ) - self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = UNIT.FlareColor.White, ParamMessage = "Released a white flare!" } ) - self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = UNIT.FlareColor.Yellow, ParamMessage = "Released a yellow flare!" } ) - if EscortGroup:IsGround() or EscortGroup:IsShip() then - self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = self } ) - self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Green, ParamMessage = "Releasing green smoke!" } ) - self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Red, ParamMessage = "Releasing red smoke!" } ) - self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.White, ParamMessage = "Releasing white smoke!" } ) - self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Orange, ParamMessage = "Releasing orange smoke!" } ) - self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Blue, ParamMessage = "Releasing blue smoke!" } ) + self.EscortGroup:WayPointInitialize(1) + + self.EscortGroup:OptionROTVertical() + self.EscortGroup:OptionROEOpenFire() + + EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " .. + "We're escorting your flight. " .. + "Use the Radio Menu and F10 and use the options under + " .. EscortName .. "\n", + 60, EscortClient + ) + + return self +end + + +--- Defines the default menus +-- @param #ESCORT self +-- @return #ESCORT +function ESCORT:Menus() + self:F() + + self:MenuFollowAt( 100 ) + self:MenuFollowAt( 200 ) + self:MenuFollowAt( 300 ) + self:MenuFollowAt( 400 ) + + self:MenuScanForTargets( 100, 60 ) + + self:MenuHoldAtEscortPosition( 30 ) + self:MenuHoldAtLeaderPosition( 30 ) + + self:MenuFlare() + self:MenuSmoke() + + self:MenuReportTargets( 60 ) + self:MenuAssistedAttack() + self:MenuROE() + self:MenuEvasion() + self:MenuResumeMission() + + return self +end + + + +--- Defines a menu slot to let the escort Join and Follow you at a certain distance. +-- This menu will appear under **Navigation**. +-- @param #ESCORT self +-- @param DCSTypes#Distance Distance The distance in meters that the escort needs to follow the client. +-- @return #ESCORT +function ESCORT:MenuFollowAt( Distance ) + self:F(Distance) + + if self.EscortGroup:IsAir() then + if not self.EscortMenuReportNavigation then + self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) + end + + if not self.EscortMenuJoinUpAndFollow then + self.EscortMenuJoinUpAndFollow = {} + end + + self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = Distance } ) + + self.EscortMode = ESCORT.MODE.FOLLOW end - - if EscortGroup:IsHelicopter() or EscortGroup:IsAirPlane() or EscortGroup:IsGround() or EscortGroup:IsShip() then - -- Report Targets + + return self +end + +--- Defines a menu slot to let the escort hold at their current position and stay low with a specified height during a specified time in seconds. +-- This menu will appear under **Hold position**. +-- @param #ESCORT self +-- @param DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. +-- @param DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. +-- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. +-- @return #ESCORT +-- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function. +function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat ) + self:F( { Height, Seconds, MenuTextFormat } ) + + if self.EscortGroup:IsAir() then + + if not self.EscortMenuHold then + self.EscortMenuHold = MENU_CLIENT:New( self.EscortClient, "Hold position", self.EscortMenu ) + end + + if not Height then + Height = 30 + end + + if not Seconds then + Seconds = 0 + end + + local MenuText = "" + if not MenuTextFormat then + if Seconds == 0 then + MenuText = string.format( "Hold at %d meter", Height ) + else + MenuText = string.format( "Hold at %d meter for %d seconds", Height, Seconds ) + end + else + if Seconds == 0 then + MenuText = string.format( MenuTextFormat, Height ) + else + MenuText = string.format( MenuTextFormat, Height, Seconds ) + end + end + + if not self.EscortMenuHoldPosition then + self.EscortMenuHoldPosition = {} + end + + self.EscortMenuHoldPosition[#self.EscortMenuHoldPosition+1] = MENU_CLIENT_COMMAND + :New( + self.EscortClient, + MenuText, + self.EscortMenuHold, + ESCORT._HoldPosition, + { ParamSelf = self, + ParamOrbitGroup = self.EscortGroup, + ParamHeight = Height, + ParamSeconds = Seconds + } + ) + end + + return self +end + + +--- Defines a menu slot to let the escort hold at the client position and stay low with a specified height during a specified time in seconds. +-- This menu will appear under **Navigation**. +-- @param #ESCORT self +-- @param DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. +-- @param DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. +-- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. +-- @return #ESCORT +-- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function. +function ESCORT:MenuHoldAtLeaderPosition( Height, Seconds, MenuTextFormat ) + self:F( { Height, Seconds, MenuTextFormat } ) + + if self.EscortGroup:IsAir() then + + if not self.EscortMenuHold then + self.EscortMenuHold = MENU_CLIENT:New( self.EscortClient, "Hold position", self.EscortMenu ) + end + + if not Height then + Height = 30 + end + + if not Seconds then + Seconds = 0 + end + + local MenuText = "" + if not MenuTextFormat then + if Seconds == 0 then + MenuText = string.format( "Rejoin and hold at %d meter", Height ) + else + MenuText = string.format( "Rejoin and hold at %d meter for %d seconds", Height, Seconds ) + end + else + if Seconds == 0 then + MenuText = string.format( MenuTextFormat, Height ) + else + MenuText = string.format( MenuTextFormat, Height, Seconds ) + end + end + + if not self.EscortMenuHoldAtLeaderPosition then + self.EscortMenuHoldAtLeaderPosition = {} + end + + self.EscortMenuHoldAtLeaderPosition[#self.EscortMenuHoldAtLeaderPosition+1] = MENU_CLIENT_COMMAND + :New( + self.EscortClient, + MenuText, + self.EscortMenuHold, + ESCORT._HoldPosition, + { ParamSelf = self, + ParamOrbitGroup = self.EscortClient, + ParamHeight = Height, + ParamSeconds = Seconds + } + ) + end + + return self +end + +--- Defines a menu slot to let the escort scan for targets at a certain height for a certain time in seconds. +-- This menu will appear under **Scan targets**. +-- @param #ESCORT self +-- @param DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. +-- @param DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. +-- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. +-- @return #ESCORT +function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) + self:F( { Height, Seconds, MenuTextFormat } ) + + if self.EscortGroup:IsAir() then + if not self.EscortMenuScan then + self.EscortMenuScan = MENU_CLIENT:New( self.EscortClient, "Scan for targets", self.EscortMenu ) + end + + if not Height then + Height = 100 + end + + if not Seconds then + Seconds = 30 + end + + local MenuText = "" + if not MenuTextFormat then + if Seconds == 0 then + MenuText = string.format( "At %d meter", Height ) + else + MenuText = string.format( "At %d meter for %d seconds", Height, Seconds ) + end + else + if Seconds == 0 then + MenuText = string.format( MenuTextFormat, Height ) + else + MenuText = string.format( MenuTextFormat, Height, Seconds ) + end + end + + if not self.EscortMenuScanForTargets then + self.EscortMenuScanForTargets = {} + end + + self.EscortMenuScanForTargets[#self.EscortMenuScanForTargets+1] = MENU_CLIENT_COMMAND + :New( + self.EscortClient, + MenuText, + self.EscortMenuScan, + ESCORT._ScanTargets, + { ParamSelf = self, + ParamScanDuration = 30 + } + ) + end + + return self +end + + + +--- Defines a menu slot to let the escort disperse a flare in a certain color. +-- This menu will appear under **Navigation**. +-- The flare will be fired from the first unit in the group. +-- @param #ESCORT self +-- @param #string MenuTextFormat Optional parameter that shows the menu option text. If no text is given, the default text will be displayed. +-- @return #ESCORT +function ESCORT:MenuFlare( MenuTextFormat ) + self:F() + + if not self.EscortMenuReportNavigation then + self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) + end + + local MenuText = "" + if not MenuTextFormat then + MenuText = "Flare" + else + MenuText = MenuTextFormat + end + + if not self.EscortMenuFlare then + self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = self } ) + self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = UNIT.FlareColor.Green, ParamMessage = "Released a green flare!" } ) + self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = UNIT.FlareColor.Red, ParamMessage = "Released a red flare!" } ) + self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = UNIT.FlareColor.White, ParamMessage = "Released a white flare!" } ) + self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = UNIT.FlareColor.Yellow, ParamMessage = "Released a yellow flare!" } ) + end + + return self +end + +--- Defines a menu slot to let the escort disperse a smoke in a certain color. +-- This menu will appear under **Navigation**. +-- Note that smoke menu options will only be displayed for ships and ground units. Not for air units. +-- The smoke will be fired from the first unit in the group. +-- @param #ESCORT self +-- @param #string MenuTextFormat Optional parameter that shows the menu option text. If no text is given, the default text will be displayed. +-- @return #ESCORT +function ESCORT:MenuSmoke( MenuTextFormat ) + self:F() + + if not self.EscortGroup:IsAir() then + if not self.EscortMenuReportNavigation then + self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) + end + + local MenuText = "" + if not MenuTextFormat then + MenuText = "Smoke" + else + MenuText = MenuTextFormat + end + + if not self.EscortMenuSmoke then + self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = self } ) + self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Green, ParamMessage = "Releasing green smoke!" } ) + self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Red, ParamMessage = "Releasing red smoke!" } ) + self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.White, ParamMessage = "Releasing white smoke!" } ) + self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Orange, ParamMessage = "Releasing orange smoke!" } ) + self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Blue, ParamMessage = "Releasing blue smoke!" } ) + end + end + + return self +end + +--- Defines a menu slot to let the escort report their current detected targets with a specified time interval in seconds. +-- This menu will appear under **Report targets**. +-- Note that if a report targets menu is not specified, no targets will be detected by the escort, and the attack and assisted attack menus will not be displayed. +-- @param #ESCORT self +-- @param DCSTypes#Time Seconds Optional parameter that lets the escort report their current detected targets after specified time interval in seconds. The default time is 30 seconds. +-- @return #ESCORT +function ESCORT:MenuReportTargets( Seconds ) + self:F( { Seconds } ) + + if not self.EscortMenuReportNearbyTargets then self.EscortMenuReportNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Report targets", self.EscortMenu ) - self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } ) - self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } ) end - if EscortGroup:IsHelicopter() then - -- Scanning Targets - self.EscortMenuScanForTargets = MENU_CLIENT:New( self.EscortClient, "Scan targets", self.EscortMenu ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Scan targets 30 seconds", self.EscortMenuScanForTargets, ESCORT._ScanTargets, { ParamSelf = self, ParamScanDuration = 30 } ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Scan targets 60 seconds", self.EscortMenuScanForTargets, ESCORT._ScanTargets, { ParamSelf = self, ParamScanDuration = 60 } ) + if not Seconds then + Seconds = 30 end - + + -- Report Targets + self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } ) + self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } ) + self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } ) + -- Attack Targets self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) - -- Request assistance from other escorts. + + self.ReportTargetsScheduler = routines.scheduleFunction( self._ReportTargetsScheduler, { self }, timer.getTime() + 1, Seconds ) + + return self +end + +--- Defines a menu slot to let the escort attack its detected targets using assisted attack from another escort joined also with the client. +-- This menu will appear under **Request assistance from**. +-- Note that this method needs to be preceded with the method MenuReportTargets. +-- @param #ESCORT self +-- @return #ESCORT +function ESCORT:MenuAssistedAttack() + self:F() + + -- Request assistance from other escorts. -- This is very useful to let f.e. an escorting ship attack a target detected by an escorting plane... self.EscortMenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, "Request assistance from", self.EscortMenu ) - - -- Rules of Engagement - self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) - if EscortGroup:OptionROEHoldFirePossible() then - self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = EscortGroup:OptionROEHoldFire(), ParamMessage = "Holding weapons!" } ) - end - if EscortGroup:OptionROEReturnFirePossible() then - self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = EscortGroup:OptionROEReturnFire(), ParamMessage = "Returning fire!" } ) - end - if EscortGroup:OptionROEOpenFirePossible() then - self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = EscortGroup:OptionROEOpenFire(), ParamMessage = "Opening fire on designated targets!!" } ) - end - if EscortGroup:OptionROEWeaponFreePossible() then - self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = EscortGroup:OptionROEWeaponFree(), ParamMessage = "Opening fire on targets of opportunity!" } ) + return self +end + +--- Defines a menu to let the escort set its rules of engagement. +-- All rules of engagement will appear under the menu **ROE**. +-- @param #ESCORT self +-- @return #ESCORT +function ESCORT:MenuROE( MenuTextFormat ) + self:F( MenuTextFormat ) + + if not self.EscortMenuROE then + -- Rules of Engagement + self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) + if self.EscortGroup:OptionROEHoldFirePossible() then + self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEHoldFire(), ParamMessage = "Holding weapons!" } ) + end + if self.EscortGroup:OptionROEReturnFirePossible() then + self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEReturnFire(), ParamMessage = "Returning fire!" } ) + end + if self.EscortGroup:OptionROEOpenFirePossible() then + self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEOpenFire(), ParamMessage = "Opening fire on designated targets!!" } ) + end + if self.EscortGroup:OptionROEWeaponFreePossible() then + self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEWeaponFree(), ParamMessage = "Opening fire on targets of opportunity!" } ) + end end - -- Reaction to Threats - self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) - if EscortGroup:OptionROTNoReactionPossible() then - self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = EscortGroup:OptionROTNoReaction(), ParamMessage = "Fighting until death!" } ) - end - if EscortGroup:OptionROTPassiveDefensePossible() then - self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = EscortGroup:OptionROTPassiveDefense(), ParamMessage = "Defending using jammers, chaff and flares!" } ) - end - if EscortGroup:OptionROTEvadeFirePossible() then - self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = EscortGroup:OptionROTEvadeFire(), ParamMessage = "Evading on enemy fire!" } ) - end - if EscortGroup:OptionROTVerticalPossible() then - self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = EscortGroup:OptionROTVertical(), ParamMessage = "Evading on enemy fire with vertical manoeuvres!" } ) - end - - -- Mission Resume Menu Root - self.EscortMenuResumeMission = MENU_CLIENT:New( self.EscortClient, "Resume the escort mission", self.EscortMenu ) + return self +end - -- Initialize the EscortGroup - - self.EscortGroup:WayPointInitialize(1) - - self.EscortGroup:OptionROTVertical() - self.EscortGroup:OptionROEOpenFire() - - - self.ReportTargetsScheduler = routines.scheduleFunction( self._ReportTargetsScheduler, { self }, timer.getTime() + 1, 30 ) - - EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " .. - "We're escorting your flight. " .. - "Use the Radio Menu and F10 and use the options under + " .. EscortName .. "\n", - 60, EscortClient - ) + +--- Defines a menu to let the escort set its evasion when under threat. +-- All rules of engagement will appear under the menu **Evasion**. +-- @param #ESCORT self +-- @return #ESCORT +function ESCORT:MenuEvasion( MenuTextFormat ) + self:F( MenuTextFormat ) + + if self.EscortGroup:IsAir() then + if not self.EscortMenuEvasion then + -- Reaction to Threats + self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) + if self.EscortGroup:OptionROTNoReactionPossible() then + self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTNoReaction(), ParamMessage = "Fighting until death!" } ) + end + if self.EscortGroup:OptionROTPassiveDefensePossible() then + self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTPassiveDefense(), ParamMessage = "Defending using jammers, chaff and flares!" } ) + end + if self.EscortGroup:OptionROTEvadeFirePossible() then + self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTEvadeFire(), ParamMessage = "Evading on enemy fire!" } ) + end + if self.EscortGroup:OptionROTVerticalPossible() then + self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTVertical(), ParamMessage = "Evading on enemy fire with vertical manoeuvres!" } ) + end + end + end + + return self +end + +--- Defines a menu to let the escort resume its mission from a waypoint on its route. +-- All rules of engagement will appear under the menu **Resume mission from**. +-- @param #ESCORT self +-- @return #ESCORT +function ESCORT:MenuResumeMission() + self:F() + + if not self.EscortMenuResumeMission then + -- Mission Resume Menu Root + self.EscortMenuResumeMission = MENU_CLIENT:New( self.EscortClient, "Resume mission from", self.EscortMenu ) + end + + return self end @@ -12364,48 +13239,41 @@ function ESCORT._HoldPosition( MenuParam ) local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - - routines.removeFunction( self.FollowScheduler ) - EscortGroup:SetTask( EscortGroup:TaskHoldPosition( 300 ) ) - EscortGroup:MessageToClient( "Holding Position.", 10, EscortClient ) -end - ---- @param #MENUPARAM MenuParam -function ESCORT._HoldPositionNearBy( MenuParam ) - - local self = MenuParam.ParamSelf - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - --MenuParam.ParamSelf.EscortGroup:TaskOrbitCircleAtVec2( MenuParam.ParamSelf.EscortClient:GetPointVec2(), 300, 30, 0 ) + local OrbitGroup = MenuParam.ParamOrbitGroup -- Group#GROUP + local OrbitUnit = OrbitGroup:GetUnit(1) -- Unit#UNIT + local OrbitHeight = MenuParam.ParamHeight + local OrbitSeconds = MenuParam.ParamSeconds -- Not implemented yet routines.removeFunction( self.FollowScheduler ) - + local PointFrom = {} - local GroupPoint = EscortGroup:GetPointVec2() + local GroupPoint = EscortGroup:GetUnit(1):GetPositionVec3() PointFrom = {} PointFrom.x = GroupPoint.x - PointFrom.y = GroupPoint.y + PointFrom.y = GroupPoint.z PointFrom.speed = 250 PointFrom.type = AI.Task.WaypointType.TURNING_POINT - PointFrom.alt = EscortClient:GetAltitude() + PointFrom.alt = GroupPoint.y PointFrom.alt_type = AI.Task.AltitudeType.BARO - local ClientPoint = MenuParam.ParamSelf.EscortClient:GetPointVec2() + local OrbitPoint = OrbitUnit:GetPointVec2() local PointTo = {} - PointTo.x = ClientPoint.x - PointTo.y = ClientPoint.y + PointTo.x = OrbitPoint.x + PointTo.y = OrbitPoint.y PointTo.speed = 250 PointTo.type = AI.Task.WaypointType.TURNING_POINT - PointTo.alt = EscortClient:GetAltitude() + PointTo.alt = OrbitHeight PointTo.alt_type = AI.Task.AltitudeType.BARO - PointTo.task = EscortGroup:TaskOrbitCircleAtVec2( EscortClient:GetPointVec2(), 300, 30, 0 ) - + PointTo.task = EscortGroup:TaskOrbitCircleAtVec2( OrbitPoint, OrbitHeight, 0 ) + local Points = { PointFrom, PointTo } - + + EscortGroup:OptionROEHoldFire() + EscortGroup:OptionROTPassiveDefense() + EscortGroup:SetTask( EscortGroup:TaskRoute( Points ) ) - EscortGroup:MessageToClient( "Rejoining to your location. Please hold at your location.", 10, EscortClient ) + EscortGroup:MessageToClient( "Orbiting at location.", 10, EscortClient ) end --- @param #MENUPARAM MenuParam @@ -12414,10 +13282,10 @@ function ESCORT._JoinUpAndFollow( MenuParam ) local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - - local Distance = MenuParam.ParamDistance - - self:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) + + self.Distance = MenuParam.ParamDistance + + self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) end --- JoinsUp and Follows a CLIENT. @@ -12431,10 +13299,12 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) if self.FollowScheduler then routines.removeFunction( self.FollowScheduler ) end - + EscortGroup:OptionROEHoldFire() EscortGroup:OptionROTPassiveDefense() - + + self.EscortMode = ESCORT.MODE.FOLLOW + self.CT1 = 0 self.GT1 = 0 self.FollowScheduler = routines.scheduleFunction( self._FollowScheduler, { self, Distance }, timer.getTime() + 1, .5 ) @@ -12447,7 +13317,7 @@ function ESCORT._Flare( MenuParam ) local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - + local Color = MenuParam.ParamColor local Message = MenuParam.ParamMessage @@ -12464,7 +13334,7 @@ function ESCORT._Smoke( MenuParam ) local Color = MenuParam.ParamColor local Message = MenuParam.ParamMessage - + EscortGroup:GetUnit(1):Smoke( Color ) EscortGroup:MessageToClient( Message, 10, EscortClient ) end @@ -12478,7 +13348,7 @@ function ESCORT._ReportNearbyTargetsNow( MenuParam ) local EscortClient = self.EscortClient self:_ReportTargetsScheduler() - + end function ESCORT._SwitchReportNearbyTargets( MenuParam ) @@ -12486,9 +13356,9 @@ function ESCORT._SwitchReportNearbyTargets( MenuParam ) local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - + self.ReportTargets = MenuParam.ParamReportTargets - + if self.ReportTargets then if not self.ReportTargetsScheduler then self.ReportTargetsScheduler = routines.scheduleFunction( self._ReportTargetsScheduler, { self }, timer.getTime() + 1, 30 ) @@ -12505,45 +13375,56 @@ function ESCORT._ScanTargets( MenuParam ) local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - + local ScanDuration = MenuParam.ParamScanDuration if self.FollowScheduler then routines.removeFunction( self.FollowScheduler ) end - + self:T( { "FollowScheduler after removefunction: ", self.FollowScheduler } ) - + if EscortGroup:IsHelicopter() then routines.scheduleFunction( EscortGroup.PushTask, - { EscortGroup, - EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 200, 20 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - timer.getTime() + 1 - ) + { EscortGroup, + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 200, 20 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ) + }, + timer.getTime() + 1 + ) elseif EscortGroup:IsAirPlane() then routines.scheduleFunction( EscortGroup.PushTask, - { EscortGroup, - EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 1000, 500 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ) - }, - timer.getTime() + 1 - ) - end - + { EscortGroup, + EscortGroup:TaskControlled( + EscortGroup:TaskOrbitCircle( 1000, 500 ), + EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) + ) + }, + timer.getTime() + 1 + ) + end + EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) - if self.FollowScheduler then + if self.EscortMode == ESCORT.MODE.FOLLOW then self.FollowScheduler = routines.scheduleFunction( self._FollowScheduler, { self, Distance }, timer.getTime() + ScanDuration, 1 ) end end +function _Resume( EscortGroup ) + env.info( '_Resume' ) + + local Escort = EscortGroup.Escort -- #ESCORT + env.info( "EscortMode = " .. Escort.EscortMode ) + if Escort.EscortMode == ESCORT.MODE.FOLLOW then + Escort:JoinUpAndFollow( EscortGroup, Escort.EscortClient, Escort.Distance ) + end + +end + --- @param #MENUPARAM MenuParam function ESCORT._AttackTarget( MenuParam ) @@ -12555,40 +13436,37 @@ function ESCORT._AttackTarget( MenuParam ) if self.FollowScheduler then routines.removeFunction( self.FollowScheduler ) end - + self:T( AttackUnit ) - + if EscortGroup:IsAir() then EscortGroup:OptionROEOpenFire() - EscortGroup:OptionROTVertical() - routines.scheduleFunction( - EscortGroup.PushTask, - { EscortGroup, + EscortGroup:OptionROTPassiveDefense() + EscortGroup.Escort = self -- Need to do this trick to get the reference for the escort in the _Resume function. + routines.scheduleFunction( + EscortGroup.PushTask, + { EscortGroup, EscortGroup:TaskCombo( { EscortGroup:TaskAttackUnit( AttackUnit ), - EscortGroup:TaskOrbitCircle( 500, 350 ) + EscortGroup:TaskFunction( 1, 2, "_Resume", {"''"} ) } ) }, timer.getTime() + 10 ) else - routines.scheduleFunction( - EscortGroup.PushTask, - { EscortGroup, + routines.scheduleFunction( + EscortGroup.PushTask, + { EscortGroup, EscortGroup:TaskCombo( { EscortGroup:TaskFireAtPoint( AttackUnit:GetPointVec2(), 50 ) } ) - }, timer.getTime() + 10 + }, timer.getTime() + 10 ) - end + end EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) - if self.FollowScheduler then - self.FollowScheduler = routines.scheduleFunction( self._FollowScheduler, { self, Distance }, timer.getTime() + ScanDuration, 1 ) - end - end --- @param #MENUPARAM MenuParam @@ -12603,34 +13481,34 @@ function ESCORT._AssistTarget( MenuParam ) if self.FollowScheduler then routines.removeFunction( self.FollowScheduler ) end - - + + self:T( AttackUnit ) - + if EscortGroupAttack:IsAir() then EscortGroupAttack:OptionROEOpenFire() EscortGroupAttack:OptionROTVertical() - routines.scheduleFunction( - EscortGroupAttack.PushTask, - { EscortGroupAttack, + routines.scheduleFunction( + EscortGroupAttack.PushTask, + { EscortGroupAttack, EscortGroupAttack:TaskCombo( { EscortGroupAttack:TaskAttackUnit( AttackUnit ), EscortGroupAttack:TaskOrbitCircle( 500, 350 ) } ) - }, timer.getTime() + 10 + }, timer.getTime() + 10 ) else - routines.scheduleFunction( - EscortGroupAttack.PushTask, - { EscortGroupAttack, + routines.scheduleFunction( + EscortGroupAttack.PushTask, + { EscortGroupAttack, EscortGroupAttack:TaskCombo( { EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetPointVec2(), 50 ) } ) - }, timer.getTime() + 10 + }, timer.getTime() + 10 ) - end + end EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) end @@ -12641,10 +13519,10 @@ function ESCORT._ROE( MenuParam ) local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - + local EscortROEFunction = MenuParam.ParamFunction local EscortROEMessage = MenuParam.ParamMessage - + pcall( function() EscortROEFunction() end ) EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) end @@ -12669,21 +13547,21 @@ function ESCORT._ResumeMission( MenuParam ) local self = MenuParam.ParamSelf local EscortGroup = self.EscortGroup local EscortClient = self.EscortClient - + local WayPoint = MenuParam.ParamWayPoint - + routines.removeFunction( self.FollowScheduler ) self.FollowScheduler = nil local WayPoints = EscortGroup:GetTaskRoute() self:T( WayPoint, WayPoints ) - + for WayPointIgnore = 1, WayPoint do table.remove( WayPoints, 1 ) end - + routines.scheduleFunction( EscortGroup.SetTask, {EscortGroup, EscortGroup:TaskRoute( WayPoints ) }, timer.getTime() + 1 ) - + EscortGroup:MessageToClient( "Resuming mission from waypoint " .. WayPoint .. ".", 10, EscortClient ) end @@ -12694,9 +13572,9 @@ function ESCORT:RegisterRoute() self:F() local EscortGroup = self.EscortGroup -- Group#GROUP - + local TaskPoints = EscortGroup:GetTaskRoute() - + self:T( TaskPoints ) return TaskPoints @@ -12707,7 +13585,7 @@ function ESCORT:_FollowScheduler( FollowDistance ) self:F( { FollowDistance }) if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then - + local ClientUnit = self.EscortClient:GetClientGroupUnit() local GroupUnit = self.EscortGroup:GetUnit( 1 ) @@ -12723,70 +13601,70 @@ function ESCORT:_FollowScheduler( FollowDistance ) local CV2 = ClientUnit:GetPositionVec3() self.CT1 = CT2 self.CV1 = CV2 - + local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5 local CT = CT2 - CT1 - + local CS = ( 3600 / CT ) * ( CD / 1000 ) - + self:T2( { "Client:", CS, CD, CT, CV2, CV1, CT2, CT1 } ) - + local GT1 = self.GT1 local GT2 = timer.getTime() local GV1 = self.GV1 local GV2 = GroupUnit:GetPositionVec3() self.GT1 = GT2 self.GV1 = GV2 - + local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5 local GT = GT2 - GT1 - + local GS = ( 3600 / GT ) * ( GD / 1000 ) - + self:T2( { "Group:", GS, GD, GT, GV2, GV1, GT2, GT1 } ) - + -- Calculate the group direction vector local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z } - + -- Calculate GH2, GH2 with the same height as CV2. local GH2 = { x = GV2.x, y = CV2.y, z = GV2.z } - + -- Calculate the angle of GV to the orthonormal plane local alpha = math.atan2( GV.z, GV.x ) - + -- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2. -- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2)) - local CVI = { x = CV2.x + FollowDistance * math.cos(alpha), - y = GH2.y, - z = CV2.z + FollowDistance * math.sin(alpha), - } - + local CVI = { x = CV2.x + FollowDistance * math.cos(alpha), + y = GH2.y, + z = CV2.z + FollowDistance * math.sin(alpha), + } + -- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction. local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z } - + -- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s. -- We need to calculate this vector to predict the point the escort group needs to fly to according its speed. -- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right... local DVu = { x = DV.x / FollowDistance, y = DV.y / FollowDistance, z = DV.z / FollowDistance } - + -- Now we can calculate the group destination vector GDV. local GDV = { x = DVu.x * CS * 8 + CVI.x, y = CVI.y, z = DVu.z * CS * 8 + CVI.z } self:T2( { "CV2:", CV2 } ) self:T2( { "CVI:", CVI } ) self:T2( { "GDV:", GDV } ) - + -- Measure distance between client and group local CatchUpDistance = ( ( GDV.x - GV2.x )^2 + ( GDV.y - GV2.y )^2 + ( GDV.z - GV2.z )^2 ) ^ 0.5 - - -- The calculation of the Speed would simulate that the group would take 30 seconds to overcome + + -- The calculation of the Speed would simulate that the group would take 30 seconds to overcome -- the requested Distance). local Time = 10 - local CatchUpSpeed = ( CatchUpDistance - ( CS * 8.4 ) ) / Time - + local CatchUpSpeed = ( CatchUpDistance - ( CS * 8.4 ) ) / Time + local Speed = CS + CatchUpSpeed - if Speed < 0 then + if Speed < 0 then Speed = 0 - end + end self:T( { "Client Speed, Escort Speed, Speed, FlyDistance, Time:", CS, GS, Speed, Distance, Time } ) @@ -12803,90 +13681,93 @@ end --- Report Targets Scheduler. -- @param #ESCORT self function ESCORT:_ReportTargetsScheduler() - self:F( self.EscortGroup:GetName() ) + self:F( self.EscortGroup:GetName() ) if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then local EscortGroupName = self.EscortGroup:GetName() local EscortTargets = self.EscortGroup:GetDetectedTargets() - + local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets - + local EscortTargetMessages = "" for EscortTargetID, EscortTarget in pairs( EscortTargets ) do local EscortObject = EscortTarget.object self:T( EscortObject ) if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then - - local EscortTargetUnit = UNIT:New( EscortObject ) - local EscortTargetUnitName = EscortTargetUnit:GetName() - - - --- local EscortTargetIsDetected, --- EscortTargetIsVisible, --- EscortTargetLastTime, --- EscortTargetKnowType, --- EscortTargetKnowDistance, --- EscortTargetLastPos, --- EscortTargetLastVelocity --- = self.EscortGroup:IsTargetDetected( EscortObject ) --- --- self:T( { EscortTargetIsDetected, --- EscortTargetIsVisible, --- EscortTargetLastTime, --- EscortTargetKnowType, --- EscortTargetKnowDistance, --- EscortTargetLastPos, --- EscortTargetLastVelocity } ) - - local EscortTargetUnitPositionVec3 = EscortTargetUnit:GetPositionVec3() - local EscortPositionVec3 = self.EscortGroup:GetPositionVec3() - local Distance = ( ( EscortTargetUnitPositionVec3.x - EscortPositionVec3.x )^2 + - ( EscortTargetUnitPositionVec3.y - EscortPositionVec3.y )^2 + - ( EscortTargetUnitPositionVec3.z - EscortPositionVec3.z )^2 - ) ^ 0.5 / 1000 + local EscortTargetUnit = UNIT:New( EscortObject ) + local EscortTargetUnitName = EscortTargetUnit:GetName() - self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) - if Distance <= 15 then - if not ClientEscortTargets[EscortTargetUnitName] then - ClientEscortTargets[EscortTargetUnitName] = {} - end - ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit - ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible - ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type - ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance - else - if ClientEscortTargets[EscortTargetUnitName] then - ClientEscortTargets[EscortTargetUnitName] = nil - end + -- local EscortTargetIsDetected, + -- EscortTargetIsVisible, + -- EscortTargetLastTime, + -- EscortTargetKnowType, + -- EscortTargetKnowDistance, + -- EscortTargetLastPos, + -- EscortTargetLastVelocity + -- = self.EscortGroup:IsTargetDetected( EscortObject ) + -- + -- self:T( { EscortTargetIsDetected, + -- EscortTargetIsVisible, + -- EscortTargetLastTime, + -- EscortTargetKnowType, + -- EscortTargetKnowDistance, + -- EscortTargetLastPos, + -- EscortTargetLastVelocity } ) + + + local EscortTargetUnitPositionVec3 = EscortTargetUnit:GetPositionVec3() + local EscortPositionVec3 = self.EscortGroup:GetPositionVec3() + local Distance = ( ( EscortTargetUnitPositionVec3.x - EscortPositionVec3.x )^2 + + ( EscortTargetUnitPositionVec3.y - EscortPositionVec3.y )^2 + + ( EscortTargetUnitPositionVec3.z - EscortPositionVec3.z )^2 + ) ^ 0.5 / 1000 + + self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) + + if Distance <= 15 then + + if not ClientEscortTargets[EscortTargetUnitName] then + ClientEscortTargets[EscortTargetUnitName] = {} end + ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit + ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible + ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type + ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance + else + if ClientEscortTargets[EscortTargetUnitName] then + ClientEscortTargets[EscortTargetUnitName] = nil + end + end end end - + self:T( { "Sorting Targets Table:", ClientEscortTargets } ) table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) self:T( { "Sorted Targets Table:", ClientEscortTargets } ) -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. self.EscortMenuAttackNearbyTargets:RemoveSubMenus() - self.EscortMenuTargetAssistance:RemoveSubMenus() - + + if self.EscortMenuTargetAssistance then + self.EscortMenuTargetAssistance:RemoveSubMenus() + end + --for MenuIndex = 1, #self.EscortMenuAttackTargets do -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() --end - + if ClientEscortTargets then for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do - + for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - + if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then - + local EscortTargetMessage = "" local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() @@ -12895,54 +13776,56 @@ function ESCORT:_ReportTargetsScheduler() else EscortTargetMessage = EscortTargetMessage .. "Unknown target at " end - + local EscortTargetUnitPositionVec3 = ClientEscortTargetData.AttackUnit:GetPositionVec3() local EscortPositionVec3 = self.EscortGroup:GetPositionVec3() - local Distance = ( ( EscortTargetUnitPositionVec3.x - EscortPositionVec3.x )^2 + - ( EscortTargetUnitPositionVec3.y - EscortPositionVec3.y )^2 + - ( EscortTargetUnitPositionVec3.z - EscortPositionVec3.z )^2 - ) ^ 0.5 / 1000 - + local Distance = ( ( EscortTargetUnitPositionVec3.x - EscortPositionVec3.x )^2 + + ( EscortTargetUnitPositionVec3.y - EscortPositionVec3.y )^2 + + ( EscortTargetUnitPositionVec3.z - EscortPositionVec3.z )^2 + ) ^ 0.5 / 1000 + self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } ) if ClientEscortTargetData.visible == false then EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km" else EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km" end - + if ClientEscortTargetData.visible then EscortTargetMessage = EscortTargetMessage .. ", visual" end - + if ClientEscortGroupName == EscortGroupName then - + MENU_CLIENT_COMMAND:New( self.EscortClient, - EscortTargetMessage, - self.EscortMenuAttackNearbyTargets, - ESCORT._AttackTarget, - { ParamSelf = self, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) - EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage + EscortTargetMessage, + self.EscortMenuAttackNearbyTargets, + ESCORT._AttackTarget, + { ParamSelf = self, + ParamUnit = ClientEscortTargetData.AttackUnit + } + ) + EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage else - local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) - MENU_CLIENT_COMMAND:New( self.EscortClient, - EscortTargetMessage, - MenuTargetAssistance, - ESCORT._AssistTarget, - { ParamSelf = self, - ParamEscortGroup = EscortGroupData.EscortGroup, - ParamUnit = ClientEscortTargetData.AttackUnit - } - ) + if self.EscortMenuTargetAssistance then + local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) + MENU_CLIENT_COMMAND:New( self.EscortClient, + EscortTargetMessage, + MenuTargetAssistance, + ESCORT._AssistTarget, + { ParamSelf = self, + ParamEscortGroup = EscortGroupData.EscortGroup, + ParamUnit = ClientEscortTargetData.AttackUnit + } + ) + end end else ClientEscortTargetData = nil end end end - + if EscortTargetMessages ~= "" and self.ReportTargets == true then self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) else @@ -12950,22 +13833,24 @@ function ESCORT:_ReportTargetsScheduler() end end - self.EscortMenuResumeMission:RemoveSubMenus() - --- if self.EscortMenuResumeWayPoints then --- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do --- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) --- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() --- end --- end + if self.EscortMenuResumeMission then + self.EscortMenuResumeMission:RemoveSubMenus() - local TaskPoints = self:RegisterRoute() - for WayPointID, WayPoint in pairs( TaskPoints ) do - local EscortPositionVec3 = self.EscortGroup:GetPositionVec3() - local Distance = ( ( WayPoint.x - EscortPositionVec3.x )^2 + - ( WayPoint.y - EscortPositionVec3.z )^2 - ) ^ 0.5 / 1000 - MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) + -- if self.EscortMenuResumeWayPoints then + -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do + -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) + -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() + -- end + -- end + + local TaskPoints = self:RegisterRoute() + for WayPointID, WayPoint in pairs( TaskPoints ) do + local EscortPositionVec3 = self.EscortGroup:GetPositionVec3() + local Distance = ( ( WayPoint.x - EscortPositionVec3.x )^2 + + ( WayPoint.y - EscortPositionVec3.z )^2 + ) ^ 0.5 / 1000 + MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) + end end else diff --git a/Moose/CleanUp.lua b/Moose/CleanUp.lua index ac28d7738..236f63e14 100644 --- a/Moose/CleanUp.lua +++ b/Moose/CleanUp.lua @@ -10,6 +10,7 @@ Include.File( "Task" ) --- The CLEANUP class. -- @type CLEANUP +-- @extends Base#BASE CLEANUP = { ClassName = "CLEANUP", ZoneNames = {}, @@ -77,9 +78,8 @@ function CLEANUP:_DestroyUnit( CleanUpUnit, CleanUpUnitName ) local CleanUpGroupUnits = CleanUpGroup:getUnits() if #CleanUpGroupUnits == 1 then local CleanUpGroupName = CleanUpGroup:getName() - local Event = {["initiator"]=CleanUpUnit,["id"]=8} - world.onEvent( Event ) - trigger.action.deactivateGroup( CleanUpGroup ) + --self:CreateEventCrash( timer.getTime(), CleanUpUnit ) + CleanUpGroup:destroy() self:T( { "Destroyed Group:", CleanUpGroupName } ) else CleanUpUnit:destroy() @@ -116,6 +116,8 @@ function CLEANUP:_OnEventBirth( Event ) _EventDispatcher:OnEngineShutDownForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) _EventDispatcher:OnEngineStartUpForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) _EventDispatcher:OnHitForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self ) + _EventDispatcher:OnPilotDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self ) + _EventDispatcher:OnDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self ) _EventDispatcher:OnCrashForUnit( Event.IniDCSUnitName, self._EventCrash, self ) _EventDispatcher:OnShotForUnit( Event.IniDCSUnitName, self._EventShot, self ) @@ -135,9 +137,10 @@ end -- Crashed units go into a CleanUpList for removal. -- @param #CLEANUP self -- @param DCSTypes#Event event -function CLEANUP:_EventCrash( event ) - self:F( { event } ) +function CLEANUP:_EventCrash( Event ) + self:F( { Event } ) + --TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed. --MESSAGE:New( "Crash ", "Crash", 10, "Crash" ):ToAll() -- self:T("before getGroup") -- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired @@ -151,6 +154,7 @@ function CLEANUP:_EventCrash( event ) 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. @@ -248,9 +252,11 @@ local CleanUpSurfaceTypeText = { --- At the defined time interval, CleanUp the Groups within the CleanUpList. -- @param #CLEANUP self function CLEANUP:_CleanUpScheduler() - self:F( "CleanUp Scheduler" ) + self:F( { "CleanUp Scheduler" } ) + local CleanUpCount = 0 for CleanUpUnitName, UnitData in pairs( self.CleanUpList ) do + CleanUpCount = CleanUpCount + 1 self:T( { CleanUpUnitName, UnitData } ) local CleanUpUnit = Unit.getByName(UnitData.CleanUpUnitName) @@ -312,5 +318,6 @@ function CLEANUP:_CleanUpScheduler() self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE end end + self:T(CleanUpCount) end diff --git a/Moose/Database.lua b/Moose/Database.lua index a05b31fea..58d2a5011 100644 --- a/Moose/Database.lua +++ b/Moose/Database.lua @@ -13,31 +13,31 @@ Include.File( "Event" ) -- @type DATABASE -- @extends Base#BASE DATABASE = { - ClassName = "DATABASE", - Units = {}, - Groups = {}, - NavPoints = {}, - Statics = {}, - Players = {}, - ActivePlayers = {}, - ClientsByName = {}, - ClientsByID = {}, + ClassName = "DATABASE", + Units = {}, + Groups = {}, + NavPoints = {}, + Statics = {}, + Players = {}, + ActivePlayers = {}, + ClientsByName = {}, + ClientsByID = {}, } -DATABASECoalition = -{ - [1] = "Red", - [2] = "Blue", -} +local _DATABASECoalition = + { + [1] = "Red", + [2] = "Blue", + } -DATABASECategory = -{ - [Unit.Category.AIRPLANE] = "Plane", - [Unit.Category.HELICOPTER] = "Helicopter", - [Unit.Category.GROUND_UNIT] = "Vehicle", - [Unit.Category.SHIP] = "Ship", - [Unit.Category.STRUCTURE] = "Structure", -} +local _DATABASECategory = + { + [Unit.Category.AIRPLANE] = "Plane", + [Unit.Category.HELICOPTER] = "Helicopter", + [Unit.Category.GROUND_UNIT] = "Vehicle", + [Unit.Category.SHIP] = "Ship", + [Unit.Category.STRUCTURE] = "Structure", + } --- Creates a new DATABASE Object to administer the Groups defined and alive within the DCSRTE. @@ -47,80 +47,79 @@ DATABASECategory = -- DBObject = DATABASE:New() function DATABASE:New() - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) - - self.Navpoints = {} - self.Units = {} - --Build routines.db.units and self.Navpoints - for coa_name, coa_data in pairs(env.mission.coalition) do + -- Inherits from BASE + local self = BASE:Inherit( self, BASE:New() ) - if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then - self.Units[coa_name] = {} - - ---------------------------------------------- - -- build nav points DB - self.Navpoints[coa_name] = {} - if coa_data.nav_points then --navpoints - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - self.Navpoints[coa_name][nav_ind] = routines.utils.deepCopy(nav_data) + self.Navpoints = {} + self.Units = {} + --Build routines.db.units and self.Navpoints + for coa_name, coa_data in pairs(env.mission.coalition) do - self.Navpoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. - self.Navpoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. - self.Navpoints[coa_name][nav_ind]['point']['x'] = nav_data.x - self.Navpoints[coa_name][nav_ind]['point']['y'] = 0 - self.Navpoints[coa_name][nav_ind]['point']['z'] = nav_data.y - end - end - end - ------------------------------------------------- - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local countryName = string.lower(cntry_data.name) - self.Units[coa_name][countryName] = {} - self.Units[coa_name][countryName]["countryId"] = cntry_data.id + if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then + self.Units[coa_name] = {} - if type(cntry_data) == 'table' then --just making sure - - for obj_type_name, obj_type_data in pairs(cntry_data) do - - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check - - local category = obj_type_name - - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - - self.Units[coa_name][countryName][category] = {} - - for group_num, GroupTemplate in pairs(obj_type_data.group) do - - if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group - self:_RegisterGroup( GroupTemplate ) - end --if GroupTemplate and GroupTemplate.units then - end --for group_num, GroupTemplate in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --if type(cntry_data) == 'table' then - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do + ---------------------------------------------- + -- build nav points DB + self.Navpoints[coa_name] = {} + if coa_data.nav_points then --navpoints + for nav_ind, nav_data in pairs(coa_data.nav_points) do - --self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) - _EventDispatcher:OnDead( self.OnDeadOrCrash, self ) - _EventDispatcher:OnCrash( self.OnDeadOrCrash, self ) - _EventDispatcher:OnHit( self.OnHit, self ) - - self.SchedulerId = routines.scheduleFunction( DATABASE._FollowPlayers, { self }, 0, 5 ) - - self:ScoreMenu() - - - return self + if type(nav_data) == 'table' then + self.Navpoints[coa_name][nav_ind] = routines.utils.deepCopy(nav_data) + + self.Navpoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. + self.Navpoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. + self.Navpoints[coa_name][nav_ind]['point']['x'] = nav_data.x + self.Navpoints[coa_name][nav_ind]['point']['y'] = 0 + self.Navpoints[coa_name][nav_ind]['point']['z'] = nav_data.y + end + end + end + ------------------------------------------------- + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + + local countryName = string.lower(cntry_data.name) + self.Units[coa_name][countryName] = {} + self.Units[coa_name][countryName]["countryId"] = cntry_data.id + + if type(cntry_data) == 'table' then --just making sure + + for obj_type_name, obj_type_data in pairs(cntry_data) do + + if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check + + local category = obj_type_name + + if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! + + self.Units[coa_name][countryName][category] = {} + + for group_num, GroupTemplate in pairs(obj_type_data.group) do + + if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group + self:_RegisterGroup( GroupTemplate ) + end --if GroupTemplate and GroupTemplate.units then + end --for group_num, GroupTemplate in pairs(obj_type_data.group) do + end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then + end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then + end --for obj_type_name, obj_type_data in pairs(cntry_data) do + end --if type(cntry_data) == 'table' then + end --for cntry_id, cntry_data in pairs(coa_data.country) do + end --if coa_data.country then --there is a country table + end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then + end --for coa_name, coa_data in pairs(mission.coalition) do + + --self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) + _EventDispatcher:OnDead( self._EventOnDeadOrCrash, self ) + _EventDispatcher:OnCrash( self._EventOnDeadOrCrash, self ) + _EventDispatcher:OnHit( self._EventOnHit, self ) + + self.SchedulerId = routines.scheduleFunction( DATABASE._FollowPlayers, { self }, 0, 5 ) + + self:ScoreMenu() + + return self end @@ -130,49 +129,49 @@ end -- This method is used by the SPAWN class. function DATABASE:Spawn( SpawnTemplate ) - self:T( { SpawnTemplate.SpawnCountryID, SpawnTemplate.SpawnCategoryID, SpawnTemplate.name } ) - - -- Copy the spawn variables of the template in temporary storage, nullify, and restore the spawn variables. - local SpawnCoalitionID = SpawnTemplate.SpawnCoalitionID - local SpawnCountryID = SpawnTemplate.SpawnCountryID - local SpawnCategoryID = SpawnTemplate.SpawnCategoryID - - -- Nullify - SpawnTemplate.SpawnCoalitionID = nil - SpawnTemplate.SpawnCountryID = nil - SpawnTemplate.SpawnCategoryID = nil - - self:_RegisterGroup( SpawnTemplate ) - coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate ) + self:T( { SpawnTemplate.SpawnCountryID, SpawnTemplate.SpawnCategoryID, SpawnTemplate.name } ) - -- Restore - SpawnTemplate.SpawnCoalitionID = SpawnCoalitionID - SpawnTemplate.SpawnCountryID = SpawnCountryID - SpawnTemplate.SpawnCategoryID = SpawnCategoryID + -- Copy the spawn variables of the template in temporary storage, nullify, and restore the spawn variables. + local SpawnCoalitionID = SpawnTemplate.SpawnCoalitionID + local SpawnCountryID = SpawnTemplate.SpawnCountryID + local SpawnCategoryID = SpawnTemplate.SpawnCategoryID - - local SpawnGroup = GROUP:New( Group.getByName( SpawnTemplate.name ) ) - return SpawnGroup + -- Nullify + SpawnTemplate.SpawnCoalitionID = nil + SpawnTemplate.SpawnCountryID = nil + SpawnTemplate.SpawnCategoryID = nil + + self:_RegisterGroup( SpawnTemplate ) + coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate ) + + -- Restore + SpawnTemplate.SpawnCoalitionID = SpawnCoalitionID + SpawnTemplate.SpawnCountryID = SpawnCountryID + SpawnTemplate.SpawnCategoryID = SpawnCategoryID + + + local SpawnGroup = GROUP:New( Group.getByName( SpawnTemplate.name ) ) + return SpawnGroup end --- Set a status to a Group within the Database, this to check crossing events for example. function DATABASE:SetStatusGroup( GroupName, Status ) - self:F( Status ) + self:F( Status ) - self.Groups[GroupName].Status = Status + self.Groups[GroupName].Status = Status end --- Get a status to a Group within the Database, this to check crossing events for example. function DATABASE:GetStatusGroup( GroupName ) - self:F( Status ) + self:F( Status ) - if self.Groups[GroupName] then - return self.Groups[GroupName].Status - else - return "" - end + if self.Groups[GroupName] then + return self.Groups[GroupName].Status + else + return "" + end end @@ -183,35 +182,35 @@ end --- Registers new Group Templates within the DATABASE Object. function DATABASE:_RegisterGroup( GroupTemplate ) - local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name) + local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name) - if not self.Groups[GroupTemplateName] then - self.Groups[GroupTemplateName] = {} - self.Groups[GroupTemplateName].Status = nil - end - self.Groups[GroupTemplateName].GroupName = GroupTemplateName - self.Groups[GroupTemplateName].Template = GroupTemplate - self.Groups[GroupTemplateName].groupId = GroupTemplate.groupId - self.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units - self.Groups[GroupTemplateName].Units = GroupTemplate.units - - self:T( { "Group", self.Groups[GroupTemplateName].GroupName, self.Groups[GroupTemplateName].UnitCount } ) - - for unit_num, UnitTemplate in pairs(GroupTemplate.units) do - - local UnitTemplateName = env.getValueDictByKey(UnitTemplate.name) - self.Units[UnitTemplateName] = {} - self.Units[UnitTemplateName].UnitName = UnitTemplateName - self.Units[UnitTemplateName].Template = UnitTemplate - self.Units[UnitTemplateName].GroupName = GroupTemplateName - self.Units[UnitTemplateName].GroupTemplate = GroupTemplate - self.Units[UnitTemplateName].GroupId = GroupTemplate.groupId - if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then - self.ClientsByName[UnitTemplateName] = UnitTemplate - self.ClientsByID[UnitTemplate.unitId] = UnitTemplate - end - self:T( { "Unit", self.Units[UnitTemplateName].UnitName } ) - end + if not self.Groups[GroupTemplateName] then + self.Groups[GroupTemplateName] = {} + self.Groups[GroupTemplateName].Status = nil + end + self.Groups[GroupTemplateName].GroupName = GroupTemplateName + self.Groups[GroupTemplateName].Template = GroupTemplate + self.Groups[GroupTemplateName].groupId = GroupTemplate.groupId + self.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units + self.Groups[GroupTemplateName].Units = GroupTemplate.units + + self:T( { "Group", self.Groups[GroupTemplateName].GroupName, self.Groups[GroupTemplateName].UnitCount } ) + + for unit_num, UnitTemplate in pairs(GroupTemplate.units) do + + local UnitTemplateName = env.getValueDictByKey(UnitTemplate.name) + self.Units[UnitTemplateName] = {} + self.Units[UnitTemplateName].UnitName = UnitTemplateName + self.Units[UnitTemplateName].Template = UnitTemplate + self.Units[UnitTemplateName].GroupName = GroupTemplateName + self.Units[UnitTemplateName].GroupTemplate = GroupTemplate + self.Units[UnitTemplateName].GroupId = GroupTemplate.groupId + if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then + self.ClientsByName[UnitTemplateName] = UnitTemplate + self.ClientsByID[UnitTemplate.unitId] = UnitTemplate + end + self:T( { "Unit", self.Units[UnitTemplateName].UnitName } ) + end end @@ -220,96 +219,94 @@ end --- Track DCSRTE DEAD or CRASH events for the internal scoring. -function DATABASE:OnDeadOrCrash( event ) - self:F( { event } ) +-- @param #DATABASE self +-- @param Event#EVENTDATA Event +function DATABASE:_EventOnDeadOrCrash( Event ) + self:F( { Event } ) - local TargetUnit = nil - local TargetGroup = nil - local TargetUnitName = "" - local TargetGroupName = "" - local TargetPlayerName = "" - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil + local TargetUnit = nil + local TargetGroup = nil + local TargetUnitName = "" + local TargetGroupName = "" + local TargetPlayerName = "" + local TargetCoalition = nil + local TargetCategory = nil + local TargetType = nil + local TargetUnitCoalition = nil + local TargetUnitCategory = nil + local TargetUnitType = nil - if event.initiator and Object.getCategory(event.initiator) == Object.Category.UNIT then - - TargetUnit = event.initiator - TargetGroup = Unit.getGroup( TargetUnit ) - TargetUnitDesc = TargetUnit:getDesc() - - TargetUnitName = TargetUnit:getName() - if TargetGroup and TargetGroup:isExist() then - TargetGroupName = TargetGroup:getName() - end - TargetPlayerName = TargetUnit:getPlayerName() + if Event.IniDCSUnit then - TargetCoalition = TargetUnit:getCoalition() - --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnitDesc.category -- Workaround - TargetType = TargetUnit:getTypeName() + TargetUnit = Event.IniDCSUnit + TargetUnitName = Event.IniDCSUnitName + TargetGroup = Event.IniDCSGroup + TargetGroupName = Event.IniDCSGroupName + TargetPlayerName = TargetUnit:getPlayerName() - TargetUnitCoalition = DATABASECoalition[TargetCoalition] - TargetUnitCategory = DATABASECategory[TargetCategory] - TargetUnitType = TargetType + TargetCoalition = TargetUnit:getCoalition() + --TargetCategory = TargetUnit:getCategory() + TargetCategory = TargetUnit:getDesc().category -- Workaround + TargetType = TargetUnit:getTypeName() - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) - end + TargetUnitCoalition = _DATABASECoalition[TargetCoalition] + TargetUnitCategory = _DATABASECategory[T1argetCategory] + TargetUnitType = TargetType - for PlayerName, PlayerData in pairs( self.Players ) do - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got killed" ) + self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) + end - -- Some variables - local InitUnitName = PlayerData.UnitName - local InitUnitType = PlayerData.UnitType - local InitCoalition = PlayerData.UnitCoalition - local InitCategory = PlayerData.UnitCategory - local InitUnitCoalition = DATABASECoalition[InitCoalition] - local InitUnitCategory = DATABASECategory[InitCategory] - - self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) + for PlayerName, PlayerData in pairs( self.Players ) do + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Something got killed" ) - -- What is he hitting? - if TargetCategory then - if PlayerData and PlayerData.Hit and PlayerData.Hit[TargetCategory] and PlayerData.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? - if not PlayerData.Kill[TargetCategory] then - PlayerData.Kill[TargetCategory] = {} - end - if not PlayerData.Kill[TargetCategory][TargetType] then - PlayerData.Kill[TargetCategory][TargetType] = {} - PlayerData.Kill[TargetCategory][TargetType].Score = 0 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = 0 - PlayerData.Kill[TargetCategory][TargetType].Penalty = 0 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = 0 - end + -- Some variables + local InitUnitName = PlayerData.UnitName + local InitUnitType = PlayerData.UnitType + local InitCoalition = PlayerData.UnitCoalition + local InitCategory = PlayerData.UnitCategory + local InitUnitCoalition = _DATABASECoalition[InitCoalition] + local InitUnitCategory = _DATABASECategory[InitCategory] - if InitCoalition == TargetCoalition then - PlayerData.Penalty = PlayerData.Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].Penalty = PlayerData.Kill[TargetCategory][TargetType].Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = PlayerData.Kill[TargetCategory][TargetType].PenaltyKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill .. " times. Penalty: -" .. PlayerData.Kill[TargetCategory][TargetType].Penalty .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - "", 5, "/PENALTY" .. PlayerName .. "/" .. InitUnitName ):ToAll() - self:ScoreAdd( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - PlayerData.Score = PlayerData.Score + 10 - PlayerData.Kill[TargetCategory][TargetType].Score = PlayerData.Kill[TargetCategory][TargetType].Score + 10 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = PlayerData.Kill[TargetCategory][TargetType].ScoreKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].ScoreKill .. " times. Score: " .. PlayerData.Kill[TargetCategory][TargetType].Score .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - "", 5, "/SCORE" .. PlayerName .. "/" .. InitUnitName ):ToAll() - self:ScoreAdd( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end - end - end - end + self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) + + -- What is he hitting? + if TargetCategory then + if PlayerData and PlayerData.Hit and PlayerData.Hit[TargetCategory] and PlayerData.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? + if not PlayerData.Kill[TargetCategory] then + PlayerData.Kill[TargetCategory] = {} + end + if not PlayerData.Kill[TargetCategory][TargetType] then + PlayerData.Kill[TargetCategory][TargetType] = {} + PlayerData.Kill[TargetCategory][TargetType].Score = 0 + PlayerData.Kill[TargetCategory][TargetType].ScoreKill = 0 + PlayerData.Kill[TargetCategory][TargetType].Penalty = 0 + PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = 0 + end + + if InitCoalition == TargetCoalition then + PlayerData.Penalty = PlayerData.Penalty + 25 + PlayerData.Kill[TargetCategory][TargetType].Penalty = PlayerData.Kill[TargetCategory][TargetType].Penalty + 25 + PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = PlayerData.Kill[TargetCategory][TargetType].PenaltyKill + 1 + MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + PlayerData.Kill[TargetCategory][TargetType].PenaltyKill .. " times. Penalty: -" .. PlayerData.Kill[TargetCategory][TargetType].Penalty .. + ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, + "", 5, "/PENALTY" .. PlayerName .. "/" .. InitUnitName ):ToAll() + self:ScoreAdd( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + PlayerData.Score = PlayerData.Score + 10 + PlayerData.Kill[TargetCategory][TargetType].Score = PlayerData.Kill[TargetCategory][TargetType].Score + 10 + PlayerData.Kill[TargetCategory][TargetType].ScoreKill = PlayerData.Kill[TargetCategory][TargetType].ScoreKill + 1 + MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + PlayerData.Kill[TargetCategory][TargetType].ScoreKill .. " times. Score: " .. PlayerData.Kill[TargetCategory][TargetType].Score .. + ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, + "", 5, "/SCORE" .. PlayerName .. "/" .. InitUnitName ):ToAll() + self:ScoreAdd( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + end + end + end + end end @@ -319,20 +316,20 @@ end --- Follows new players entering Clients within the DCSRTE. function DATABASE:_FollowPlayers() - self:F3( "_FollowPlayers" ) + self:F3( "_FollowPlayers" ) - local ClientUnit = 0 - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } - local unitId - local unitData - local AlivePlayerUnits = {} - - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - self:T3( { "_FollowPlayers", CoalitionData } ) - for UnitId, UnitData in pairs( CoalitionData ) do - self:_AddPlayerFromUnit( UnitData ) - end - end + local ClientUnit = 0 + local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } + local unitId + local unitData + local AlivePlayerUnits = {} + + for CoalitionId, CoalitionData in pairs( CoalitionsData ) do + self:T3( { "_FollowPlayers", CoalitionData } ) + for UnitId, UnitData in pairs( CoalitionData ) do + self:_AddPlayerFromUnit( UnitData ) + end + end end @@ -342,102 +339,102 @@ end --- Add a new player entering a Unit. function DATABASE:_AddPlayerFromUnit( UnitData ) - self:F( UnitData ) + self:F( UnitData ) - if UnitData:isExist() then - local UnitName = UnitData:getName() - local PlayerName = UnitData:getPlayerName() - local UnitDesc = UnitData:getDesc() - local UnitCategory = UnitDesc.category - local UnitCoalition = UnitData:getCoalition() - local UnitTypeName = UnitData:getTypeName() + if UnitData:isExist() then + local UnitName = UnitData:getName() + local PlayerName = UnitData:getPlayerName() + local UnitDesc = UnitData:getDesc() + local UnitCategory = UnitDesc.category + local UnitCoalition = UnitData:getCoalition() + local UnitTypeName = UnitData:getTypeName() - self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) + self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) - if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... - self.Players[PlayerName] = {} - self.Players[PlayerName].Hit = {} - self.Players[PlayerName].Kill = {} - self.Players[PlayerName].Mission = {} - - -- for CategoryID, CategoryName in pairs( DATABASECategory ) do - -- self.Players[PlayerName].Hit[CategoryID] = {} - -- self.Players[PlayerName].Kill[CategoryID] = {} - -- end - self.Players[PlayerName].HitPlayers = {} - self.Players[PlayerName].HitUnits = {} + if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... + self.Players[PlayerName] = {} + self.Players[PlayerName].Hit = {} + self.Players[PlayerName].Kill = {} + self.Players[PlayerName].Mission = {} + + -- for CategoryID, CategoryName in pairs( DATABASECategory ) do + -- self.Players[PlayerName].Hit[CategoryID] = {} + -- self.Players[PlayerName].Kill[CategoryID] = {} + -- end + self.Players[PlayerName].HitPlayers = {} + self.Players[PlayerName].HitUnits = {} self.Players[PlayerName].Score = 0 - self.Players[PlayerName].Penalty = 0 - self.Players[PlayerName].PenaltyCoalition = 0 - self.Players[PlayerName].PenaltyWarning = 0 - end + self.Players[PlayerName].Penalty = 0 + self.Players[PlayerName].PenaltyCoalition = 0 + self.Players[PlayerName].PenaltyWarning = 0 + end - if not self.Players[PlayerName].UnitCoalition then - self.Players[PlayerName].UnitCoalition = UnitCoalition - else - if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then - self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 - self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. DATABASECoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. DATABASECoalition[UnitCoalition] .. - "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", - "", - 2, - "/PENALTYCOALITION" .. PlayerName - ):ToAll() - self:ScoreAdd( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, DATABASECoalition[self.Players[PlayerName].UnitCoalition], DATABASECategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, - UnitName, DATABASECoalition[UnitCoalition], DATABASECategory[UnitCategory], UnitData:getTypeName() ) - end - end - self.Players[PlayerName].UnitName = UnitName - self.Players[PlayerName].UnitCoalition = UnitCoalition - self.Players[PlayerName].UnitCategory = UnitCategory - self.Players[PlayerName].UnitType = UnitTypeName + if not self.Players[PlayerName].UnitCoalition then + self.Players[PlayerName].UnitCoalition = UnitCoalition + else + if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then + self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 + self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 + MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. _DATABASECoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _DATABASECoalition[UnitCoalition] .. + "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", + "", + 2, + "/PENALTYCOALITION" .. PlayerName + ):ToAll() + self:ScoreAdd( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _DATABASECoalition[self.Players[PlayerName].UnitCoalition], _DATABASECategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, + UnitName, _DATABASECoalition[UnitCoalition], _DATABASECategory[UnitCategory], UnitData:getTypeName() ) + end + end + self.Players[PlayerName].UnitName = UnitName + self.Players[PlayerName].UnitCoalition = UnitCoalition + self.Players[PlayerName].UnitCategory = UnitCategory + self.Players[PlayerName].UnitType = UnitTypeName if self.Players[PlayerName].Penalty > 100 then if self.Players[PlayerName].PenaltyWarning < 1 then - MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than 150, you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, - "", - 30, - "/PENALTYCOALITION" .. PlayerName - ):ToAll() + MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than 150, you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, + "", + 30, + "/PENALTYCOALITION" .. PlayerName + ):ToAll() self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 end end - + if self.Players[PlayerName].Penalty > 150 then ClientGroup = GROUP:NewFromDCSUnit( UnitData ) ClientGroup:Destroy() - MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", - "", - 10, - "/PENALTYCOALITION" .. PlayerName - ):ToAll() + MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", + "", + 10, + "/PENALTYCOALITION" .. PlayerName + ):ToAll() end - - end + + end end --- Registers Scores the players completing a Mission Task. function DATABASE:_AddMissionTaskScore( PlayerUnit, MissionName, Score ) - self:F( { PlayerUnit, MissionName, Score } ) + self:F( { PlayerUnit, MissionName, Score } ) - local PlayerName = PlayerUnit:getPlayerName() - - if not self.Players[PlayerName].Mission[MissionName] then - self.Players[PlayerName].Mission[MissionName] = {} - self.Players[PlayerName].Mission[MissionName].ScoreTask = 0 - self.Players[PlayerName].Mission[MissionName].ScoreMission = 0 - end - - self:T( PlayerName ) - self:T( self.Players[PlayerName].Mission[MissionName] ) + local PlayerName = PlayerUnit:getPlayerName() - self.Players[PlayerName].Score = self.Players[PlayerName].Score + Score - self.Players[PlayerName].Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score + if not self.Players[PlayerName].Mission[MissionName] then + self.Players[PlayerName].Mission[MissionName] = {} + self.Players[PlayerName].Mission[MissionName].ScoreTask = 0 + self.Players[PlayerName].Mission[MissionName].ScoreMission = 0 + end - MESSAGE:New( "Player '" .. PlayerName .. "' has finished another Task in Mission '" .. MissionName .. "'. " .. - Score .. " Score points added.", + self:T( PlayerName ) + self:T( self.Players[PlayerName].Mission[MissionName] ) + + self.Players[PlayerName].Score = self.Players[PlayerName].Score + Score + self.Players[PlayerName].Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score + + MESSAGE:New( "Player '" .. PlayerName .. "' has finished another Task in Mission '" .. MissionName .. "'. " .. + Score .. " Score points added.", "", 20, "/SCORETASK" .. PlayerName ):ToAll() _Database:ScoreAdd( PlayerName, "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:getName() ) @@ -446,19 +443,19 @@ end --- Registers Mission Scores for possible multiple players that contributed in the Mission. function DATABASE:_AddMissionScore( MissionName, Score ) - self:F( { PlayerUnit, MissionName, Score } ) + self:F( { PlayerUnit, MissionName, Score } ) - for PlayerName, PlayerData in pairs( self.Players ) do - - if PlayerData.Mission[MissionName] then - PlayerData.Score = PlayerData.Score + Score - PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score - MESSAGE:New( "Player '" .. PlayerName .. "' has finished Mission '" .. MissionName .. "'. " .. - Score .. " Score points added.", - "", 20, "/SCOREMISSION" .. PlayerName ):ToAll() - _Database:ScoreAdd( PlayerName, "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) - end - end + for PlayerName, PlayerData in pairs( self.Players ) do + + if PlayerData.Mission[MissionName] then + PlayerData.Score = PlayerData.Score + Score + PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score + MESSAGE:New( "Player '" .. PlayerName .. "' has finished Mission '" .. MissionName .. "'. " .. + Score .. " Score points added.", + "", 20, "/SCOREMISSION" .. PlayerName ):ToAll() + _Database:ScoreAdd( PlayerName, "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) + end + end end @@ -466,373 +463,367 @@ end -- @section Events -function DATABASE:OnHit( event ) - self:F( { event } ) +--- Handles the OnHit event for the scoring. +-- @param #DATABASE self +-- @param Event#EVENTDATA Event +function DATABASE:_EventOnHit( Event ) + self:F( { Event } ) - local InitUnit = nil - local InitUnitName = "" - local InitGroupName = "" - local InitPlayerName = "dummy" + local InitUnit = nil + local InitUnitName = "" + local InitGroup = nil + local InitGroupName = "" + local InitPlayerName = "dummy" - local InitCoalition = nil - local InitCategory = nil - local InitType = nil - local InitUnitCoalition = nil - local InitUnitCategory = nil - local InitUnitType = nil + local InitCoalition = nil + local InitCategory = nil + local InitType = nil + local InitUnitCoalition = nil + local InitUnitCategory = nil + local InitUnitType = nil - local TargetUnit = nil - local TargetUnitName = "" - local TargetGroupName = "" - local TargetPlayerName = "" + local TargetUnit = nil + local TargetUnitName = "" + local TargetGroup = nil + local TargetGroupName = "" + local TargetPlayerName = "" - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil + local TargetCoalition = nil + local TargetCategory = nil + local TargetType = nil + local TargetUnitCoalition = nil + local TargetUnitCategory = nil + local TargetUnitType = nil - if event.initiator and event.initiator:getName() then - - if event.initiator and Object.getCategory(event.initiator) == Object.Category.UNIT then - - InitUnit = event.initiator - InitGroup = Unit.getGroup( InitUnit ) - InitUnitDesc = InitUnit:getDesc() - - InitUnitName = InitUnit:getName() - if InitGroup and InitGroup:isExist() then - InitGroupName = InitGroup:getName() - end - InitPlayerName = InitUnit:getPlayerName() - - InitCoalition = InitUnit:getCoalition() - --InitCategory = InitUnit:getCategory() - InitCategory = InitUnitDesc.category -- Workaround - InitType = InitUnit:getTypeName() + if Event.IniDCSUnit then - InitUnitCoalition = DATABASECoalition[InitCoalition] - InitUnitCategory = DATABASECategory[InitCategory] - InitUnitType = InitType - - self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } ) - self:T( { InitUnitDesc } ) - end + InitUnit = Event.IniDCSUnit + InitUnitName = Event.IniDCSUnitName + InitGroup = Event.IniDCSGroup + InitGroupName = Event.IniDCSGroupName + InitPlayerName = InitUnit:getPlayerName() - - if event.target and Object.getCategory(event.target) == Object.Category.UNIT then - - TargetUnit = event.target - TargetGroup = Unit.getGroup( TargetUnit ) - TargetUnitDesc = TargetUnit:getDesc() - - TargetUnitName = TargetUnit:getName() - if TargetGroup and TargetGroup:isExist() then - TargetGroupName = TargetGroup:getName() - end - TargetPlayerName = TargetUnit:getPlayerName() + InitCoalition = InitUnit:getCoalition() + --TODO: Workaround Client DCS Bug + --InitCategory = InitUnit:getCategory() + InitCategory = InitUnit:getDesc().category + InitType = InitUnit:getTypeName() - TargetCoalition = TargetUnit:getCoalition() - --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnitDesc.category -- Workaround - TargetType = TargetUnit:getTypeName() + InitUnitCoalition = _DATABASECoalition[InitCoalition] + InitUnitCategory = _DATABASECategory[InitCategory] + InitUnitType = InitType - TargetUnitCoalition = DATABASECoalition[TargetCoalition] - TargetUnitCategory = DATABASECategory[TargetCategory] - TargetUnitType = TargetType - - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } ) - self:T( { TargetUnitDesc } ) - end - - if InitPlayerName ~= nil then -- It is a player that is hitting something - self:_AddPlayerFromUnit( InitUnit ) - if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( TargetUnit ) - self.Players[InitPlayerName].HitPlayers = self.Players[InitPlayerName].HitPlayers + 1 - end - - self:T( "Hitting Something" ) - -- What is he hitting? - if TargetCategory then - if not self.Players[InitPlayerName].Hit[TargetCategory] then - self.Players[InitPlayerName].Hit[TargetCategory] = {} - end - if not self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] then - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] = {} - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = 0 - end - local Score = 0 - if InitCoalition == TargetCoalition then - self.Players[InitPlayerName].Penalty = self.Players[InitPlayerName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: -" .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - "", - 2, - "/PENALTY" .. InitPlayerName .. "/" .. InitUnitName - ):ToAll() - self:ScoreAdd( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - self.Players[InitPlayerName].Score = self.Players[InitPlayerName].Score + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score + 1 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit .. " times. Score: " .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - "", - 2, - "/SCORE" .. InitPlayerName .. "/" .. InitUnitName - ):ToAll() - self:ScoreAdd( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end - end - elseif InitPlayerName == nil then -- It is an AI hitting a player??? - - end - end + self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } ) + end + + + if Event.TgtDCSUnit then + + TargetUnit = Event.TgtDCSUnit + TargetUnitName = Event.TgtDCSUnitName + TargetGroup = Event.TgtDCSGroup + TargetGroupName = Event.TgtDCSGroupName + TargetPlayerName = TargetUnit:getPlayerName() + + TargetCoalition = TargetUnit:getCoalition() + --TODO: Workaround Client DCS Bug + --TargetCategory = TargetUnit:getCategory() + TargetCategory = TargetUnit:getDesc().category + TargetType = TargetUnit:getTypeName() + + TargetUnitCoalition = _DATABASECoalition[TargetCoalition] + TargetUnitCategory = _DATABASECategory[TargetCategory] + TargetUnitType = TargetType + + self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } ) + end + + if InitPlayerName ~= nil then -- It is a player that is hitting something + self:_AddPlayerFromUnit( InitUnit ) + if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + self:_AddPlayerFromUnit( TargetUnit ) + self.Players[InitPlayerName].HitPlayers = self.Players[InitPlayerName].HitPlayers + 1 + end + + self:T( "Hitting Something" ) + -- What is he hitting? + if TargetCategory then + if not self.Players[InitPlayerName].Hit[TargetCategory] then + self.Players[InitPlayerName].Hit[TargetCategory] = {} + end + if not self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] then + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] = {} + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = 0 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = 0 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = 0 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = 0 + end + local Score = 0 + if InitCoalition == TargetCoalition then + self.Players[InitPlayerName].Penalty = self.Players[InitPlayerName].Penalty + 10 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty + 10 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit + 1 + MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: -" .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty .. + ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, + "", + 2, + "/PENALTY" .. InitPlayerName .. "/" .. InitUnitName + ):ToAll() + self:ScoreAdd( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + self.Players[InitPlayerName].Score = self.Players[InitPlayerName].Score + 10 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score + 1 + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit + 1 + MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit .. " times. Score: " .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score .. + ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, + "", + 2, + "/SCORE" .. InitPlayerName .. "/" .. InitUnitName + ):ToAll() + self:ScoreAdd( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + end + end + elseif InitPlayerName == nil then -- It is an AI hitting a player??? + + end end function DATABASE:ReportScoreAll() -env.info( "Hello World " ) + env.info( "Hello World " ) - local ScoreMessage = "" - local PlayerMessage = "" - - self:T( "Score Report" ) + local ScoreMessage = "" + local PlayerMessage = "" - for PlayerName, PlayerData in pairs( self.Players ) do - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) + self:T( "Score Report" ) - -- Some variables - local InitUnitCoalition = DATABASECoalition[PlayerData.UnitCoalition] - local InitUnitCategory = DATABASECategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = ":\n" - - local ScoreMessageHits = "" + for PlayerName, PlayerData in pairs( self.Players ) do + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - for CategoryID, CategoryName in pairs( DATABASECategory ) do - self:T( CategoryName ) - if PlayerData.Hit[CategoryID] then - local Score = 0 - local ScoreHit = 0 - local Penalty = 0 - local PenaltyHit = 0 - self:T( "Hit scores exist for player " .. PlayerName ) - for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do - Score = Score + UnitData.Score - ScoreHit = ScoreHit + UnitData.ScoreHit - Penalty = Penalty + UnitData.Penalty - PenaltyHit = UnitData.PenaltyHit - end - local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) - self:T( ScoreMessageHit ) - ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageHits ~= "" then - ScoreMessage = ScoreMessage .. " Hits: " .. ScoreMessageHits .. "\n" - end - - local ScoreMessageKills = "" - for CategoryID, CategoryName in pairs( DATABASECategory ) do - self:T( "Kill scores exist for player " .. PlayerName ) - if PlayerData.Kill[CategoryID] then - local Score = 0 - local ScoreKill = 0 - local Penalty = 0 - local PenaltyKill = 0 - - for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do - Score = Score + UnitData.Score - ScoreKill = ScoreKill + UnitData.ScoreKill - Penalty = Penalty + UnitData.Penalty - PenaltyKill = PenaltyKill + UnitData.PenaltyKill - end - - local ScoreMessageKill = string.format( " %s:%d ", CategoryName, Score - Penalty ) - self:T( ScoreMessageKill ) - ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill + -- Some variables + local InitUnitCoalition = _DATABASECoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _DATABASECategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageKills ~= "" then - ScoreMessage = ScoreMessage .. " Kills: " .. ScoreMessageKills .. "\n" - end - - local ScoreMessageCoalitionChangePenalties = "" - if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) - PlayerPenalty = PlayerPenalty + PlayerData.Penalty - end - if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. " Coalition Penalties: " .. ScoreMessageCoalitionChangePenalties .. "\n" - end + local PlayerScore = 0 + local PlayerPenalty = 0 - local ScoreMessageMission = "" - local ScoreMission = 0 - local ScoreTask = 0 - for MissionName, MissionData in pairs( PlayerData.Mission ) do - ScoreMission = ScoreMission + MissionData.ScoreMission - ScoreTask = ScoreTask + MissionData.ScoreTask - ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " - end - PlayerScore = PlayerScore + ScoreMission + ScoreTask + ScoreMessage = ":\n" - if ScoreMessageMission ~= "" then - ScoreMessage = ScoreMessage .. " Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")\n" - end - - PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score:%d (%d Score -%d Penalties)%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) - end - end - MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() + local ScoreMessageHits = "" + + for CategoryID, CategoryName in pairs( _DATABASECategory ) do + self:T( CategoryName ) + if PlayerData.Hit[CategoryID] then + local Score = 0 + local ScoreHit = 0 + local Penalty = 0 + local PenaltyHit = 0 + self:T( "Hit scores exist for player " .. PlayerName ) + for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do + Score = Score + UnitData.Score + ScoreHit = ScoreHit + UnitData.ScoreHit + Penalty = Penalty + UnitData.Penalty + PenaltyHit = UnitData.PenaltyHit + end + local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageHit ) + ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageHits ~= "" then + ScoreMessage = ScoreMessage .. " Hits: " .. ScoreMessageHits .. "\n" + end + + local ScoreMessageKills = "" + for CategoryID, CategoryName in pairs( _DATABASECategory ) do + self:T( "Kill scores exist for player " .. PlayerName ) + if PlayerData.Kill[CategoryID] then + local Score = 0 + local ScoreKill = 0 + local Penalty = 0 + local PenaltyKill = 0 + + for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do + Score = Score + UnitData.Score + ScoreKill = ScoreKill + UnitData.ScoreKill + Penalty = Penalty + UnitData.Penalty + PenaltyKill = PenaltyKill + UnitData.PenaltyKill + end + + local ScoreMessageKill = string.format( " %s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageKill ) + ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill + + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageKills ~= "" then + ScoreMessage = ScoreMessage .. " Kills: " .. ScoreMessageKills .. "\n" + end + + local ScoreMessageCoalitionChangePenalties = "" + if PlayerData.PenaltyCoalition ~= 0 then + ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) + PlayerPenalty = PlayerPenalty + PlayerData.Penalty + end + if ScoreMessageCoalitionChangePenalties ~= "" then + ScoreMessage = ScoreMessage .. " Coalition Penalties: " .. ScoreMessageCoalitionChangePenalties .. "\n" + end + + local ScoreMessageMission = "" + local ScoreMission = 0 + local ScoreTask = 0 + for MissionName, MissionData in pairs( PlayerData.Mission ) do + ScoreMission = ScoreMission + MissionData.ScoreMission + ScoreTask = ScoreTask + MissionData.ScoreTask + ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " + end + PlayerScore = PlayerScore + ScoreMission + ScoreTask + + if ScoreMessageMission ~= "" then + ScoreMessage = ScoreMessage .. " Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")\n" + end + + PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score:%d (%d Score -%d Penalties)%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) + end + end + MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() end function DATABASE:ReportScorePlayer() -env.info( "Hello World " ) + env.info( "Hello World " ) - local ScoreMessage = "" - local PlayerMessage = "" - - self:T( "Score Report" ) + local ScoreMessage = "" + local PlayerMessage = "" - for PlayerName, PlayerData in pairs( self.Players ) do - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) + self:T( "Score Report" ) - -- Some variables - local InitUnitCoalition = DATABASECoalition[PlayerData.UnitCoalition] - local InitUnitCategory = DATABASECategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = "" - - local ScoreMessageHits = "" + for PlayerName, PlayerData in pairs( self.Players ) do + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - for CategoryID, CategoryName in pairs( DATABASECategory ) do - self:T( CategoryName ) - if PlayerData.Hit[CategoryID] then - local Score = 0 - local ScoreHit = 0 - local Penalty = 0 - local PenaltyHit = 0 - self:T( "Hit scores exist for player " .. PlayerName ) - for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do - Score = Score + UnitData.Score - ScoreHit = ScoreHit + UnitData.ScoreHit - Penalty = Penalty + UnitData.Penalty - PenaltyHit = UnitData.PenaltyHit - end - local ScoreMessageHit = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreHit, PenaltyHit ) - self:T( ScoreMessageHit ) - ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageHits ~= "" then - ScoreMessage = ScoreMessage .. "\n Hits: " .. ScoreMessageHits .. " " - end - - local ScoreMessageKills = "" - for CategoryID, CategoryName in pairs( DATABASECategory ) do - self:T( "Kill scores exist for player " .. PlayerName ) - if PlayerData.Kill[CategoryID] then - local Score = 0 - local ScoreKill = 0 - local Penalty = 0 - local PenaltyKill = 0 - - for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do - Score = Score + UnitData.Score - ScoreKill = ScoreKill + UnitData.ScoreKill - Penalty = Penalty + UnitData.Penalty - PenaltyKill = PenaltyKill + UnitData.PenaltyKill - end - - local ScoreMessageKill = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill ) - self:T( ScoreMessageKill ) - ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill + -- Some variables + local InitUnitCoalition = _DATABASECoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _DATABASECategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageKills ~= "" then - ScoreMessage = ScoreMessage .. "\n Kills: " .. ScoreMessageKills .. " " - end - - local ScoreMessageCoalitionChangePenalties = "" - if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) - PlayerPenalty = PlayerPenalty + PlayerData.Penalty - end - if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. "\n Coalition: " .. ScoreMessageCoalitionChangePenalties .. " " - end + local PlayerScore = 0 + local PlayerPenalty = 0 - local ScoreMessageMission = "" - local ScoreMission = 0 - local ScoreTask = 0 - for MissionName, MissionData in pairs( PlayerData.Mission ) do - ScoreMission = ScoreMission + MissionData.ScoreMission - ScoreTask = ScoreTask + MissionData.ScoreTask - ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " - end - PlayerScore = PlayerScore + ScoreMission + ScoreTask + ScoreMessage = "" - if ScoreMessageMission ~= "" then - ScoreMessage = ScoreMessage .. "\n Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") " - end - - PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties ):%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) - end - end - MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() + local ScoreMessageHits = "" + + for CategoryID, CategoryName in pairs( _DATABASECategory ) do + self:T( CategoryName ) + if PlayerData.Hit[CategoryID] then + local Score = 0 + local ScoreHit = 0 + local Penalty = 0 + local PenaltyHit = 0 + self:T( "Hit scores exist for player " .. PlayerName ) + for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do + Score = Score + UnitData.Score + ScoreHit = ScoreHit + UnitData.ScoreHit + Penalty = Penalty + UnitData.Penalty + PenaltyHit = UnitData.PenaltyHit + end + local ScoreMessageHit = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreHit, PenaltyHit ) + self:T( ScoreMessageHit ) + ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageHits ~= "" then + ScoreMessage = ScoreMessage .. "\n Hits: " .. ScoreMessageHits .. " " + end + + local ScoreMessageKills = "" + for CategoryID, CategoryName in pairs( _DATABASECategory ) do + self:T( "Kill scores exist for player " .. PlayerName ) + if PlayerData.Kill[CategoryID] then + local Score = 0 + local ScoreKill = 0 + local Penalty = 0 + local PenaltyKill = 0 + + for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do + Score = Score + UnitData.Score + ScoreKill = ScoreKill + UnitData.ScoreKill + Penalty = Penalty + UnitData.Penalty + PenaltyKill = PenaltyKill + UnitData.PenaltyKill + end + + local ScoreMessageKill = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill ) + self:T( ScoreMessageKill ) + ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill + + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + end + if ScoreMessageKills ~= "" then + ScoreMessage = ScoreMessage .. "\n Kills: " .. ScoreMessageKills .. " " + end + + local ScoreMessageCoalitionChangePenalties = "" + if PlayerData.PenaltyCoalition ~= 0 then + ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) + PlayerPenalty = PlayerPenalty + PlayerData.Penalty + end + if ScoreMessageCoalitionChangePenalties ~= "" then + ScoreMessage = ScoreMessage .. "\n Coalition: " .. ScoreMessageCoalitionChangePenalties .. " " + end + + local ScoreMessageMission = "" + local ScoreMission = 0 + local ScoreTask = 0 + for MissionName, MissionData in pairs( PlayerData.Mission ) do + ScoreMission = ScoreMission + MissionData.ScoreMission + ScoreTask = ScoreTask + MissionData.ScoreTask + ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " + end + PlayerScore = PlayerScore + ScoreMission + ScoreTask + + if ScoreMessageMission ~= "" then + ScoreMessage = ScoreMessage .. "\n Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") " + end + + PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties ):%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) + end + end + MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll() end function DATABASE:ScoreMenu() - local ReportScore = SUBMENU:New( 'Scoring' ) - local ReportAllScores = COMMANDMENU:New( 'Score All Active Players', ReportScore, DATABASE.ReportScoreAll, self ) - local ReportPlayerScores = COMMANDMENU:New('Your Current Score', ReportScore, DATABASE.ReportScorePlayer, self ) + local ReportScore = SUBMENU:New( 'Scoring' ) + local ReportAllScores = COMMANDMENU:New( 'Score All Active Players', ReportScore, DATABASE.ReportScoreAll, self ) + local ReportPlayerScores = COMMANDMENU:New('Your Current Score', ReportScore, DATABASE.ReportScorePlayer, self ) end @@ -841,112 +832,113 @@ end -- File Logic for tracking the scores function DATABASE:SecondsToClock(sSeconds) -local nSeconds = sSeconds - if nSeconds == 0 then - --return nil; - return "00:00:00"; - else - nHours = string.format("%02.f", math.floor(nSeconds/3600)); - nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60))); - nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60)); - return nHours..":"..nMins..":"..nSecs - end + local nSeconds = sSeconds + if nSeconds == 0 then + --return nil; + return "00:00:00"; + else + nHours = string.format("%02.f", math.floor(nSeconds/3600)); + nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60))); + nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60)); + return nHours..":"..nMins..":"..nSecs + end end function DATABASE:ScoreOpen() - if lfs then - local fdir = lfs.writedir() .. [[Logs\]] .. "Player_Scores_" .. os.date( "%Y-%m-%d_%H-%M-%S" ) .. ".csv" - self.StatFile, self.err = io.open(fdir,"w+") - if not self.StatFile then - error( "Error: Cannot open 'Player Scores.csv' file in " .. lfs.writedir() ) - end - self.StatFile:write( '"RunID","Time","PlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' ) - - self.RunID = os.date("%y-%m-%d_%H-%M-%S") - end + if lfs then + local fdir = lfs.writedir() .. [[Logs\]] .. "Player_Scores_" .. os.date( "%Y-%m-%d_%H-%M-%S" ) .. ".csv" + self.StatFile, self.err = io.open(fdir,"w+") + if not self.StatFile then + error( "Error: Cannot open 'Player Scores.csv' file in " .. lfs.writedir() ) + end + self.StatFile:write( '"RunID","Time","PlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' ) + + self.RunID = os.date("%y-%m-%d_%H-%M-%S") + end end function DATABASE:ScoreAdd( PlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - --write statistic information to file - local ScoreTime = self:SecondsToClock(timer.getTime()) - PlayerName = PlayerName:gsub( '"', '_' ) + --write statistic information to file + local ScoreTime = self:SecondsToClock(timer.getTime()) + PlayerName = PlayerName:gsub( '"', '_' ) - if PlayerUnitName and PlayerUnitName ~= '' then - local PlayerUnit = Unit.getByName( PlayerUnitName ) - - if PlayerUnit then - if not PlayerUnitCategory then - --PlayerUnitCategory = DATABASECategory[PlayerUnit:getCategory()] - PlayerUnitCategory = DATABASECategory[PlayerUnit:getDesc().category] - end - - if not PlayerUnitCoalition then - PlayerUnitCoalition = DATABASECoalition[PlayerUnit:getCoalition()] - end - - if not PlayerUnitType then - PlayerUnitType = PlayerUnit:getTypeName() - end - else - PlayerUnitName = '' - PlayerUnitCategory = '' - PlayerUnitCoalition = '' - PlayerUnitType = '' - end - else - PlayerUnitName = '' - PlayerUnitCategory = '' - PlayerUnitCoalition = '' - PlayerUnitType = '' - end - - if not TargetUnitCoalition then - TargetUnitCoalition = '' - end - - if not TargetUnitCategory then - TargetUnitCategory = '' - end - - if not TargetUnitType then - TargetUnitType = '' - end - - if not TargetUnitName then - TargetUnitName = '' - end + if PlayerUnitName and PlayerUnitName ~= '' then + local PlayerUnit = Unit.getByName( PlayerUnitName ) - if lfs then - self.StatFile:write( - '"' .. self.RunID .. '"' .. ',' .. - '' .. ScoreTime .. '' .. ',' .. - '"' .. PlayerName .. '"' .. ',' .. - '"' .. ScoreType .. '"' .. ',' .. - '"' .. PlayerUnitCoalition .. '"' .. ',' .. - '"' .. PlayerUnitCategory .. '"' .. ',' .. - '"' .. PlayerUnitType .. '"' .. ',' .. - '"' .. PlayerUnitName .. '"' .. ',' .. - '"' .. TargetUnitCoalition .. '"' .. ',' .. - '"' .. TargetUnitCategory .. '"' .. ',' .. - '"' .. TargetUnitType .. '"' .. ',' .. - '"' .. TargetUnitName .. '"' .. ',' .. - '' .. ScoreTimes .. '' .. ',' .. - '' .. ScoreAmount - ) - - self.StatFile:write( "\n" ) - end + if PlayerUnit then + if not PlayerUnitCategory then + --PlayerUnitCategory = DATABASECategory[PlayerUnit:getCategory()] + PlayerUnitCategory = _DATABASECategory[PlayerUnit:getDesc().category] + end + + if not PlayerUnitCoalition then + PlayerUnitCoalition = _DATABASECoalition[PlayerUnit:getCoalition()] + end + + if not PlayerUnitType then + PlayerUnitType = PlayerUnit:getTypeName() + end + else + PlayerUnitName = '' + PlayerUnitCategory = '' + PlayerUnitCoalition = '' + PlayerUnitType = '' + end + else + PlayerUnitName = '' + PlayerUnitCategory = '' + PlayerUnitCoalition = '' + PlayerUnitType = '' + end + + if not TargetUnitCoalition then + TargetUnitCoalition = '' + end + + if not TargetUnitCategory then + TargetUnitCategory = '' + end + + if not TargetUnitType then + TargetUnitType = '' + end + + if not TargetUnitName then + TargetUnitName = '' + end + + if lfs then + self.StatFile:write( + '"' .. self.RunID .. '"' .. ',' .. + '' .. ScoreTime .. '' .. ',' .. + '"' .. PlayerName .. '"' .. ',' .. + '"' .. ScoreType .. '"' .. ',' .. + '"' .. PlayerUnitCoalition .. '"' .. ',' .. + '"' .. PlayerUnitCategory .. '"' .. ',' .. + '"' .. PlayerUnitType .. '"' .. ',' .. + '"' .. PlayerUnitName .. '"' .. ',' .. + '"' .. TargetUnitCoalition .. '"' .. ',' .. + '"' .. TargetUnitCategory .. '"' .. ',' .. + '"' .. TargetUnitType .. '"' .. ',' .. + '"' .. TargetUnitName .. '"' .. ',' .. + '' .. ScoreTimes .. '' .. ',' .. + '' .. ScoreAmount + ) + + self.StatFile:write( "\n" ) + end end - + function LogClose() - if lfs then - self.StatFile:close() - end + if lfs then + self.StatFile:close() + end end _Database = DATABASE:New() -- Database#DATABASE _Database:ScoreOpen() + diff --git a/Moose/Event.lua b/Moose/Event.lua index 6ad91e3cf..a3501b44c 100644 --- a/Moose/Event.lua +++ b/Moose/Event.lua @@ -13,7 +13,7 @@ EVENT = { ClassID = 0, } -local EVENTCODES = { +local _EVENTCODES = { "S_EVENT_SHOT", "S_EVENT_HIT", "S_EVENT_TAKEOFF", @@ -69,6 +69,13 @@ function EVENT:New() return self end +function EVENT:EventText( EventID ) + + local EventText = _EVENTCODES[EventID] + + return EventText +end + --- Initializes the Events structure for the event -- @param #EVENT self @@ -76,16 +83,13 @@ end -- @param #string EventClass -- @return #EVENT.Events function EVENT:Init( EventID, EventClass ) - self:F( { EventID, EventClass } ) + self:F3( { _EVENTCODES[EventID], EventClass } ) if not self.Events[EventID] then self.Events[EventID] = {} end if not self.Events[EventID][EventClass] then self.Events[EventID][EventClass] = {} end - if not self.Events[EventID][EventClass].IniUnit then - self.Events[EventID][EventClass].IniUnit = {} - end return self.Events[EventID][EventClass] end @@ -98,7 +102,7 @@ end -- @param #function OnEventFunction -- @return #EVENT function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, OnEventFunction ) - self:F( EventTemplate ) + self:F2( EventTemplate.name ) for EventUnitID, EventUnit in pairs( EventTemplate.units ) do OnEventFunction( self, EventUnit.name, EventFunction, EventSelf ) @@ -113,7 +117,7 @@ end -- @param EventID -- @return #EVENT function EVENT:OnEventGeneric( EventFunction, EventSelf, EventID ) - self:F( { EventID } ) + self:F2( { EventID } ) local Event = self:Init( EventID, EventSelf:GetClassNameAndID() ) Event.EventFunction = EventFunction @@ -130,9 +134,12 @@ end -- @param EventID -- @return #EVENT function EVENT:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, EventID ) - self:F( EventDCSUnitName ) + self:F2( EventDCSUnitName ) local Event = self:Init( EventID, EventSelf:GetClassNameAndID() ) + if not Event.IniUnit then + Event.IniUnit = {} + end Event.IniUnit[EventDCSUnitName] = {} Event.IniUnit[EventDCSUnitName].EventFunction = EventFunction Event.IniUnit[EventDCSUnitName].EventSelf = EventSelf @@ -147,9 +154,11 @@ end -- @param EventSelf The self instance of the class for which the event is. -- @return #EVENT function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventSelf ) - self:F( { EventTemplate } ) + self:F( EventTemplate.name ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnBirthForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnBirthForUnit ) + + return self end --- Set a new listener for an S_EVENT_BIRTH event, and registers the unit born. @@ -160,7 +169,9 @@ end function EVENT:OnBirth( EventFunction, EventSelf ) self:F() - return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_BIRTH ) + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_BIRTH ) + + return self end --- Set a new listener for an S_EVENT_BIRTH event. @@ -172,7 +183,9 @@ end function EVENT:OnBirthForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) - return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_BIRTH ) + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_BIRTH ) + + return self end --- Create an OnCrash event handler for a group @@ -182,9 +195,11 @@ end -- @param EventSelf The self instance of the class for which the event is. -- @return #EVENT function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventSelf ) - self:F( EventTemplate ) + self:F( EventTemplate.name ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnCrashForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnCrashForUnit ) + + return self end --- Set a new listener for an S_EVENT_CRASH event. @@ -195,7 +210,9 @@ end function EVENT:OnCrash( EventFunction, EventSelf ) self:F() - return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_CRASH ) + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_CRASH ) + + return self end --- Set a new listener for an S_EVENT_CRASH event. @@ -206,8 +223,10 @@ end -- @return #EVENT function EVENT:OnCrashForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_CRASH ) - return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_CRASH ) + return self end --- Create an OnDead event handler for a group @@ -217,9 +236,11 @@ end -- @param EventSelf The self instance of the class for which the event is. -- @return #EVENT function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventSelf ) - self:F( EventTemplate ) + self:F( EventTemplate.name ) + + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnDeadForUnit ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnDeadForUnit ) + return self end --- Set a new listener for an S_EVENT_DEAD event. @@ -230,7 +251,9 @@ end function EVENT:OnDead( EventFunction, EventSelf ) self:F() - return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_DEAD ) + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_DEAD ) + + return self end @@ -243,7 +266,23 @@ end function EVENT:OnDeadForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) - return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_DEAD ) + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_DEAD ) + + return self +end + +--- Set a new listener for an S_EVENT_PILOT_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:OnPilotDeadForUnit( EventDCSUnitName, EventFunction, EventSelf ) + self:F( EventDCSUnitName ) + + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_PILOT_DEAD ) + + return self end --- Create an OnDead event handler for a group @@ -253,9 +292,11 @@ end -- @param EventSelf The self instance of the class for which the event is. -- @return #EVENT function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventSelf ) - self:F( EventTemplate ) + self:F( EventTemplate.name ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnLandForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnLandForUnit ) + + return self end --- Set a new listener for an S_EVENT_LAND event. @@ -267,7 +308,9 @@ end function EVENT:OnLandForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) - return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_LAND ) + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_LAND ) + + return self end --- Create an OnDead event handler for a group @@ -277,9 +320,11 @@ end -- @param EventSelf The self instance of the class for which the event is. -- @return #EVENT function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventSelf ) - self:F( EventTemplate ) + self:F( EventTemplate.name ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnTakeOffForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnTakeOffForUnit ) + + return self end --- Set a new listener for an S_EVENT_TAKEOFF event. @@ -291,7 +336,9 @@ end function EVENT:OnTakeOffForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) - return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_TAKEOFF ) + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_TAKEOFF ) + + return self end --- Create an OnDead event handler for a group @@ -301,9 +348,11 @@ end -- @param EventSelf The self instance of the class for which the event is. -- @return #EVENT function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventSelf ) - self:F( EventTemplate ) + self:F( EventTemplate.name ) - return self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnEngineShutDownForUnit ) + self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnEngineShutDownForUnit ) + + return self end --- Set a new listener for an S_EVENT_ENGINE_SHUTDOWN event. @@ -315,7 +364,9 @@ end function EVENT:OnEngineShutDownForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) - return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_SHUTDOWN ) + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_SHUTDOWN ) + + return self end --- Set a new listener for an S_EVENT_ENGINE_STARTUP event. @@ -327,7 +378,9 @@ end function EVENT:OnEngineStartUpForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) - return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_STARTUP ) + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_STARTUP ) + + return self end --- Set a new listener for an S_EVENT_SHOT event. @@ -338,7 +391,9 @@ end function EVENT:OnShot( EventFunction, EventSelf ) self:F() - return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_SHOT ) + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_SHOT ) + + return self end --- Set a new listener for an S_EVENT_SHOT event for a unit. @@ -350,7 +405,9 @@ end function EVENT:OnShotForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) - return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_SHOT ) + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_SHOT ) + + return self end --- Set a new listener for an S_EVENT_HIT event. @@ -361,7 +418,9 @@ end function EVENT:OnHit( EventFunction, EventSelf ) self:F() - return self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_HIT ) + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_HIT ) + + return self end --- Set a new listener for an S_EVENT_HIT event. @@ -373,13 +432,15 @@ end function EVENT:OnHitForUnit( EventDCSUnitName, EventFunction, EventSelf ) self:F( EventDCSUnitName ) - return self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_HIT ) + self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_HIT ) + + return self end function EVENT:onEvent( Event ) - self:F( { EVENTCODES[Event.id], Event } ) + self:F( { _EVENTCODES[Event.id], Event } ) if self and self.Events and self.Events[Event.id] then if Event.initiator and Event.initiator:getCategory() == Object.Category.UNIT then @@ -405,15 +466,15 @@ function EVENT:onEvent( Event ) if Event.weapon then Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() - Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() + --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end for ClassName, EventData in pairs( self.Events[Event.id] ) do if Event.IniDCSUnitName and EventData.IniUnit and EventData.IniUnit[Event.IniDCSUnitName] then - self:T( { "Calling event function for class ", ClassName, " unit ", Event.IniDCSUnitName } ) + self:T2( { "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 } ) + self:T2( { "Calling event function for class ", ClassName } ) EventData.EventFunction( EventData.EventSelf, Event ) end end diff --git a/Moose/Sead.lua b/Moose/Sead.lua index 2bbfd31b8..8c1ddbc6b 100644 --- a/Moose/Sead.lua +++ b/Moose/Sead.lua @@ -62,7 +62,7 @@ function SEAD:EventShot( Event ) 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 = Event.WeaponTgtDCSUnit -- Identify target + local _targetMim = Event.Weapon:getTarget() -- 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 f6659d7e1..9ad27e90e 100644 --- a/Moose/Spawn.lua +++ b/Moose/Spawn.lua @@ -471,7 +471,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) self.SpawnGroups[self.SpawnIndex].Spawned = true return self.SpawnGroups[self.SpawnIndex].Group else - self:E( { self.SpawnTemplatePrefix, "No more Groups to Spawn:", SpawnIndex, self.SpawnMaxGroups } ) + --self:E( { self.SpawnTemplatePrefix, "No more Groups to Spawn:", SpawnIndex, self.SpawnMaxGroups } ) end return nil diff --git a/Test Missions/Moose_Test_CLEANUP/Moose_Test_CLEANUP.lua b/Test Missions/Moose_Test_CLEANUP/Moose_Test_CLEANUP.lua new file mode 100644 index 000000000..1066f424a --- /dev/null +++ b/Test Missions/Moose_Test_CLEANUP/Moose_Test_CLEANUP.lua @@ -0,0 +1,10 @@ +Include.File( 'Cleanup' ) +Include.File( 'Spawn' ) +Include.File( 'Event') + +Clean = CLEANUP:New( 'CLEAN_BATUMI', 180 ) + +SpawnRU = SPAWN:New( 'RU Attack Heli Batumi'):Limit( 2, 20 ):SpawnScheduled( 2, 0.2 ) + +SpawnUS = SPAWN:New( 'US Attack Heli Batumi'):Limit( 2, 20 ):SpawnScheduled( 2, 0.2 ) + diff --git a/Test Missions/Moose_Test_CLEANUP/Moose_Test_CLEANUP.miz b/Test Missions/Moose_Test_CLEANUP/Moose_Test_CLEANUP.miz new file mode 100644 index 000000000..2627bceb2 Binary files /dev/null and b/Test Missions/Moose_Test_CLEANUP/Moose_Test_CLEANUP.miz differ