diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 10967685b..cfb7ab4cd 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -127,14 +127,14 @@ -- **IMPORTANT NOTE:** Some events can involve not just UNIT objects, but also STATIC objects!!! -- In that case the initiator or target unit fields will refer to a STATIC object! -- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated. --- The fields **IniCategory** and **TgtCategory** contain the indicator which **kind of object is involved** in the event. --- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniCategory and TgtCategory. +-- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event. +-- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory. -- Example code snippet: -- --- if Event.IniCategory == Object.Category.UNIT then +-- if Event.IniObjectCategory == Object.Category.UNIT then -- ... -- end --- if Event.IniCategory == Object.Category.STATIC then +-- if Event.IniObjectCategory == Object.Category.STATIC then -- ... -- end -- @@ -169,7 +169,7 @@ -- @module Event -- TODO: Need to update the EVENTDATA documentation with IniPlayerName and TgtPlayerName --- TODO: Need to update the EVENTDATA documentation with IniCategory and TgtCategory +-- TODO: Need to update the EVENTDATA documentation with IniObjectCategory and TgtObjectCategory @@ -220,8 +220,8 @@ EVENTS = { -- @type EVENTDATA -- @field #number id The identifier of the event. -- --- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category IniCategory (UNIT/STATIC) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). +-- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. +-- @field Dcs.DCSObject#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field Dcs.DCSUnit#Unit IniDCSUnit (UNIT/STATIC) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. -- @field #string IniDCSUnitName (UNIT/STATIC) The initiating Unit name. -- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Wrapper.Unit#UNIT} of the initiator Unit object. @@ -231,9 +231,12 @@ EVENTS = { -- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Wrapper.Group#GROUP} of the initiator Group object. -- @field #string IniGroupName (UNIT) The initiating GROUP name (same as IniDCSGroupName). -- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot. +-- @field Dcs.DCScoalition#coalition.side IniCoalition (UNIT) The coalition of the initiator. +-- @field Dcs.DCSUnit#Unit.Category IniCategory (UNIT) The category of the initiator. +-- @field #string IniTypeName (UNIT) The type name of the initiator. -- -- @field Dcs.DCSUnit#Unit target (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category TgtCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). +-- @field Dcs.DCSObject#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field Dcs.DCSUnit#Unit TgtDCSUnit (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. -- @field #string TgtDCSUnitName (UNIT/STATIC) The target Unit name. -- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Wrapper.Unit#UNIT} of the target Unit object. @@ -243,6 +246,9 @@ EVENTS = { -- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Wrapper.Group#GROUP} of the target Group object. -- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName). -- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot. +-- @field Dcs.DCScoalition#coalition.side TgtCoalition (UNIT) The coalition of the target. +-- @field Dcs.DCSUnit#Unit.Category TgtCategory (UNIT) The category of the target. +-- @field #string TgtTypeName (UNIT) The type name of the target. -- -- @field weapon The weapon used during the event. -- @field Weapon @@ -1050,8 +1056,10 @@ function EVENT:onEvent( Event ) if Event.initiator then - Event.IniCategory = Event.initiator:getCategory() - if Event.IniCategory == Object.Category.UNIT then + + Event.IniObjectCategory = Event.initiator:getCategory() + + if Event.IniObjectCategory == Object.Category.UNIT then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniUnitName = Event.IniDCSUnitName @@ -1068,19 +1076,36 @@ function EVENT:onEvent( Event ) self:E( { IniGroup = Event.IniGroup } ) end Event.IniPlayerName = Event.IniDCSUnit:getPlayerName() + Event.IniCoalition = Event.IniDCSUnit:getCoalition() + Event.IniTypeName = Event.IniDCSUnit:getTypeName() + Event.IniCategory = Event.IniDCSUnit:getDesc().category end - if Event.IniCategory == Object.Category.STATIC then + if Event.IniObjectCategory == Object.Category.STATIC then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniUnitName = Event.IniDCSUnitName Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName ) + Event.IniCoalition = Event.IniDCSUnit:getCoalition() + Event.IniCategory = Event.IniDCSUnit:getDesc().category + Event.IniTypeName = Event.IniDCSUnit:getTypeName() + end + + if Event.IniObjectCategory == Object.Category.SCENERY then + Event.IniDCSUnit = Event.initiator + Event.IniDCSUnitName = Event.IniDCSUnit:getName() + Event.IniUnitName = Event.IniDCSUnitName + Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator ) + Event.IniCategory = Event.IniDCSUnit:getDesc().category + Event.IniTypeName = Event.IniDCSUnit:getTypeName() end end if Event.target then - Event.TgtCategory = Event.target:getCategory() - if Event.TgtCategory == Object.Category.UNIT then + + Event.TgtObjectCategory = Event.target:getCategory() + + if Event.TgtObjectCategory == Object.Category.UNIT then Event.TgtDCSUnit = Event.target Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup() Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() @@ -1091,18 +1116,31 @@ function EVENT:onEvent( Event ) Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() end Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() + Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() end - if Event.TgtCategory == Object.Category.STATIC then + if Event.TgtObjectCategory == Object.Category.STATIC then Event.TgtDCSUnit = Event.target Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() Event.TgtUnitName = Event.TgtDCSUnitName Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName ) + Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() + end + + if Event.TgtObjectCategory == Object.Category.SCENERY then + Event.TgtDCSUnit = Event.target + Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() + Event.TgtUnitName = Event.TgtDCSUnitName + Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target ) + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() end end - - if Event.weapon then Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 08ea61bdc..d263245e9 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -182,6 +182,20 @@ function MESSAGE:ToCoalition( CoalitionSide ) return self end +--- Sends a MESSAGE to a Coalition if the given Condition is true. +-- @param #MESSAGE self +-- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. +-- @return #MESSAGE +function MESSAGE:ToCoalitionIf( CoalitionSide, Condition ) + self:F( CoalitionSide ) + + if Condition and Condition == true then + self:ToCoalition( CoalitionSide ) + end + + return self +end + --- Sends a MESSAGE to all players. -- @param #MESSAGE self -- @return #MESSAGE @@ -194,10 +208,24 @@ end -- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ) -- MessageAll:ToAll() function MESSAGE:ToAll() - self:F() + self:F() - self:ToCoalition( coalition.side.RED ) - self:ToCoalition( coalition.side.BLUE ) + self:ToCoalition( coalition.side.RED ) + self:ToCoalition( coalition.side.BLUE ) + + return self +end + + +--- Sends a MESSAGE to all players if the given Condition is true. +-- @param #MESSAGE self +-- @return #MESSAGE +function MESSAGE:ToAllIf( Condition ) + + if Condition and Condition == true then + self:ToCoalition( coalition.side.RED ) + self:ToCoalition( coalition.side.BLUE ) + end return self end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index d60a8cb14..6bdbf1112 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -35,8 +35,8 @@ -- -- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: -- --- * @{#ZONE_BASE.IsPointVec2InZone}(): Returns if a @{Point#POINT_VEC2} is within the zone. --- * @{#ZONE_BASE.IsPointVec3InZone}(): Returns if a @{Point#POINT_VEC3} is within the zone. +-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. +-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. -- -- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: -- @@ -145,21 +145,28 @@ -- -- Hereby the change log: -- --- 2017-02-18: ZONE_POLYGON_BASE:**GetRandomPointVec2()** added. +-- 2017-02-28: ZONE\_BASE:**IsVec2InZone()** replaces ZONE\_BASE:_IsPointVec2InZone()_. +-- 2017-02-28: ZONE\_BASE:**IsVec3InZone()** replaces ZONE\_BASE:_IsPointVec3InZone()_. +-- 2017-02-28: ZONE\_RADIUS:**IsVec2InZone()** replaces ZONE\_RADIUS:_IsPointVec2InZone()_. +-- 2017-02-28: ZONE\_RADIUS:**IsVec3InZone()** replaces ZONE\_RADIUS:_IsPointVec3InZone()_. +-- 2017-02-28: ZONE\_POLYGON:**IsVec2InZone()** replaces ZONE\_POLYGON:_IsPointVec2InZone()_. +-- 2017-02-28: ZONE\_POLYGON:**IsVec3InZone()** replaces ZONE\_POLYGON:_IsPointVec3InZone()_. -- --- 2017-02-18: ZONE_POLYGON_BASE:**GetRandomPointVec3()** added. +-- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec2()** added. -- --- 2017-02-18: ZONE_RADIUS:**GetRandomPointVec3( inner, outer )** added. +-- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec3()** added. -- --- 2017-02-18: ZONE_RADIUS:**GetRandomPointVec2( inner, outer )** added. +-- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec3( inner, outer )** added. -- --- 2016-08-15: ZONE_BASE:**GetName()** added. +-- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec2( inner, outer )** added. -- --- 2016-08-15: ZONE_BASE:**SetZoneProbability( ZoneProbability )** added. +-- 2016-08-15: ZONE\_BASE:**GetName()** added. -- --- 2016-08-15: ZONE_BASE:**GetZoneProbability()** added. +-- 2016-08-15: ZONE\_BASE:**SetZoneProbability( ZoneProbability )** added. -- --- 2016-08-15: ZONE_BASE:**GetZoneMaybe()** added. +-- 2016-08-15: ZONE\_BASE:**GetZoneProbability()** added. +-- +-- 2016-08-15: ZONE\_BASE:**GetZoneMaybe()** added. -- -- === -- @@ -212,7 +219,7 @@ end -- @param #ZONE_BASE self -- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_BASE:IsPointVec2InZone( Vec2 ) +function ZONE_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) return false @@ -222,10 +229,10 @@ end -- @param #ZONE_BASE self -- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. -- @return #boolean true if the point is within the zone. -function ZONE_BASE:IsPointVec3InZone( Vec3 ) +function ZONE_BASE:IsVec3InZone( Vec3 ) self:F2( Vec3 ) - local InZone = self:IsPointVec2InZone( { x = Vec3.x, y = Vec3.z } ) + local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) return InZone end @@ -455,7 +462,7 @@ end -- @param #ZONE_RADIUS self -- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_RADIUS:IsPointVec2InZone( Vec2 ) +function ZONE_RADIUS:IsVec2InZone( Vec2 ) self:F2( Vec2 ) local ZoneVec2 = self:GetVec2() @@ -473,10 +480,10 @@ end -- @param #ZONE_RADIUS self -- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. -- @return #boolean true if the point is within the zone. -function ZONE_RADIUS:IsPointVec3InZone( Vec3 ) +function ZONE_RADIUS:IsVec3InZone( Vec3 ) self:F2( Vec3 ) - local InZone = self:IsPointVec2InZone( { x = Vec3.x, y = Vec3.z } ) + local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) return InZone end @@ -795,7 +802,7 @@ end -- @param #ZONE_POLYGON_BASE self -- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_POLYGON_BASE:IsPointVec2InZone( Vec2 ) +function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) local Next @@ -837,7 +844,7 @@ function ZONE_POLYGON_BASE:GetRandomVec2() while Vec2Found == false do Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } self:T2( Vec2 ) - if self:IsPointVec2InZone( Vec2 ) then + if self:IsVec2InZone( Vec2 ) then Vec2Found = true end end diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index c3ed9d4e0..57699a745 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -1,7 +1,7 @@ --- Single-Player:**Yes** / Multi-Player:**Yes** / Core:**Yes** -- **Administer the scoring of player achievements, -- and create a CSV file logging the scoring events for use at team or squadron websites.** -- --- -- ![Banner Image](..\Presentations\AI_Balancer\Dia1.JPG) +-- ![Banner Image](..\Presentations\SCORING\Dia1.JPG) -- -- === -- @@ -9,14 +9,154 @@ -- -- The @{#SCORING} class administers the scoring of player achievements, -- and creates a CSV file logging the scoring events for use at team or squadron websites. --- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions. -- --- This scoring class calculates the hits and kills that players make within a simulation session. --- Scoring is calculated using a defined algorithm. --- With a small change in MissionScripting.lua, the scoring can also be logged in a CSV file, that can then be uploaded --- to a database or a BI tool to publish the scoring results to the player community. +-- The scores are calculated by scoring the hits and destroys of objects that players make, +-- which are @{Unit} and @{Static) objects within your mission. +-- +-- Scores are calculated based on the threat level of the objects involved. +-- The threat level of a unit can be a value between 0 and 10. +-- A calculated score takes the threat level of the target divided by the threat level of the player unit. +-- This provides a value between 0.1 and 10. +-- The stronger or the higher the threat of the player unit, the less score will be given in destroys. +-- That value can then be multiplied by a multiplier. A specific multiplier can be set for enemies and friendlies destroys. +-- +-- If multiple players hit the same target, and finally the target gets destroyed, each player who contributed to the target +-- destruction, will receive a score. This is important for targets that require significant damage before it can be destroyed, like +-- ships or heavy planes. +-- +-- **Additional scores** can be granted to **specific objects**, when the player(s) destroy these objects. +-- **Various @{Zone}s** can be defined for which scores are also granted when objects in that @{Zone} are destroyed. +-- This is **specifically useful** to designate **scenery targets on the map** that will generate points when destroyed. +-- +-- With a small change in MissionScripting.lua, the scoring can also be logged in a CSV file. +-- The CSV files can be used to: +-- +-- * Upload scoring to a database or a BI tool to publish the scoring results to the player community. +-- * Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results. +-- * Share scoring amoung players after the mission to discuss mission results. +-- +-- Scores can be reported. Menu options are automatically added to each group when a player joins a client slot or a CA unit. +-- Use the radio menu F10 to consult the scores while running the mission. +-- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. +-- +-- ## 1.1) Set the destroy score or penalty multiplier +-- +-- Score multipliers can be set for scores granted when enemies or friendlies are destroyed. +-- Use the method @{#SCORING.SetMultiplierDestroyScore}() to set the multiplier of enemy destroys (positive destroys). +-- Use the method @{#SCORING.SetMultiplierDestroyPenalty}() to set the multiplier of friendly destroys (negative destroys). +-- +-- local Scoring = SCORING:New( "Scoring File" ) +-- Scoring:SetMultiplierDestroyScore( 10 ) +-- Scoring:SetMultiplierDestroyPenalty( 40 ) +-- +-- The above sets the multiplier for valid scores to 10. So scores will be given in a scale from 0 to 10. +-- The penalties will be given in a scale from 0 to 40. +-- +-- ## 1.2) Define special targets that will give extra scores. +-- +-- Special targets can be set that will give extra scores to the players when these are destroyed. +-- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Unit}s. +-- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Static}s. +-- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Group}s. +-- +-- local Scoring = SCORING:New( "Scoring File" ) +-- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) +-- Scoring:AddStaticScore( STATIC:FindByName( "Static #1" ), 100 ) +-- +-- The above grants an additional score of 200 points for Unit #001 and an additional 100 points of Static #1 if these are destroyed. +-- Note that later in the mission, one can remove these scores set, for example, when the a goal achievement time limit is over. +-- For example, this can be done as follows: +-- +-- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) ) +-- +-- +-- +-- ## 1.3) Define destruction zones that will give extra scores. +-- +-- Define zones of destruction. Any object destroyed within the zone of the given category will give extra points. +-- Use the method @{#SCORING.AddZoneScore}() to add a @{Zone} for additional scoring. +-- Use the method @{#SCORING.RemoveZoneScore}() to remove a @{Zone} for additional scoring. +-- There are interesting variations that can be achieved with this functionality. For example, if the @{Zone} is a @{Zone#ZONE_UNIT}, +-- then the zone is a moving zone, and anything destroyed within that @{Zone} will generate points. +-- The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a @{Zone}, +-- just large enough around that building. +-- +-- ## 1.4) Configure fratricide level. +-- +-- When a player commits too much damage to friendlies, his penalty score will reach a certain level. +-- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked. +-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. +-- +-- ## 1.5) Penalty score when a player changes the coalition. +-- +-- When a player changes the coalition, he can receive a penalty score. +-- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. +-- By default, the penalty for changing coalition is the default penalty multiplier. +-- +-- ## 1.8) Define output CSV files. +-- +-- The CSV file is given the name of the string given in the @{#SCORING.New}{} constructor, followed by the .csv extension. +-- The file is incrementally saved in the **\\Saved Games\\DCS\\Logs** folder, and has a time stamp indicating each mission run. +-- See the following example: +-- +-- local ScoringFirstMission = SCORING:New( "FirstMission" ) +-- local ScoringSecondMission = SCORING:New( "SecondMission" ) +-- +-- The above documents that 2 Scoring objects are created, ScoringFirstMission and ScoringSecondMission. +-- +-- ## 1.9) Configure messages. +-- +-- When players hit or destroy targets, messages are sent. +-- Various methods exist to configure: +-- +-- * Which messages are sent upon the event. +-- * Which audience receives the message. +-- +-- ### 1.9.1) Configure the messages sent upon the event. +-- +-- Use the following methods to configure when to send messages. By default, all messages are sent. +-- +-- * @{#SCORING.SetMessagesHit}(): Configure to send messages after a target has been hit. +-- * @{#SCORING.SetMessagesDestroy}(): Configure to send messages after a target has been destroyed. +-- * @{#SCORING.SetMessagesAddon}(): Configure to send messages for additional score, after a target has been destroyed. +-- * @{#SCORING.SetMessagesZone}(): Configure to send messages for additional score, after a target has been destroyed within a given zone. +-- +-- ### 1.9.2) Configure the audience of the messages. +-- +-- Use the following methods to configure the audience of the messages. By default, the messages are sent to all players in the mission. +-- +-- * @{#SCORING.SetMessagesToAll}(): Configure to send messages to all players. +-- * @{#SCORING.SetMessagesToCoalition}(): Configure to send messages to only those players within the same coalition as the player. +-- +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-02-26: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **Wingthor (TAW)**: Testing & Advice. +-- * **Dutch-Baron (TAW)**: Testing & Advice. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing and Advice. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- -- @module Scoring --- @author FlightControl --- The Scoring class @@ -54,7 +194,7 @@ local _SCORINGCategory = function SCORING:New( GameName ) -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #SCORING if GameName then self.GameName = GameName @@ -62,54 +202,310 @@ function SCORING:New( GameName ) error( "A game name must be given to register the scoring results" ) end + -- Multipliers + self.MultiplierDestroyScore = 10 + self.MultiplierDestroyPenalty = 20 + -- Additional Object scores + self.ScoringObjects = {} + + -- Additional Zone scores. + self.ScoringZones = {} + + -- Configure Messages + self:SetMessagesToAll() + self:SetMessagesHit( true ) + self:SetMessagesDestroy( true ) + self:SetMessagesScore( true ) + self:SetMessagesZone( true ) + + -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). + self:SetFratricide( self.MultiplierDestroyPenalty * 2 ) + + -- Default penalty when a player changes coalition. + self:SetCoalitionChangePenalty( self.MultiplierDestroyPenalty ) + + -- Event handlers self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Hit, self._EventOnHit ) + self:HandleEvent( EVENTS.PlayerEnterUnit ) + self:HandleEvent( EVENTS.PlayerLeaveUnit ) - --self.SchedulerId = routines.scheduleFunction( SCORING._FollowPlayersScheduled, { self }, 0, 5 ) - --self.SchedulerId = SCHEDULER:New( self, self._FollowPlayersScheduled, {}, 0, 5 ) - - self:ScoreMenu() - - self:OpenCSV( GameName) + -- Create the CSV file. + self:OpenCSV( GameName ) return self end ---- Creates a score radio menu. Can be accessed using Radio -> F10. +--- Set the multiplier for scoring valid destroys (enemy destroys). +-- A calculated score is a value between 0.1 and 10. +-- The multiplier magnifies the scores given to the players. -- @param #SCORING self --- @return #SCORING self -function SCORING:ScoreMenu() - self.Menu = MENU_MISSION:New( 'Scoring' ) - self.AllScoresMenu = MENU_MISSION_COMMAND:New( 'Score All Active Players', self.Menu, SCORING.ReportScoreAll, self ) - --- = COMMANDMENU:New('Your Current Score', ReportScore, SCORING.ReportScorePlayer, self ) +-- @param #number Multiplier The multiplier of the score given. +function SCORING:SetMultiplierDestroyScore( Multiplier ) + + self.MultiplierDestroyScore = Multiplier + return self end ---- Follows new players entering Clients within the DCSRTE. --- TODO: Need to see if i can catch this also with an event. It will eliminate the schedule ... -function SCORING:_FollowPlayersScheduled() - self:F3( "_FollowPlayersScheduled" ) +--- Set the multiplier for scoring penalty destroys (friendly destroys). +-- A calculated score is a value between 0.1 and 10. +-- The multiplier magnifies the scores given to the players. +-- @param #SCORING self +-- @param #number Multiplier The multiplier of the score given. +-- @return #SCORING +function SCORING:SetMultiplierDestroyPenalty( Multiplier ) - local ClientUnit = 0 - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } - local unitId - local unitData - local AlivePlayerUnits = {} + self.MultiplierDestroyPenalty = Multiplier + + return self +end - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - self:T3( { "_FollowPlayersScheduled", CoalitionData } ) - for UnitId, UnitData in pairs( CoalitionData ) do - self:_AddPlayerFromUnit( UnitData ) - end +--- Add a @{Unit} for additional scoring when the @{Unit} is destroyed. +-- Note that if there was already a @{Unit} declared within the scoring with the same name, +-- then the old @{Unit} will be replaced with the new @{Unit}. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddUnitScore( ScoreUnit, Score ) + + local UnitName = ScoreUnit:GetName() + + self.ScoringObjects[UnitName] = Score + + return self +end + +--- Removes a @{Unit} for additional scoring when the @{Unit} is destroyed. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. +-- @return #SCORING +function SCORING:RemoveUnitScore( ScoreUnit ) + + local UnitName = ScoreUnit:GetName() + + self.ScoringObjects[UnitName] = nil + + return self +end + +--- Add a @{Static} for additional scoring when the @{Static} is destroyed. +-- Note that if there was already a @{Static} declared within the scoring with the same name, +-- then the old @{Static} will be replaced with the new @{Static}. +-- @param #SCORING self +-- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddStaticScore( ScoreStatic, Score ) + + local StaticName = ScoreStatic:GetName() + + self.ScoringObjects[StaticName] = Score + + return self +end + +--- Removes a @{Static} for additional scoring when the @{Static} is destroyed. +-- @param #SCORING self +-- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. +-- @return #SCORING +function SCORING:RemoveStaticScore( ScoreStatic ) + + local StaticName = ScoreStatic:GetName() + + self.ScoringObjects[StaticName] = nil + + return self +end + + +--- Specify a special additional score for a @{Group}. +-- @param #SCORING self +-- @param Wrapper.Group#GROUP ScoreGroup The @{Group} for which each @{Unit} a Score is given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddScoreGroup( ScoreGroup, Score ) + + local ScoreUnits = ScoreGroup:GetUnits() + + for ScoreUnitID, ScoreUnit in pairs( ScoreUnits ) do + local UnitName = ScoreUnit:GetName() + self.ScoringObjects[UnitName] = Score end - return true + return self +end + +--- Add a @{Zone} to define additional scoring when any object is destroyed in that zone. +-- Note that if a @{Zone} with the same name is already within the scoring added, the @{Zone} (type) and Score will be replaced! +-- This allows for a dynamic destruction zone evolution within your mission. +-- @param #SCORING self +-- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. +-- Note that a zone can be a polygon or a moving zone. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddZoneScore( ScoreZone, Score ) + + local ZoneName = ScoreZone:GetName() + + self.ScoringZones[ZoneName] = {} + self.ScoringZones[ZoneName].ScoreZone = ScoreZone + self.ScoringZones[ZoneName].Score = Score + + return self +end + +--- Remove a @{Zone} for additional scoring. +-- The scoring will search if any @{Zone} is added with the given name, and will remove that zone from the scoring. +-- This allows for a dynamic destruction zone evolution within your mission. +-- @param #SCORING self +-- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. +-- Note that a zone can be a polygon or a moving zone. +-- @return #SCORING +function SCORING:RemoveZoneScore( ScoreZone ) + + local ZoneName = ScoreZone:GetName() + + self.ScoringZones[ZoneName] = nil + + return self end +--- Configure to send messages after a target has been hit. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesHit( OnOff ) + + self.MessagesHit = OnOff + return self +end + +--- If to send messages after a target has been hit. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesHit() + + return self.MessagesHit +end + +--- Configure to send messages after a target has been destroyed. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesDestroy( OnOff ) + + self.MessagesDestroy = OnOff + return self +end + +--- If to send messages after a target has been destroyed. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesDestroy() + + return self.MessagesDestroy +end + +--- Configure to send messages after a target has been destroyed and receives additional scores. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesScore( OnOff ) + + self.MessagesScore = OnOff + return self +end + +--- If to send messages after a target has been destroyed and receives additional scores. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesScore() + + return self.MessagesScore +end + +--- Configure to send messages after a target has been hit in a zone, and additional score is received. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesZone( OnOff ) + + self.MessagesZone = OnOff + return self +end + +--- If to send messages after a target has been hit in a zone, and additional score is received. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesZone() + + return self.MessagesZone +end + +--- Configure to send messages to all players. +-- @param #SCORING self +-- @return #SCORING +function SCORING:SetMessagesToAll() + + self.MessagesAudience = 1 + return self +end + +--- If to send messages to all players. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesToAll() + + return self.MessagesAudience == 1 +end + +--- Configure to send messages to only those players within the same coalition as the player. +-- @param #SCORING self +-- @return #SCORING +function SCORING:SetMessagesToCoalition() + + self.MessagesAudience = 2 + return self +end + +--- If to send messages to only those players within the same coalition as the player. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesToCoalition() + + return self.MessagesAudience == 2 +end + + +--- When a player commits too much damage to friendlies, his penalty score will reach a certain level. +-- Use this method to define the level when a player gets kicked. +-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. +-- @param #SCORING self +-- @param #number Fratricide The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked. +-- @return #SCORING +function SCORING:SetFratricide( Fratricide ) + + self.Fratricide = Fratricide + return self +end + +--- When a player changes the coalition, he can receive a penalty score. +-- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. +-- By default, the penalty for changing coalition is the default penalty multiplier. +-- @param #SCORING self +-- @param #number CoalitionChangePenalty The amount of penalty that is given. +-- @return #SCORING +function SCORING:SetCoalitionChangePenalty( CoalitionChangePenalty ) + + self.CoalitionChangePenalty = CoalitionChangePenalty + return self +end --- Add a new player entering a Unit. @@ -131,12 +527,12 @@ function SCORING:_AddPlayerFromUnit( UnitData ) 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].Destroy = {} self.Players[PlayerName].Mission = {} -- for CategoryID, CategoryName in pairs( SCORINGCategory ) do -- self.Players[PlayerName].Hit[CategoryID] = {} - -- self.Players[PlayerName].Kill[CategoryID] = {} + -- self.Players[PlayerName].Destroy[CategoryID] = {} -- end self.Players[PlayerName].HitPlayers = {} self.Players[PlayerName].Score = 0 @@ -165,7 +561,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) self.Players[PlayerName].UnitType = UnitTypeName self.Players[PlayerName].UNIT = UnitData - if self.Players[PlayerName].Penalty > 100 then + if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 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 @@ -174,7 +570,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) end end - if self.Players[PlayerName].Penalty > 150 then + if self.Players[PlayerName].Penalty > self.Fratricide then local ClientGroup = GROUP:NewFromDCSUnit( UnitData ) ClientGroup:Destroy() MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", @@ -254,6 +650,34 @@ function SCORING:_AddMissionScore( Mission, Text, Score ) end end + +--- Handles the OnPlayerEnterUnit event for the scoring. +-- @param #SCORING self +-- @param Core.Event#EVENTDATA Event +function SCORING:OnEventPlayerEnterUnit( Event ) + if Event.IniUnit then + self:_AddPlayerFromUnit( Event.IniUnit ) + local Menu = MENU_GROUP:New( Event.IniGroup, 'Scoring' ) + local ReportGroupSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report players in group', Menu, SCORING.ReportScoreGroupSummary, self, Event.IniGroup ) + local ReportGroupDetailed = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Detailed report players in group', Menu, SCORING.ReportScoreGroupDetailed, self, Event.IniGroup ) + local ReportToAllSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report all players', Menu, SCORING.ReportScoreAllSummary, self, Event.IniGroup ) + self:SetState( Event.IniUnit, "ScoringMenu", Menu ) + end +end + +--- Handles the OnPlayerLeaveUnit event for the scoring. +-- @param #SCORING self +-- @param Core.Event#EVENTDATA Event +function SCORING:OnEventPlayerLeaveUnit( Event ) + if Event.IniUnit then + local Menu = self:GetState( Event.IniUnit, "ScoringMenu" ) -- Core.Menu#MENU_GROUP + if Menu then + Menu:Remove() + end + end +end + + --- Handles the OnHit event for the scoring. -- @param #SCORING self -- @param Core.Event#EVENTDATA Event @@ -297,11 +721,12 @@ function SCORING:_EventOnHit( Event ) InitGroupName = Event.IniDCSGroupName InitPlayerName = Event.IniPlayerName - InitCoalition = InitUnit:getCoalition() + InitCoalition = Event.IniCoalition --TODO: Workaround Client DCS Bug --InitCategory = InitUnit:getCategory() - InitCategory = InitUnit:getDesc().category - InitType = InitUnit:getTypeName() + --InitCategory = InitUnit:getDesc().category + InitCategory = Event.IniCategory + InitType = Event.IniTypeName InitUnitCoalition = _SCORINGCoalition[InitCoalition] InitUnitCategory = _SCORINGCategory[InitCategory] @@ -320,11 +745,12 @@ function SCORING:_EventOnHit( Event ) TargetGroupName = Event.TgtDCSGroupName TargetPlayerName = Event.TgtPlayerName - TargetCoalition = TargetUnit:getCoalition() + TargetCoalition = Event.TgtCoalition --TODO: Workaround Client DCS Bug --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnit:getDesc().category - TargetType = TargetUnit:getTypeName() + --TargetCategory = TargetUnit:getDesc().category + TargetCategory = Event.TgtCategory + TargetType = Event.TgtTypeName TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] TargetUnitCategory = _SCORINGCategory[TargetCategory] @@ -374,43 +800,66 @@ function SCORING:_EventOnHit( Event ) end local Score = 0 - if InitCoalition == TargetCoalition then - Player.Penalty = Player.Penalty + 10 - PlayerHit.Penalty = PlayerHit.Penalty + 10 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 - - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.PenaltyHit .. " times. Penalty: -" .. PlayerHit.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() + + if InitCoalition then -- A coalition object was hit. + if InitCoalition == TargetCoalition then + Player.Penalty = Player.Penalty + 10 + PlayerHit.Penalty = PlayerHit.Penalty + 10 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 + + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. + "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + else + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit a friendly target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. + "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + end + self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.PenaltyHit .. " times. Penalty: -" .. PlayerHit.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() + Player.Score = Player.Score + 1 + PlayerHit.Score = PlayerHit.Score + 1 + PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. + "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + else + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit an enemy target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. + "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + end + self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) end - self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - Player.Score = Player.Score + 1 - PlayerHit.Score = PlayerHit.Score + 1 - PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.ScoreHit .. " times. Score: " .. PlayerHit.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() - else - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.ScoreHit .. " times. Score: " .. PlayerHit.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() - end - self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else -- A scenery object was hit. + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit a scenery object.", + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end end end @@ -446,10 +895,11 @@ function SCORING:_EventOnDeadOrCrash( Event ) TargetGroupName = Event.IniDCSGroupName TargetPlayerName = Event.IniPlayerName - TargetCoalition = TargetUnit:getCoalition() + TargetCoalition = Event.IniCoalition --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnit:getDesc().category -- Workaround - TargetType = TargetUnit:getTypeName() + --TargetCategory = TargetUnit:getDesc().category -- Workaround + TargetCategory = Event.IniCategory + TargetType = Event.IniTypeName TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] TargetUnitCategory = _SCORINGCategory[TargetCategory] @@ -461,7 +911,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) -- Player contains the score and reference data for the player. for PlayerName, Player in pairs( self.Players ) do if Player then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got killed" ) + self:T( "Something got destroyed" ) -- Some variables local InitUnitName = Player.UnitName @@ -473,67 +923,136 @@ function SCORING:_EventOnDeadOrCrash( Event ) self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - -- What is he hitting? - if TargetCategory then - if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? + -- What is the player destroying? + if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? - - Player.Kill[TargetCategory] = Player.Kill[TargetCategory] or {} - Player.Kill[TargetCategory][TargetType] = Player.Kill[TargetCategory][TargetType] or {} + Player.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} + Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} - -- PlayerKill contains the kill score data per category and target type of the player. - local PlayerKill = Player.Kill[TargetCategory][TargetType] - Player.Kill[TargetCategory][TargetType] = {} - PlayerKill.Score = PlayerKill.Score or 0 - PlayerKill.ScoreKill = PlayerKill.ScoreKill or 0 - PlayerKill.Penalty = PlayerKill.Penalty or 0 - PlayerKill.PenaltyKill = PlayerKill.PenaltyKill or 0 - PlayerKill.UNIT = PlayerKill.UNIT or Player.Hit[TargetCategory][TargetUnitName].UNIT - + -- PlayerDestroy contains the destroy score data per category and target type of the player. + local TargetDestroy = Player.Destroy[TargetCategory][TargetType] + TargetDestroy.Score = TargetDestroy.Score or 0 + TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy or 0 + TargetDestroy.Penalty = TargetDestroy.Penalty or 0 + TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy or 0 + TargetDestroy.UNIT = TargetDestroy.UNIT or Player.Hit[TargetCategory][TargetUnitName].UNIT + + if TargetCoalition then if InitCoalition == TargetCoalition then - local ThreatLevelTarget, ThreatTypeTarget = PlayerKill.UNIT:GetThreatLevel() + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() - local ThreatLevel = math.ceil( ThreatLevelTarget / ThreatLevelPlayer * 100 ) - self:E( { ThreatLevel = ThreatLevel, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) + local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.MultiplierDestroyPenalty ) + self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - Player.Penalty = Player.Penalty + ThreatLevel * 4 - PlayerKill.Penalty = PlayerKill.Penalty + ThreatLevel * 4 - PlayerKill.PenaltyKill = PlayerKill.PenaltyKill + 1 + Player.Penalty = Player.Penalty + ThreatPenalty + TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty + TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 - if Player.HitPlayers[TargetPlayerName] then -- A player killed another player - MESSAGE:New( "Player '" .. PlayerName .. "' killed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.PenaltyKill .. " times. Penalty: -" .. PlayerKill.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. + "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) else - MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.PenaltyKill .. " times. Penalty: -" .. PlayerKill.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed a friendly target " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. + "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end - self:ScoreCSV( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + self:ScoreCSV( PlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - - local ThreatLevelTarget, ThreatTypeTarget = PlayerKill.UNIT:GetThreatLevel() + + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() - local ThreatLevel = math.ceil( ThreatLevelTarget / ThreatLevelPlayer * 100 ) - self:E( { ThreatLevel = ThreatLevel, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - - Player.Score = Player.Score + ThreatLevel - PlayerKill.Score = PlayerKill.Score + ThreatLevel - PlayerKill.ScoreKill = PlayerKill.ScoreKill + 1 - if Player.HitPlayers[TargetPlayerName] then -- A player killed another player - MESSAGE:New( "Player '" .. PlayerName .. "' killed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.ScoreKill .. " times. Score: " .. PlayerKill.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.MultiplierDestroyScore ) + self:E( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) + + Player.Score = Player.Score + ThreatScore + TargetDestroy.Score = TargetDestroy.Score + ThreatScore + TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1 + if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. + "Score: " .. TargetDestroy.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) else - MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.ScoreKill .. " times. Score: " .. PlayerKill.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed an enemy " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. + "Score: " .. TargetDestroy.Score .. ". Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) + end + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + + local UnitName = TargetDestroy.UNIT:GetName() + local Score = self.ScoringObjects[UnitName] + if Score then + Player.Score = Player.Score + Score + TargetDestroy.Score = TargetDestroy.Score + Score + MESSAGE + :New( "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " .. + "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + + -- Check if there are Zones where the destruction happened. + for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do + self:E( { ScoringZone = ScoreZoneData } ) + local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE + local Score = ScoreZoneData.Score + if ScoreZone:IsVec2InZone( TargetDestroy.UNIT:GetVec2() ) then + Player.Score = Player.Score + Score + TargetDestroy.Score = TargetDestroy.Score + Score + MESSAGE + :New( "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." .. + "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. + "Total: " .. Player.Score - Player.Penalty, + 15 ) + :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + end + + end + else + -- Check if there are Zones where the destruction happened. + for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do + self:E( { ScoringZone = ScoreZoneData } ) + local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE + local Score = ScoreZoneData.Score + if ScoreZone:IsVec2InZone( TargetDestroy.UNIT:GetVec2() ) then + Player.Score = Player.Score + Score + TargetDestroy.Score = TargetDestroy.Score + Score + MESSAGE + :New( "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." .. + "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. + "Total: " .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end - self:ScoreCSV( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) end end end @@ -542,229 +1061,324 @@ function SCORING:_EventOnDeadOrCrash( Event ) end -function SCORING:ReportScoreAll() - - env.info( "Hello World " ) +--- Produce detailed report of player hit scores. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerHits( PlayerName ) local ScoreMessage = "" - local PlayerMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 - self:T( "Score Report" ) + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - 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 ) + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = ":\n" - - local ScoreMessageHits = "" - - for CategoryID, CategoryName in pairs( _SCORINGCategory ) 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 ) + local ScoreMessageHits = "" + for CategoryID, CategoryName in pairs( _SCORINGCategory ) do + self:T( CategoryName ) + if PlayerData.Hit[CategoryID] then + self:T( "Hit scores exist for player " .. PlayerName ) + local Score = 0 + local ScoreHit = 0 + local Penalty = 0 + local PenaltyHit = 0 + 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 - if ScoreMessageHits ~= "" then - ScoreMessage = ScoreMessage .. " Hits: " .. ScoreMessageHits .. "\n" - end - - local ScoreMessageKills = "" - for CategoryID, CategoryName in pairs( _SCORINGCategory ) 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 + if ScoreMessageHits ~= "" then + ScoreMessage = "Hits: " .. ScoreMessageHits end end - MESSAGE:New( PlayerMessage, 30, "Player Scores" ):ToAll() + + return ScoreMessage, PlayerScore, PlayerPenalty end -function SCORING:ReportScorePlayer() - - env.info( "Hello World " ) +--- Produce detailed report of player destroy scores. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerDestroys( PlayerName ) local ScoreMessage = "" - local PlayerMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 - self:T( "Score Report" ) + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - 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 ) + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName + local ScoreMessageDestroys = "" + for CategoryID, CategoryName in pairs( _SCORINGCategory ) do + if PlayerData.Destroy[CategoryID] then + self:T( "Destroy scores exist for player " .. PlayerName ) + local Score = 0 + local ScoreDestroy = 0 + local Penalty = 0 + local PenaltyDestroy = 0 - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = "" - - local ScoreMessageHits = "" - - for CategoryID, CategoryName in pairs( _SCORINGCategory ) 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 + for UnitName, UnitData in pairs( PlayerData.Destroy[CategoryID] ) do + self:E( { UnitData = UnitData } ) + if UnitData ~= {} then Score = Score + UnitData.Score - ScoreHit = ScoreHit + UnitData.ScoreHit + ScoreDestroy = ScoreDestroy + UnitData.ScoreDestroy Penalty = Penalty + UnitData.Penalty - PenaltyHit = UnitData.PenaltyHit + PenaltyDestroy = PenaltyDestroy + UnitData.PenaltyDestroy 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( _SCORINGCategory ) 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 + local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageDestroy ) + ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy - 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 + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) 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 + if ScoreMessageDestroys ~= "" then + ScoreMessage = "Destroys: " .. ScoreMessageDestroys + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + +--- Produce detailed report of player penalty scores because of changing the coalition. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerCoalitionChanges( PlayerName ) + + local ScoreMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 + + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) + + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName + + 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 + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + +--- Produce detailed report of player penalty scores because of changing the coalition. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerMissions( PlayerName ) + + local ScoreMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 + + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) + + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName + + 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 + + if ScoreMessageMission ~= "" then + ScoreMessage = ScoreMessage .. "Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")" + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + + +--- Report Group Score Summary +-- @param #SCORING self +-- @param Wrapper.Group#GROUP PlayerGroup The player group. +function SCORING:ReportScoreGroupSummary( PlayerGroup ) + + local PlayerMessage = "" + + self:T( "Report Score Group Summary" ) + + local PlayerUnits = PlayerGroup:GetUnits() + for UnitID, PlayerUnit in pairs( PlayerUnits ) do + local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + + if PlayerName then + + local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) + ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits + self:E( { ReportHits, ScoreHits, PenaltyHits } ) + local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) + ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys + self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) + local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) + ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges + self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty + ) + MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) + end + end + +end + +--- Report Group Score Detailed +-- @param #SCORING self +-- @param Wrapper.Group#GROUP PlayerGroup The player group. +function SCORING:ReportScoreGroupDetailed( PlayerGroup ) + + local PlayerMessage = "" + + self:T( "Report Score Group Detailed" ) + + local PlayerUnits = PlayerGroup:GetUnits() + for UnitID, PlayerUnit in pairs( PlayerUnits ) do + local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + + if PlayerName then + + local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) + ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits + self:E( { ReportHits, ScoreHits, PenaltyHits } ) + local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) + ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys + self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) + local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) + ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges + self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty, + ReportHits, + ReportDestroys, + ReportCoalitionChanges, + ReportMissions + ) + MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) + end + end + +end + +--- Report all players score +-- @param #SCORING self +-- @param Wrapper.Group#GROUP PlayerGroup The player group. +function SCORING:ReportScoreAllSummary( PlayerGroup ) + + local PlayerMessage = "" + + self:T( "Report Score All Players" ) + + for PlayerName, PlayerData in pairs( self.Players ) do + + if PlayerName then + + local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) + ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits + self:E( { ReportHits, ScoreHits, PenaltyHits } ) + local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) + ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys + self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) + local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) + ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges + self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty + ) + MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) end end - MESSAGE:New( PlayerMessage, 30, "Player Scores" ):ToAll() end @@ -865,21 +1479,10 @@ function SCORING:ScoreCSV( PlayerName, ScoreType, ScoreTimes, ScoreAmount, Playe 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 + TargetUnitCoalition = TargetUnitCoalition or "" + TargetUnitCategory = TargetUnitCategory or "" + TargetUnitType = TargetUnitType or "" + TargetUnitName = TargetUnitName or "" if lfs and io and os then self.CSVFile:write( diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index ed13ffe2e..3d1c4339e 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -27,6 +27,7 @@ Include.File( "Wrapper/Unit" ) Include.File( "Wrapper/Client" ) Include.File( "Wrapper/Static" ) Include.File( "Wrapper/Airbase" ) +Include.File( "Wrapper/Scenery" ) --- Functional Classes Include.File( "Functional/Scoring" ) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 4a2a4d3f6..998f7ee3e 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -455,8 +455,7 @@ function GROUP:IsCompletelyInZone( Zone ) for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT - -- TODO: Rename IsPointVec3InZone to IsVec3InZone - if Zone:IsPointVec3InZone( Unit:GetVec3() ) then + if Zone:IsVec3InZone( Unit:GetVec3() ) then else return false end @@ -474,7 +473,7 @@ function GROUP:IsPartlyInZone( Zone ) for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsPointVec3InZone( Unit:GetVec3() ) then + if Zone:IsVec3InZone( Unit:GetVec3() ) then return true end end @@ -491,7 +490,7 @@ function GROUP:IsNotInZone( Zone ) for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsPointVec3InZone( Unit:GetVec3() ) then + if Zone:IsVec3InZone( Unit:GetVec3() ) then return false end end diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index abfa0c607..edcd3ffff 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -217,10 +217,7 @@ function IDENTIFIABLE:GetCallsign() end +function IDENTIFIABLE:GetThreatLevel() - - - - - - + return 0, "Scenery" +end diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua new file mode 100644 index 000000000..c697d250e --- /dev/null +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -0,0 +1,39 @@ +--- This module contains the SCENERY class. +-- +-- 1) @{Scenery#SCENERY} class, extends @{Positionable#POSITIONABLE} +-- =============================================================== +-- Scenery objects are defined on the map. +-- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: +-- +-- * Wraps the DCS Scenery objects. +-- * Support all DCS Scenery APIs. +-- * Enhance with Scenery specific APIs not in the DCS API set. +-- +-- @module Scenery +-- @author FlightControl + + + +--- The SCENERY class +-- @type SCENERY +-- @extends Wrapper.Positionable#POSITIONABLE +SCENERY = { + ClassName = "SCENERY", +} + + +function SCENERY:Register( SceneryName, SceneryObject ) + local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) + self.SceneryName = SceneryName + self.SceneryObject = SceneryObject + return self +end + +function SCENERY:GetDCSObject() + return self.SceneryObject +end + +function SCENERY:GetThreatLevel() + + return 0, "Scenery" +end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index dd7a775e8..ad0fc58be 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -673,7 +673,7 @@ function UNIT:IsInZone( Zone ) self:F2( { self.UnitName, Zone } ) if self:IsAlive() then - local IsInZone = Zone:IsPointVec3InZone( self:GetVec3() ) + local IsInZone = Zone:IsVec3InZone( self:GetVec3() ) self:T( { IsInZone } ) return IsInZone @@ -690,7 +690,7 @@ function UNIT:IsNotInZone( Zone ) self:F2( { self.UnitName, Zone } ) if self:IsAlive() then - local IsInZone = not Zone:IsPointVec3InZone( self:GetVec3() ) + local IsInZone = not Zone:IsVec3InZone( self:GetVec3() ) self:T( { IsInZone } ) return IsInZone diff --git a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua index 8b4e8253d..2a2b9f013 100644 --- a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua +++ b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE STATIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170226_1531' ) +env.info( 'Moose Generation Timestamp: 20170228_1815' ) local base = _G Include = {} @@ -4148,14 +4148,14 @@ end -- **IMPORTANT NOTE:** Some events can involve not just UNIT objects, but also STATIC objects!!! -- In that case the initiator or target unit fields will refer to a STATIC object! -- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated. --- The fields **IniCategory** and **TgtCategory** contain the indicator which **kind of object is involved** in the event. --- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniCategory and TgtCategory. +-- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event. +-- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory. -- Example code snippet: -- --- if Event.IniCategory == Object.Category.UNIT then +-- if Event.IniObjectCategory == Object.Category.UNIT then -- ... -- end --- if Event.IniCategory == Object.Category.STATIC then +-- if Event.IniObjectCategory == Object.Category.STATIC then -- ... -- end -- @@ -4190,7 +4190,7 @@ end -- @module Event -- TODO: Need to update the EVENTDATA documentation with IniPlayerName and TgtPlayerName --- TODO: Need to update the EVENTDATA documentation with IniCategory and TgtCategory +-- TODO: Need to update the EVENTDATA documentation with IniObjectCategory and TgtObjectCategory @@ -4241,8 +4241,8 @@ EVENTS = { -- @type EVENTDATA -- @field #number id The identifier of the event. -- --- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category IniCategory (UNIT/STATIC) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). +-- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. +-- @field Dcs.DCSObject#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field Dcs.DCSUnit#Unit IniDCSUnit (UNIT/STATIC) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. -- @field #string IniDCSUnitName (UNIT/STATIC) The initiating Unit name. -- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Wrapper.Unit#UNIT} of the initiator Unit object. @@ -4252,9 +4252,12 @@ EVENTS = { -- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Wrapper.Group#GROUP} of the initiator Group object. -- @field #string IniGroupName (UNIT) The initiating GROUP name (same as IniDCSGroupName). -- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot. +-- @field Dcs.DCScoalition#coalition.side IniCoalition (UNIT) The coalition of the initiator. +-- @field Dcs.DCSUnit#Unit.Category IniCategory (UNIT) The category of the initiator. +-- @field #string IniTypeName (UNIT) The type name of the initiator. -- -- @field Dcs.DCSUnit#Unit target (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category TgtCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). +-- @field Dcs.DCSObject#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field Dcs.DCSUnit#Unit TgtDCSUnit (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. -- @field #string TgtDCSUnitName (UNIT/STATIC) The target Unit name. -- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Wrapper.Unit#UNIT} of the target Unit object. @@ -4264,6 +4267,9 @@ EVENTS = { -- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Wrapper.Group#GROUP} of the target Group object. -- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName). -- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot. +-- @field Dcs.DCScoalition#coalition.side TgtCoalition (UNIT) The coalition of the target. +-- @field Dcs.DCSUnit#Unit.Category TgtCategory (UNIT) The category of the target. +-- @field #string TgtTypeName (UNIT) The type name of the target. -- -- @field weapon The weapon used during the event. -- @field Weapon @@ -5071,8 +5077,10 @@ function EVENT:onEvent( Event ) if Event.initiator then - Event.IniCategory = Event.initiator:getCategory() - if Event.IniCategory == Object.Category.UNIT then + + Event.IniObjectCategory = Event.initiator:getCategory() + + if Event.IniObjectCategory == Object.Category.UNIT then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniUnitName = Event.IniDCSUnitName @@ -5089,19 +5097,36 @@ function EVENT:onEvent( Event ) self:E( { IniGroup = Event.IniGroup } ) end Event.IniPlayerName = Event.IniDCSUnit:getPlayerName() + Event.IniCoalition = Event.IniDCSUnit:getCoalition() + Event.IniTypeName = Event.IniDCSUnit:getTypeName() + Event.IniCategory = Event.IniDCSUnit:getDesc().category end - if Event.IniCategory == Object.Category.STATIC then + if Event.IniObjectCategory == Object.Category.STATIC then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniUnitName = Event.IniDCSUnitName Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName ) + Event.IniCoalition = Event.IniDCSUnit:getCoalition() + Event.IniCategory = Event.IniDCSUnit:getDesc().category + Event.IniTypeName = Event.IniDCSUnit:getTypeName() + end + + if Event.IniObjectCategory == Object.Category.SCENERY then + Event.IniDCSUnit = Event.initiator + Event.IniDCSUnitName = Event.IniDCSUnit:getName() + Event.IniUnitName = Event.IniDCSUnitName + Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator ) + Event.IniCategory = Event.IniDCSUnit:getDesc().category + Event.IniTypeName = Event.IniDCSUnit:getTypeName() end end if Event.target then - Event.TgtCategory = Event.target:getCategory() - if Event.TgtCategory == Object.Category.UNIT then + + Event.TgtObjectCategory = Event.target:getCategory() + + if Event.TgtObjectCategory == Object.Category.UNIT then Event.TgtDCSUnit = Event.target Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup() Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() @@ -5112,18 +5137,31 @@ function EVENT:onEvent( Event ) Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() end Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() + Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() end - if Event.TgtCategory == Object.Category.STATIC then + if Event.TgtObjectCategory == Object.Category.STATIC then Event.TgtDCSUnit = Event.target Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() Event.TgtUnitName = Event.TgtDCSUnitName Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName ) + Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() + end + + if Event.TgtObjectCategory == Object.Category.SCENERY then + Event.TgtDCSUnit = Event.target + Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() + Event.TgtUnitName = Event.TgtDCSUnitName + Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target ) + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() end end - - if Event.weapon then Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() @@ -6165,8 +6203,8 @@ end -- -- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: -- --- * @{#ZONE_BASE.IsPointVec2InZone}(): Returns if a @{Point#POINT_VEC2} is within the zone. --- * @{#ZONE_BASE.IsPointVec3InZone}(): Returns if a @{Point#POINT_VEC3} is within the zone. +-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. +-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. -- -- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: -- @@ -6275,21 +6313,28 @@ end -- -- Hereby the change log: -- --- 2017-02-18: ZONE_POLYGON_BASE:**GetRandomPointVec2()** added. +-- 2017-02-28: ZONE\_BASE:**IsVec2InZone()** replaces ZONE\_BASE:_IsPointVec2InZone()_. +-- 2017-02-28: ZONE\_BASE:**IsVec3InZone()** replaces ZONE\_BASE:_IsPointVec3InZone()_. +-- 2017-02-28: ZONE\_RADIUS:**IsVec2InZone()** replaces ZONE\_RADIUS:_IsPointVec2InZone()_. +-- 2017-02-28: ZONE\_RADIUS:**IsVec3InZone()** replaces ZONE\_RADIUS:_IsPointVec3InZone()_. +-- 2017-02-28: ZONE\_POLYGON:**IsVec2InZone()** replaces ZONE\_POLYGON:_IsPointVec2InZone()_. +-- 2017-02-28: ZONE\_POLYGON:**IsVec3InZone()** replaces ZONE\_POLYGON:_IsPointVec3InZone()_. -- --- 2017-02-18: ZONE_POLYGON_BASE:**GetRandomPointVec3()** added. +-- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec2()** added. -- --- 2017-02-18: ZONE_RADIUS:**GetRandomPointVec3( inner, outer )** added. +-- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec3()** added. -- --- 2017-02-18: ZONE_RADIUS:**GetRandomPointVec2( inner, outer )** added. +-- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec3( inner, outer )** added. -- --- 2016-08-15: ZONE_BASE:**GetName()** added. +-- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec2( inner, outer )** added. -- --- 2016-08-15: ZONE_BASE:**SetZoneProbability( ZoneProbability )** added. +-- 2016-08-15: ZONE\_BASE:**GetName()** added. -- --- 2016-08-15: ZONE_BASE:**GetZoneProbability()** added. +-- 2016-08-15: ZONE\_BASE:**SetZoneProbability( ZoneProbability )** added. -- --- 2016-08-15: ZONE_BASE:**GetZoneMaybe()** added. +-- 2016-08-15: ZONE\_BASE:**GetZoneProbability()** added. +-- +-- 2016-08-15: ZONE\_BASE:**GetZoneMaybe()** added. -- -- === -- @@ -6342,7 +6387,7 @@ end -- @param #ZONE_BASE self -- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_BASE:IsPointVec2InZone( Vec2 ) +function ZONE_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) return false @@ -6352,10 +6397,10 @@ end -- @param #ZONE_BASE self -- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. -- @return #boolean true if the point is within the zone. -function ZONE_BASE:IsPointVec3InZone( Vec3 ) +function ZONE_BASE:IsVec3InZone( Vec3 ) self:F2( Vec3 ) - local InZone = self:IsPointVec2InZone( { x = Vec3.x, y = Vec3.z } ) + local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) return InZone end @@ -6585,7 +6630,7 @@ end -- @param #ZONE_RADIUS self -- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_RADIUS:IsPointVec2InZone( Vec2 ) +function ZONE_RADIUS:IsVec2InZone( Vec2 ) self:F2( Vec2 ) local ZoneVec2 = self:GetVec2() @@ -6603,10 +6648,10 @@ end -- @param #ZONE_RADIUS self -- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. -- @return #boolean true if the point is within the zone. -function ZONE_RADIUS:IsPointVec3InZone( Vec3 ) +function ZONE_RADIUS:IsVec3InZone( Vec3 ) self:F2( Vec3 ) - local InZone = self:IsPointVec2InZone( { x = Vec3.x, y = Vec3.z } ) + local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) return InZone end @@ -6925,7 +6970,7 @@ end -- @param #ZONE_POLYGON_BASE self -- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_POLYGON_BASE:IsPointVec2InZone( Vec2 ) +function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) local Next @@ -6967,7 +7012,7 @@ function ZONE_POLYGON_BASE:GetRandomVec2() while Vec2Found == false do Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } self:T2( Vec2 ) - if self:IsPointVec2InZone( Vec2 ) then + if self:IsVec2InZone( Vec2 ) then Vec2Found = true end end @@ -11040,6 +11085,20 @@ function MESSAGE:ToCoalition( CoalitionSide ) return self end +--- Sends a MESSAGE to a Coalition if the given Condition is true. +-- @param #MESSAGE self +-- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. +-- @return #MESSAGE +function MESSAGE:ToCoalitionIf( CoalitionSide, Condition ) + self:F( CoalitionSide ) + + if Condition and Condition == true then + self:ToCoalition( CoalitionSide ) + end + + return self +end + --- Sends a MESSAGE to all players. -- @param #MESSAGE self -- @return #MESSAGE @@ -11052,10 +11111,24 @@ end -- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ) -- MessageAll:ToAll() function MESSAGE:ToAll() - self:F() + self:F() - self:ToCoalition( coalition.side.RED ) - self:ToCoalition( coalition.side.BLUE ) + self:ToCoalition( coalition.side.RED ) + self:ToCoalition( coalition.side.BLUE ) + + return self +end + + +--- Sends a MESSAGE to all players if the given Condition is true. +-- @param #MESSAGE self +-- @return #MESSAGE +function MESSAGE:ToAllIf( Condition ) + + if Condition and Condition == true then + self:ToCoalition( coalition.side.RED ) + self:ToCoalition( coalition.side.BLUE ) + end return self end @@ -12495,13 +12568,10 @@ function IDENTIFIABLE:GetCallsign() end +function IDENTIFIABLE:GetThreatLevel() - - - - - - + return 0, "Scenery" +end --- This module contains the POSITIONABLE class. -- -- 1) @{Positionable#POSITIONABLE} class, extends @{Identifiable#IDENTIFIABLE} @@ -15689,8 +15759,7 @@ function GROUP:IsCompletelyInZone( Zone ) for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT - -- TODO: Rename IsPointVec3InZone to IsVec3InZone - if Zone:IsPointVec3InZone( Unit:GetVec3() ) then + if Zone:IsVec3InZone( Unit:GetVec3() ) then else return false end @@ -15708,7 +15777,7 @@ function GROUP:IsPartlyInZone( Zone ) for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsPointVec3InZone( Unit:GetVec3() ) then + if Zone:IsVec3InZone( Unit:GetVec3() ) then return true end end @@ -15725,7 +15794,7 @@ function GROUP:IsNotInZone( Zone ) for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsPointVec3InZone( Unit:GetVec3() ) then + if Zone:IsVec3InZone( Unit:GetVec3() ) then return false end end @@ -16808,7 +16877,7 @@ function UNIT:IsInZone( Zone ) self:F2( { self.UnitName, Zone } ) if self:IsAlive() then - local IsInZone = Zone:IsPointVec3InZone( self:GetVec3() ) + local IsInZone = Zone:IsVec3InZone( self:GetVec3() ) self:T( { IsInZone } ) return IsInZone @@ -16825,7 +16894,7 @@ function UNIT:IsNotInZone( Zone ) self:F2( { self.UnitName, Zone } ) if self:IsAlive() then - local IsInZone = not Zone:IsPointVec3InZone( self:GetVec3() ) + local IsInZone = not Zone:IsVec3InZone( self:GetVec3() ) self:T( { IsInZone } ) return IsInZone @@ -17749,10 +17818,49 @@ end +--- This module contains the SCENERY class. +-- +-- 1) @{Scenery#SCENERY} class, extends @{Positionable#POSITIONABLE} +-- =============================================================== +-- Scenery objects are defined on the map. +-- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: +-- +-- * Wraps the DCS Scenery objects. +-- * Support all DCS Scenery APIs. +-- * Enhance with Scenery specific APIs not in the DCS API set. +-- +-- @module Scenery +-- @author FlightControl + + + +--- The SCENERY class +-- @type SCENERY +-- @extends Wrapper.Positionable#POSITIONABLE +SCENERY = { + ClassName = "SCENERY", +} + + +function SCENERY:Register( SceneryName, SceneryObject ) + local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) + self.SceneryName = SceneryName + self.SceneryObject = SceneryObject + return self +end + +function SCENERY:GetDCSObject() + return self.SceneryObject +end + +function SCENERY:GetThreatLevel() + + return 0, "Scenery" +end --- Single-Player:**Yes** / Multi-Player:**Yes** / Core:**Yes** -- **Administer the scoring of player achievements, -- and create a CSV file logging the scoring events for use at team or squadron websites.** -- --- -- ![Banner Image](..\Presentations\AI_Balancer\Dia1.JPG) +-- ![Banner Image](..\Presentations\SCORING\Dia1.JPG) -- -- === -- @@ -17760,14 +17868,154 @@ end -- -- The @{#SCORING} class administers the scoring of player achievements, -- and creates a CSV file logging the scoring events for use at team or squadron websites. --- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions. -- --- This scoring class calculates the hits and kills that players make within a simulation session. --- Scoring is calculated using a defined algorithm. --- With a small change in MissionScripting.lua, the scoring can also be logged in a CSV file, that can then be uploaded --- to a database or a BI tool to publish the scoring results to the player community. +-- The scores are calculated by scoring the hits and destroys of objects that players make, +-- which are @{Unit} and @{Static) objects within your mission. +-- +-- Scores are calculated based on the threat level of the objects involved. +-- The threat level of a unit can be a value between 0 and 10. +-- A calculated score takes the threat level of the target divided by the threat level of the player unit. +-- This provides a value between 0.1 and 10. +-- The stronger or the higher the threat of the player unit, the less score will be given in destroys. +-- That value can then be multiplied by a multiplier. A specific multiplier can be set for enemies and friendlies destroys. +-- +-- If multiple players hit the same target, and finally the target gets destroyed, each player who contributed to the target +-- destruction, will receive a score. This is important for targets that require significant damage before it can be destroyed, like +-- ships or heavy planes. +-- +-- **Additional scores** can be granted to **specific objects**, when the player(s) destroy these objects. +-- **Various @{Zone}s** can be defined for which scores are also granted when objects in that @{Zone} are destroyed. +-- This is **specifically useful** to designate **scenery targets on the map** that will generate points when destroyed. +-- +-- With a small change in MissionScripting.lua, the scoring can also be logged in a CSV file. +-- The CSV files can be used to: +-- +-- * Upload scoring to a database or a BI tool to publish the scoring results to the player community. +-- * Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results. +-- * Share scoring amoung players after the mission to discuss mission results. +-- +-- Scores can be reported. Menu options are automatically added to each group when a player joins a client slot or a CA unit. +-- Use the radio menu F10 to consult the scores while running the mission. +-- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. +-- +-- ## 1.1) Set the destroy score or penalty multiplier +-- +-- Score multipliers can be set for scores granted when enemies or friendlies are destroyed. +-- Use the method @{#SCORING.SetMultiplierDestroyScore}() to set the multiplier of enemy destroys (positive destroys). +-- Use the method @{#SCORING.SetMultiplierDestroyPenalty}() to set the multiplier of friendly destroys (negative destroys). +-- +-- local Scoring = SCORING:New( "Scoring File" ) +-- Scoring:SetMultiplierDestroyScore( 10 ) +-- Scoring:SetMultiplierDestroyPenalty( 40 ) +-- +-- The above sets the multiplier for valid scores to 10. So scores will be given in a scale from 0 to 10. +-- The penalties will be given in a scale from 0 to 40. +-- +-- ## 1.2) Define special targets that will give extra scores. +-- +-- Special targets can be set that will give extra scores to the players when these are destroyed. +-- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Unit}s. +-- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Static}s. +-- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Group}s. +-- +-- local Scoring = SCORING:New( "Scoring File" ) +-- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) +-- Scoring:AddStaticScore( STATIC:FindByName( "Static #1" ), 100 ) +-- +-- The above grants an additional score of 200 points for Unit #001 and an additional 100 points of Static #1 if these are destroyed. +-- Note that later in the mission, one can remove these scores set, for example, when the a goal achievement time limit is over. +-- For example, this can be done as follows: +-- +-- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) ) +-- +-- +-- +-- ## 1.3) Define destruction zones that will give extra scores. +-- +-- Define zones of destruction. Any object destroyed within the zone of the given category will give extra points. +-- Use the method @{#SCORING.AddZoneScore}() to add a @{Zone} for additional scoring. +-- Use the method @{#SCORING.RemoveZoneScore}() to remove a @{Zone} for additional scoring. +-- There are interesting variations that can be achieved with this functionality. For example, if the @{Zone} is a @{Zone#ZONE_UNIT}, +-- then the zone is a moving zone, and anything destroyed within that @{Zone} will generate points. +-- The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a @{Zone}, +-- just large enough around that building. +-- +-- ## 1.4) Configure fratricide level. +-- +-- When a player commits too much damage to friendlies, his penalty score will reach a certain level. +-- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked. +-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. +-- +-- ## 1.5) Penalty score when a player changes the coalition. +-- +-- When a player changes the coalition, he can receive a penalty score. +-- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. +-- By default, the penalty for changing coalition is the default penalty multiplier. +-- +-- ## 1.8) Define output CSV files. +-- +-- The CSV file is given the name of the string given in the @{#SCORING.New}{} constructor, followed by the .csv extension. +-- The file is incrementally saved in the **\\Saved Games\\DCS\\Logs** folder, and has a time stamp indicating each mission run. +-- See the following example: +-- +-- local ScoringFirstMission = SCORING:New( "FirstMission" ) +-- local ScoringSecondMission = SCORING:New( "SecondMission" ) +-- +-- The above documents that 2 Scoring objects are created, ScoringFirstMission and ScoringSecondMission. +-- +-- ## 1.9) Configure messages. +-- +-- When players hit or destroy targets, messages are sent. +-- Various methods exist to configure: +-- +-- * Which messages are sent upon the event. +-- * Which audience receives the message. +-- +-- ### 1.9.1) Configure the messages sent upon the event. +-- +-- Use the following methods to configure when to send messages. By default, all messages are sent. +-- +-- * @{#SCORING.SetMessagesHit}(): Configure to send messages after a target has been hit. +-- * @{#SCORING.SetMessagesDestroy}(): Configure to send messages after a target has been destroyed. +-- * @{#SCORING.SetMessagesAddon}(): Configure to send messages for additional score, after a target has been destroyed. +-- * @{#SCORING.SetMessagesZone}(): Configure to send messages for additional score, after a target has been destroyed within a given zone. +-- +-- ### 1.9.2) Configure the audience of the messages. +-- +-- Use the following methods to configure the audience of the messages. By default, the messages are sent to all players in the mission. +-- +-- * @{#SCORING.SetMessagesToAll}(): Configure to send messages to all players. +-- * @{#SCORING.SetMessagesToCoalition}(): Configure to send messages to only those players within the same coalition as the player. +-- +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-02-26: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **Wingthor**: Testing & Advice. +-- * **Dutch-Baron**: Testing & Advice. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- -- @module Scoring --- @author FlightControl --- The Scoring class @@ -17805,7 +18053,7 @@ local _SCORINGCategory = function SCORING:New( GameName ) -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #SCORING if GameName then self.GameName = GameName @@ -17813,54 +18061,310 @@ function SCORING:New( GameName ) error( "A game name must be given to register the scoring results" ) end + -- Multipliers + self.MultiplierDestroyScore = 10 + self.MultiplierDestroyPenalty = 20 + -- Additional Object scores + self.ScoringObjects = {} + + -- Additional Zone scores. + self.ScoringZones = {} + + -- Configure Messages + self:SetMessagesToAll() + self:SetMessagesHit( true ) + self:SetMessagesDestroy( true ) + self:SetMessagesScore( true ) + self:SetMessagesZone( true ) + + -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). + self:SetFratricide( self.MultiplierDestroyPenalty * 2 ) + + -- Default penalty when a player changes coalition. + self:SetCoalitionChangePenalty( self.MultiplierDestroyPenalty ) + + -- Event handlers self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Hit, self._EventOnHit ) + self:HandleEvent( EVENTS.PlayerEnterUnit ) + self:HandleEvent( EVENTS.PlayerLeaveUnit ) - --self.SchedulerId = routines.scheduleFunction( SCORING._FollowPlayersScheduled, { self }, 0, 5 ) - --self.SchedulerId = SCHEDULER:New( self, self._FollowPlayersScheduled, {}, 0, 5 ) - - self:ScoreMenu() - - self:OpenCSV( GameName) + -- Create the CSV file. + self:OpenCSV( GameName ) return self end ---- Creates a score radio menu. Can be accessed using Radio -> F10. +--- Set the multiplier for scoring valid destroys (enemy destroys). +-- A calculated score is a value between 0.1 and 10. +-- The multiplier magnifies the scores given to the players. -- @param #SCORING self --- @return #SCORING self -function SCORING:ScoreMenu() - self.Menu = MENU_MISSION:New( 'Scoring' ) - self.AllScoresMenu = MENU_MISSION_COMMAND:New( 'Score All Active Players', self.Menu, SCORING.ReportScoreAll, self ) - --- = COMMANDMENU:New('Your Current Score', ReportScore, SCORING.ReportScorePlayer, self ) +-- @param #number Multiplier The multiplier of the score given. +function SCORING:SetMultiplierDestroyScore( Multiplier ) + + self.MultiplierDestroyScore = Multiplier + return self end ---- Follows new players entering Clients within the DCSRTE. --- TODO: Need to see if i can catch this also with an event. It will eliminate the schedule ... -function SCORING:_FollowPlayersScheduled() - self:F3( "_FollowPlayersScheduled" ) +--- Set the multiplier for scoring penalty destroys (friendly destroys). +-- A calculated score is a value between 0.1 and 10. +-- The multiplier magnifies the scores given to the players. +-- @param #SCORING self +-- @param #number Multiplier The multiplier of the score given. +-- @return #SCORING +function SCORING:SetMultiplierDestroyPenalty( Multiplier ) - local ClientUnit = 0 - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } - local unitId - local unitData - local AlivePlayerUnits = {} + self.MultiplierDestroyPenalty = Multiplier + + return self +end - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - self:T3( { "_FollowPlayersScheduled", CoalitionData } ) - for UnitId, UnitData in pairs( CoalitionData ) do - self:_AddPlayerFromUnit( UnitData ) - end +--- Add a @{Unit} for additional scoring when the @{Unit} is destroyed. +-- Note that if there was already a @{Unit} declared within the scoring with the same name, +-- then the old @{Unit} will be replaced with the new @{Unit}. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddUnitScore( ScoreUnit, Score ) + + local UnitName = ScoreUnit:GetName() + + self.ScoringObjects[UnitName] = Score + + return self +end + +--- Removes a @{Unit} for additional scoring when the @{Unit} is destroyed. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. +-- @return #SCORING +function SCORING:RemoveUnitScore( ScoreUnit ) + + local UnitName = ScoreUnit:GetName() + + self.ScoringObjects[UnitName] = nil + + return self +end + +--- Add a @{Static} for additional scoring when the @{Static} is destroyed. +-- Note that if there was already a @{Static} declared within the scoring with the same name, +-- then the old @{Static} will be replaced with the new @{Static}. +-- @param #SCORING self +-- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddStaticScore( ScoreStatic, Score ) + + local StaticName = ScoreStatic:GetName() + + self.ScoringObjects[StaticName] = Score + + return self +end + +--- Removes a @{Static} for additional scoring when the @{Static} is destroyed. +-- @param #SCORING self +-- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. +-- @return #SCORING +function SCORING:RemoveStaticScore( ScoreStatic ) + + local StaticName = ScoreStatic:GetName() + + self.ScoringObjects[StaticName] = nil + + return self +end + + +--- Specify a special additional score for a @{Group}. +-- @param #SCORING self +-- @param Wrapper.Group#GROUP ScoreGroup The @{Group} for which each @{Unit} a Score is given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddScoreGroup( ScoreGroup, Score ) + + local ScoreUnits = ScoreGroup:GetUnits() + + for ScoreUnitID, ScoreUnit in pairs( ScoreUnits ) do + local UnitName = ScoreUnit:GetName() + self.ScoringObjects[UnitName] = Score end - return true + return self +end + +--- Add a @{Zone} to define additional scoring when any object is destroyed in that zone. +-- Note that if a @{Zone} with the same name is already within the scoring added, the @{Zone} (type) and Score will be replaced! +-- This allows for a dynamic destruction zone evolution within your mission. +-- @param #SCORING self +-- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. +-- Note that a zone can be a polygon or a moving zone. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddZoneScore( ScoreZone, Score ) + + local ZoneName = ScoreZone:GetName() + + self.ScoringZones[ZoneName] = {} + self.ScoringZones[ZoneName].ScoreZone = ScoreZone + self.ScoringZones[ZoneName].Score = Score + + return self +end + +--- Remove a @{Zone} for additional scoring. +-- The scoring will search if any @{Zone} is added with the given name, and will remove that zone from the scoring. +-- This allows for a dynamic destruction zone evolution within your mission. +-- @param #SCORING self +-- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. +-- Note that a zone can be a polygon or a moving zone. +-- @return #SCORING +function SCORING:RemoveZoneScore( ScoreZone ) + + local ZoneName = ScoreZone:GetName() + + self.ScoringZones[ZoneName] = nil + + return self end +--- Configure to send messages after a target has been hit. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesHit( OnOff ) + + self.MessagesHit = OnOff + return self +end + +--- If to send messages after a target has been hit. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesHit() + + return self.MessagesHit +end + +--- Configure to send messages after a target has been destroyed. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesDestroy( OnOff ) + + self.MessagesDestroy = OnOff + return self +end + +--- If to send messages after a target has been destroyed. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesDestroy() + + return self.MessagesDestroy +end + +--- Configure to send messages after a target has been destroyed and receives additional scores. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesScore( OnOff ) + + self.MessagesScore = OnOff + return self +end + +--- If to send messages after a target has been destroyed and receives additional scores. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesScore() + + return self.MessagesScore +end + +--- Configure to send messages after a target has been hit in a zone, and additional score is received. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesZone( OnOff ) + + self.MessagesZone = OnOff + return self +end + +--- If to send messages after a target has been hit in a zone, and additional score is received. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesZone() + + return self.MessagesZone +end + +--- Configure to send messages to all players. +-- @param #SCORING self +-- @return #SCORING +function SCORING:SetMessagesToAll() + + self.MessagesAudience = 1 + return self +end + +--- If to send messages to all players. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesToAll() + + return self.MessagesAudience == 1 +end + +--- Configure to send messages to only those players within the same coalition as the player. +-- @param #SCORING self +-- @return #SCORING +function SCORING:SetMessagesToCoalition() + + self.MessagesAudience = 2 + return self +end + +--- If to send messages to only those players within the same coalition as the player. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesToCoalition() + + return self.MessagesAudience == 2 +end + + +--- When a player commits too much damage to friendlies, his penalty score will reach a certain level. +-- Use this method to define the level when a player gets kicked. +-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. +-- @param #SCORING self +-- @param #number Fratricide The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked. +-- @return #SCORING +function SCORING:SetFratricide( Fratricide ) + + self.Fratricide = Fratricide + return self +end + +--- When a player changes the coalition, he can receive a penalty score. +-- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. +-- By default, the penalty for changing coalition is the default penalty multiplier. +-- @param #SCORING self +-- @param #number CoalitionChangePenalty The amount of penalty that is given. +-- @return #SCORING +function SCORING:SetCoalitionChangePenalty( CoalitionChangePenalty ) + + self.CoalitionChangePenalty = CoalitionChangePenalty + return self +end --- Add a new player entering a Unit. @@ -17882,12 +18386,12 @@ function SCORING:_AddPlayerFromUnit( UnitData ) 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].Destroy = {} self.Players[PlayerName].Mission = {} -- for CategoryID, CategoryName in pairs( SCORINGCategory ) do -- self.Players[PlayerName].Hit[CategoryID] = {} - -- self.Players[PlayerName].Kill[CategoryID] = {} + -- self.Players[PlayerName].Destroy[CategoryID] = {} -- end self.Players[PlayerName].HitPlayers = {} self.Players[PlayerName].Score = 0 @@ -17916,7 +18420,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) self.Players[PlayerName].UnitType = UnitTypeName self.Players[PlayerName].UNIT = UnitData - if self.Players[PlayerName].Penalty > 100 then + if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 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 @@ -17925,7 +18429,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) end end - if self.Players[PlayerName].Penalty > 150 then + if self.Players[PlayerName].Penalty > self.Fratricide then local ClientGroup = GROUP:NewFromDCSUnit( UnitData ) ClientGroup:Destroy() MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", @@ -18005,6 +18509,34 @@ function SCORING:_AddMissionScore( Mission, Text, Score ) end end + +--- Handles the OnPlayerEnterUnit event for the scoring. +-- @param #SCORING self +-- @param Core.Event#EVENTDATA Event +function SCORING:OnEventPlayerEnterUnit( Event ) + if Event.IniUnit then + self:_AddPlayerFromUnit( Event.IniUnit ) + local Menu = MENU_GROUP:New( Event.IniGroup, 'Scoring' ) + local ReportGroupSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report players in group', Menu, SCORING.ReportScoreGroupSummary, self, Event.IniGroup ) + local ReportGroupDetailed = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Detailed report players in group', Menu, SCORING.ReportScoreGroupDetailed, self, Event.IniGroup ) + local ReportToAllSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report all players', Menu, SCORING.ReportScoreAllSummary, self, Event.IniGroup ) + self:SetState( Event.IniUnit, "ScoringMenu", Menu ) + end +end + +--- Handles the OnPlayerLeaveUnit event for the scoring. +-- @param #SCORING self +-- @param Core.Event#EVENTDATA Event +function SCORING:OnEventPlayerLeaveUnit( Event ) + if Event.IniUnit then + local Menu = self:GetState( Event.IniUnit, "ScoringMenu" ) -- Core.Menu#MENU_GROUP + if Menu then + Menu:Remove() + end + end +end + + --- Handles the OnHit event for the scoring. -- @param #SCORING self -- @param Core.Event#EVENTDATA Event @@ -18048,11 +18580,12 @@ function SCORING:_EventOnHit( Event ) InitGroupName = Event.IniDCSGroupName InitPlayerName = Event.IniPlayerName - InitCoalition = InitUnit:getCoalition() + InitCoalition = Event.IniCoalition --TODO: Workaround Client DCS Bug --InitCategory = InitUnit:getCategory() - InitCategory = InitUnit:getDesc().category - InitType = InitUnit:getTypeName() + --InitCategory = InitUnit:getDesc().category + InitCategory = Event.IniCategory + InitType = Event.IniTypeName InitUnitCoalition = _SCORINGCoalition[InitCoalition] InitUnitCategory = _SCORINGCategory[InitCategory] @@ -18071,11 +18604,12 @@ function SCORING:_EventOnHit( Event ) TargetGroupName = Event.TgtDCSGroupName TargetPlayerName = Event.TgtPlayerName - TargetCoalition = TargetUnit:getCoalition() + TargetCoalition = Event.TgtCoalition --TODO: Workaround Client DCS Bug --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnit:getDesc().category - TargetType = TargetUnit:getTypeName() + --TargetCategory = TargetUnit:getDesc().category + TargetCategory = Event.TgtCategory + TargetType = Event.TgtTypeName TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] TargetUnitCategory = _SCORINGCategory[TargetCategory] @@ -18125,43 +18659,66 @@ function SCORING:_EventOnHit( Event ) end local Score = 0 - if InitCoalition == TargetCoalition then - Player.Penalty = Player.Penalty + 10 - PlayerHit.Penalty = PlayerHit.Penalty + 10 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 - - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.PenaltyHit .. " times. Penalty: -" .. PlayerHit.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() + + if InitCoalition then -- A coalition object was hit. + if InitCoalition == TargetCoalition then + Player.Penalty = Player.Penalty + 10 + PlayerHit.Penalty = PlayerHit.Penalty + 10 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 + + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. + "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + else + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit a friendly target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. + "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + end + self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.PenaltyHit .. " times. Penalty: -" .. PlayerHit.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() + Player.Score = Player.Score + 1 + PlayerHit.Score = PlayerHit.Score + 1 + PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. + "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + else + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit an enemy target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. + "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + end + self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) end - self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - Player.Score = Player.Score + 1 - PlayerHit.Score = PlayerHit.Score + 1 - PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.ScoreHit .. " times. Score: " .. PlayerHit.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() - else - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.ScoreHit .. " times. Score: " .. PlayerHit.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() - end - self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else -- A scenery object was hit. + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit a scenery object.", + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end end end @@ -18197,10 +18754,11 @@ function SCORING:_EventOnDeadOrCrash( Event ) TargetGroupName = Event.IniDCSGroupName TargetPlayerName = Event.IniPlayerName - TargetCoalition = TargetUnit:getCoalition() + TargetCoalition = Event.IniCoalition --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnit:getDesc().category -- Workaround - TargetType = TargetUnit:getTypeName() + --TargetCategory = TargetUnit:getDesc().category -- Workaround + TargetCategory = Event.IniCategory + TargetType = Event.IniTypeName TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] TargetUnitCategory = _SCORINGCategory[TargetCategory] @@ -18212,7 +18770,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) -- Player contains the score and reference data for the player. for PlayerName, Player in pairs( self.Players ) do if Player then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got killed" ) + self:T( "Something got destroyed" ) -- Some variables local InitUnitName = Player.UnitName @@ -18224,67 +18782,136 @@ function SCORING:_EventOnDeadOrCrash( Event ) self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - -- What is he hitting? - if TargetCategory then - if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? + -- What is the player destroying? + if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? - - Player.Kill[TargetCategory] = Player.Kill[TargetCategory] or {} - Player.Kill[TargetCategory][TargetType] = Player.Kill[TargetCategory][TargetType] or {} + Player.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} + Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} - -- PlayerKill contains the kill score data per category and target type of the player. - local PlayerKill = Player.Kill[TargetCategory][TargetType] - Player.Kill[TargetCategory][TargetType] = {} - PlayerKill.Score = PlayerKill.Score or 0 - PlayerKill.ScoreKill = PlayerKill.ScoreKill or 0 - PlayerKill.Penalty = PlayerKill.Penalty or 0 - PlayerKill.PenaltyKill = PlayerKill.PenaltyKill or 0 - PlayerKill.UNIT = PlayerKill.UNIT or Player.Hit[TargetCategory][TargetUnitName].UNIT - + -- PlayerDestroy contains the destroy score data per category and target type of the player. + local TargetDestroy = Player.Destroy[TargetCategory][TargetType] + TargetDestroy.Score = TargetDestroy.Score or 0 + TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy or 0 + TargetDestroy.Penalty = TargetDestroy.Penalty or 0 + TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy or 0 + TargetDestroy.UNIT = TargetDestroy.UNIT or Player.Hit[TargetCategory][TargetUnitName].UNIT + + if TargetCoalition then if InitCoalition == TargetCoalition then - local ThreatLevelTarget, ThreatTypeTarget = PlayerKill.UNIT:GetThreatLevel() + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() - local ThreatLevel = math.ceil( ThreatLevelTarget / ThreatLevelPlayer * 100 ) - self:E( { ThreatLevel = ThreatLevel, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) + local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.MultiplierDestroyPenalty ) + self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - Player.Penalty = Player.Penalty + ThreatLevel * 4 - PlayerKill.Penalty = PlayerKill.Penalty + ThreatLevel * 4 - PlayerKill.PenaltyKill = PlayerKill.PenaltyKill + 1 + Player.Penalty = Player.Penalty + ThreatPenalty + TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty + TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 - if Player.HitPlayers[TargetPlayerName] then -- A player killed another player - MESSAGE:New( "Player '" .. PlayerName .. "' killed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.PenaltyKill .. " times. Penalty: -" .. PlayerKill.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. + "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) else - MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.PenaltyKill .. " times. Penalty: -" .. PlayerKill.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed a friendly target " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. + "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end - self:ScoreCSV( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + self:ScoreCSV( PlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - - local ThreatLevelTarget, ThreatTypeTarget = PlayerKill.UNIT:GetThreatLevel() + + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() - local ThreatLevel = math.ceil( ThreatLevelTarget / ThreatLevelPlayer * 100 ) - self:E( { ThreatLevel = ThreatLevel, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - - Player.Score = Player.Score + ThreatLevel - PlayerKill.Score = PlayerKill.Score + ThreatLevel - PlayerKill.ScoreKill = PlayerKill.ScoreKill + 1 - if Player.HitPlayers[TargetPlayerName] then -- A player killed another player - MESSAGE:New( "Player '" .. PlayerName .. "' killed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.ScoreKill .. " times. Score: " .. PlayerKill.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.MultiplierDestroyScore ) + self:E( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) + + Player.Score = Player.Score + ThreatScore + TargetDestroy.Score = TargetDestroy.Score + ThreatScore + TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1 + if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. + "Score: " .. TargetDestroy.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) else - MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.ScoreKill .. " times. Score: " .. PlayerKill.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed an enemy " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. + "Score: " .. TargetDestroy.Score .. ". Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) + end + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + + local UnitName = TargetDestroy.UNIT:GetName() + local Score = self.ScoringObjects[UnitName] + if Score then + Player.Score = Player.Score + Score + TargetDestroy.Score = TargetDestroy.Score + Score + MESSAGE + :New( "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " .. + "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + + -- Check if there are Zones where the destruction happened. + for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do + self:E( { ScoringZone = ScoreZoneData } ) + local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE + local Score = ScoreZoneData.Score + if ScoreZone:IsVec2InZone( TargetDestroy.UNIT:GetVec2() ) then + Player.Score = Player.Score + Score + TargetDestroy.Score = TargetDestroy.Score + Score + MESSAGE + :New( "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." .. + "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. + "Total: " .. Player.Score - Player.Penalty, + 15 ) + :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + end + + end + else + -- Check if there are Zones where the destruction happened. + for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do + self:E( { ScoringZone = ScoreZoneData } ) + local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE + local Score = ScoreZoneData.Score + if ScoreZone:IsVec2InZone( TargetDestroy.UNIT:GetVec2() ) then + Player.Score = Player.Score + Score + TargetDestroy.Score = TargetDestroy.Score + Score + MESSAGE + :New( "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." .. + "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. + "Total: " .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end - self:ScoreCSV( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) end end end @@ -18293,229 +18920,324 @@ function SCORING:_EventOnDeadOrCrash( Event ) end -function SCORING:ReportScoreAll() - - env.info( "Hello World " ) +--- Produce detailed report of player hit scores. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerHits( PlayerName ) local ScoreMessage = "" - local PlayerMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 - self:T( "Score Report" ) + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - 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 ) + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = ":\n" - - local ScoreMessageHits = "" - - for CategoryID, CategoryName in pairs( _SCORINGCategory ) 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 ) + local ScoreMessageHits = "" + for CategoryID, CategoryName in pairs( _SCORINGCategory ) do + self:T( CategoryName ) + if PlayerData.Hit[CategoryID] then + self:T( "Hit scores exist for player " .. PlayerName ) + local Score = 0 + local ScoreHit = 0 + local Penalty = 0 + local PenaltyHit = 0 + 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 - if ScoreMessageHits ~= "" then - ScoreMessage = ScoreMessage .. " Hits: " .. ScoreMessageHits .. "\n" - end - - local ScoreMessageKills = "" - for CategoryID, CategoryName in pairs( _SCORINGCategory ) 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 + if ScoreMessageHits ~= "" then + ScoreMessage = "Hits: " .. ScoreMessageHits end end - MESSAGE:New( PlayerMessage, 30, "Player Scores" ):ToAll() + + return ScoreMessage, PlayerScore, PlayerPenalty end -function SCORING:ReportScorePlayer() - - env.info( "Hello World " ) +--- Produce detailed report of player destroy scores. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerDestroys( PlayerName ) local ScoreMessage = "" - local PlayerMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 - self:T( "Score Report" ) + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - 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 ) + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName + local ScoreMessageDestroys = "" + for CategoryID, CategoryName in pairs( _SCORINGCategory ) do + if PlayerData.Destroy[CategoryID] then + self:T( "Destroy scores exist for player " .. PlayerName ) + local Score = 0 + local ScoreDestroy = 0 + local Penalty = 0 + local PenaltyDestroy = 0 - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = "" - - local ScoreMessageHits = "" - - for CategoryID, CategoryName in pairs( _SCORINGCategory ) 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 + for UnitName, UnitData in pairs( PlayerData.Destroy[CategoryID] ) do + self:E( { UnitData = UnitData } ) + if UnitData ~= {} then Score = Score + UnitData.Score - ScoreHit = ScoreHit + UnitData.ScoreHit + ScoreDestroy = ScoreDestroy + UnitData.ScoreDestroy Penalty = Penalty + UnitData.Penalty - PenaltyHit = UnitData.PenaltyHit + PenaltyDestroy = PenaltyDestroy + UnitData.PenaltyDestroy 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( _SCORINGCategory ) 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 + local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageDestroy ) + ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy - 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 + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) 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 + if ScoreMessageDestroys ~= "" then + ScoreMessage = "Destroys: " .. ScoreMessageDestroys + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + +--- Produce detailed report of player penalty scores because of changing the coalition. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerCoalitionChanges( PlayerName ) + + local ScoreMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 + + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) + + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName + + 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 + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + +--- Produce detailed report of player penalty scores because of changing the coalition. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerMissions( PlayerName ) + + local ScoreMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 + + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) + + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName + + 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 + + if ScoreMessageMission ~= "" then + ScoreMessage = ScoreMessage .. "Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")" + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + + +--- Report Group Score Summary +-- @param #SCORING self +-- @param Wrapper.Group#GROUP PlayerGroup The player group. +function SCORING:ReportScoreGroupSummary( PlayerGroup ) + + local PlayerMessage = "" + + self:T( "Report Score Group Summary" ) + + local PlayerUnits = PlayerGroup:GetUnits() + for UnitID, PlayerUnit in pairs( PlayerUnits ) do + local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + + if PlayerName then + + local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) + ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits + self:E( { ReportHits, ScoreHits, PenaltyHits } ) + local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) + ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys + self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) + local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) + ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges + self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty + ) + MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) + end + end + +end + +--- Report Group Score Detailed +-- @param #SCORING self +-- @param Wrapper.Group#GROUP PlayerGroup The player group. +function SCORING:ReportScoreGroupDetailed( PlayerGroup ) + + local PlayerMessage = "" + + self:T( "Report Score Group Detailed" ) + + local PlayerUnits = PlayerGroup:GetUnits() + for UnitID, PlayerUnit in pairs( PlayerUnits ) do + local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + + if PlayerName then + + local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) + ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits + self:E( { ReportHits, ScoreHits, PenaltyHits } ) + local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) + ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys + self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) + local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) + ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges + self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty, + ReportHits, + ReportDestroys, + ReportCoalitionChanges, + ReportMissions + ) + MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) + end + end + +end + +--- Report all players score +-- @param #SCORING self +-- @param Wrapper.Group#GROUP PlayerGroup The player group. +function SCORING:ReportScoreAllSummary( PlayerGroup ) + + local PlayerMessage = "" + + self:T( "Report Score All Players" ) + + for PlayerName, PlayerData in pairs( self.Players ) do + + if PlayerName then + + local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) + ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits + self:E( { ReportHits, ScoreHits, PenaltyHits } ) + local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) + ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys + self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) + local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) + ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges + self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty + ) + MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) end end - MESSAGE:New( PlayerMessage, 30, "Player Scores" ):ToAll() end @@ -18616,21 +19338,10 @@ function SCORING:ScoreCSV( PlayerName, ScoreType, ScoreTimes, ScoreAmount, Playe 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 + TargetUnitCoalition = TargetUnitCoalition or "" + TargetUnitCategory = TargetUnitCategory or "" + TargetUnitType = TargetUnitType or "" + TargetUnitName = TargetUnitName or "" if lfs and io and os then self.CSVFile:write( @@ -32290,6 +33001,7 @@ Include.File( "Wrapper/Unit" ) Include.File( "Wrapper/Client" ) Include.File( "Wrapper/Static" ) Include.File( "Wrapper/Airbase" ) +Include.File( "Wrapper/Scenery" ) --- Functional Classes Include.File( "Functional/Scoring" ) diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index 8b4e8253d..2a2b9f013 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE STATIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170226_1531' ) +env.info( 'Moose Generation Timestamp: 20170228_1815' ) local base = _G Include = {} @@ -4148,14 +4148,14 @@ end -- **IMPORTANT NOTE:** Some events can involve not just UNIT objects, but also STATIC objects!!! -- In that case the initiator or target unit fields will refer to a STATIC object! -- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated. --- The fields **IniCategory** and **TgtCategory** contain the indicator which **kind of object is involved** in the event. --- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniCategory and TgtCategory. +-- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event. +-- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory. -- Example code snippet: -- --- if Event.IniCategory == Object.Category.UNIT then +-- if Event.IniObjectCategory == Object.Category.UNIT then -- ... -- end --- if Event.IniCategory == Object.Category.STATIC then +-- if Event.IniObjectCategory == Object.Category.STATIC then -- ... -- end -- @@ -4190,7 +4190,7 @@ end -- @module Event -- TODO: Need to update the EVENTDATA documentation with IniPlayerName and TgtPlayerName --- TODO: Need to update the EVENTDATA documentation with IniCategory and TgtCategory +-- TODO: Need to update the EVENTDATA documentation with IniObjectCategory and TgtObjectCategory @@ -4241,8 +4241,8 @@ EVENTS = { -- @type EVENTDATA -- @field #number id The identifier of the event. -- --- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category IniCategory (UNIT/STATIC) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). +-- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. +-- @field Dcs.DCSObject#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field Dcs.DCSUnit#Unit IniDCSUnit (UNIT/STATIC) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. -- @field #string IniDCSUnitName (UNIT/STATIC) The initiating Unit name. -- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Wrapper.Unit#UNIT} of the initiator Unit object. @@ -4252,9 +4252,12 @@ EVENTS = { -- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Wrapper.Group#GROUP} of the initiator Group object. -- @field #string IniGroupName (UNIT) The initiating GROUP name (same as IniDCSGroupName). -- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot. +-- @field Dcs.DCScoalition#coalition.side IniCoalition (UNIT) The coalition of the initiator. +-- @field Dcs.DCSUnit#Unit.Category IniCategory (UNIT) The category of the initiator. +-- @field #string IniTypeName (UNIT) The type name of the initiator. -- -- @field Dcs.DCSUnit#Unit target (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category TgtCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). +-- @field Dcs.DCSObject#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field Dcs.DCSUnit#Unit TgtDCSUnit (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. -- @field #string TgtDCSUnitName (UNIT/STATIC) The target Unit name. -- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Wrapper.Unit#UNIT} of the target Unit object. @@ -4264,6 +4267,9 @@ EVENTS = { -- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Wrapper.Group#GROUP} of the target Group object. -- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName). -- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot. +-- @field Dcs.DCScoalition#coalition.side TgtCoalition (UNIT) The coalition of the target. +-- @field Dcs.DCSUnit#Unit.Category TgtCategory (UNIT) The category of the target. +-- @field #string TgtTypeName (UNIT) The type name of the target. -- -- @field weapon The weapon used during the event. -- @field Weapon @@ -5071,8 +5077,10 @@ function EVENT:onEvent( Event ) if Event.initiator then - Event.IniCategory = Event.initiator:getCategory() - if Event.IniCategory == Object.Category.UNIT then + + Event.IniObjectCategory = Event.initiator:getCategory() + + if Event.IniObjectCategory == Object.Category.UNIT then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniUnitName = Event.IniDCSUnitName @@ -5089,19 +5097,36 @@ function EVENT:onEvent( Event ) self:E( { IniGroup = Event.IniGroup } ) end Event.IniPlayerName = Event.IniDCSUnit:getPlayerName() + Event.IniCoalition = Event.IniDCSUnit:getCoalition() + Event.IniTypeName = Event.IniDCSUnit:getTypeName() + Event.IniCategory = Event.IniDCSUnit:getDesc().category end - if Event.IniCategory == Object.Category.STATIC then + if Event.IniObjectCategory == Object.Category.STATIC then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniUnitName = Event.IniDCSUnitName Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName ) + Event.IniCoalition = Event.IniDCSUnit:getCoalition() + Event.IniCategory = Event.IniDCSUnit:getDesc().category + Event.IniTypeName = Event.IniDCSUnit:getTypeName() + end + + if Event.IniObjectCategory == Object.Category.SCENERY then + Event.IniDCSUnit = Event.initiator + Event.IniDCSUnitName = Event.IniDCSUnit:getName() + Event.IniUnitName = Event.IniDCSUnitName + Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator ) + Event.IniCategory = Event.IniDCSUnit:getDesc().category + Event.IniTypeName = Event.IniDCSUnit:getTypeName() end end if Event.target then - Event.TgtCategory = Event.target:getCategory() - if Event.TgtCategory == Object.Category.UNIT then + + Event.TgtObjectCategory = Event.target:getCategory() + + if Event.TgtObjectCategory == Object.Category.UNIT then Event.TgtDCSUnit = Event.target Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup() Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() @@ -5112,18 +5137,31 @@ function EVENT:onEvent( Event ) Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() end Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() + Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() end - if Event.TgtCategory == Object.Category.STATIC then + if Event.TgtObjectCategory == Object.Category.STATIC then Event.TgtDCSUnit = Event.target Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() Event.TgtUnitName = Event.TgtDCSUnitName Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName ) + Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() + end + + if Event.TgtObjectCategory == Object.Category.SCENERY then + Event.TgtDCSUnit = Event.target + Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() + Event.TgtUnitName = Event.TgtDCSUnitName + Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target ) + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() end end - - if Event.weapon then Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() @@ -6165,8 +6203,8 @@ end -- -- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: -- --- * @{#ZONE_BASE.IsPointVec2InZone}(): Returns if a @{Point#POINT_VEC2} is within the zone. --- * @{#ZONE_BASE.IsPointVec3InZone}(): Returns if a @{Point#POINT_VEC3} is within the zone. +-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. +-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. -- -- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: -- @@ -6275,21 +6313,28 @@ end -- -- Hereby the change log: -- --- 2017-02-18: ZONE_POLYGON_BASE:**GetRandomPointVec2()** added. +-- 2017-02-28: ZONE\_BASE:**IsVec2InZone()** replaces ZONE\_BASE:_IsPointVec2InZone()_. +-- 2017-02-28: ZONE\_BASE:**IsVec3InZone()** replaces ZONE\_BASE:_IsPointVec3InZone()_. +-- 2017-02-28: ZONE\_RADIUS:**IsVec2InZone()** replaces ZONE\_RADIUS:_IsPointVec2InZone()_. +-- 2017-02-28: ZONE\_RADIUS:**IsVec3InZone()** replaces ZONE\_RADIUS:_IsPointVec3InZone()_. +-- 2017-02-28: ZONE\_POLYGON:**IsVec2InZone()** replaces ZONE\_POLYGON:_IsPointVec2InZone()_. +-- 2017-02-28: ZONE\_POLYGON:**IsVec3InZone()** replaces ZONE\_POLYGON:_IsPointVec3InZone()_. -- --- 2017-02-18: ZONE_POLYGON_BASE:**GetRandomPointVec3()** added. +-- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec2()** added. -- --- 2017-02-18: ZONE_RADIUS:**GetRandomPointVec3( inner, outer )** added. +-- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec3()** added. -- --- 2017-02-18: ZONE_RADIUS:**GetRandomPointVec2( inner, outer )** added. +-- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec3( inner, outer )** added. -- --- 2016-08-15: ZONE_BASE:**GetName()** added. +-- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec2( inner, outer )** added. -- --- 2016-08-15: ZONE_BASE:**SetZoneProbability( ZoneProbability )** added. +-- 2016-08-15: ZONE\_BASE:**GetName()** added. -- --- 2016-08-15: ZONE_BASE:**GetZoneProbability()** added. +-- 2016-08-15: ZONE\_BASE:**SetZoneProbability( ZoneProbability )** added. -- --- 2016-08-15: ZONE_BASE:**GetZoneMaybe()** added. +-- 2016-08-15: ZONE\_BASE:**GetZoneProbability()** added. +-- +-- 2016-08-15: ZONE\_BASE:**GetZoneMaybe()** added. -- -- === -- @@ -6342,7 +6387,7 @@ end -- @param #ZONE_BASE self -- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_BASE:IsPointVec2InZone( Vec2 ) +function ZONE_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) return false @@ -6352,10 +6397,10 @@ end -- @param #ZONE_BASE self -- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. -- @return #boolean true if the point is within the zone. -function ZONE_BASE:IsPointVec3InZone( Vec3 ) +function ZONE_BASE:IsVec3InZone( Vec3 ) self:F2( Vec3 ) - local InZone = self:IsPointVec2InZone( { x = Vec3.x, y = Vec3.z } ) + local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) return InZone end @@ -6585,7 +6630,7 @@ end -- @param #ZONE_RADIUS self -- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_RADIUS:IsPointVec2InZone( Vec2 ) +function ZONE_RADIUS:IsVec2InZone( Vec2 ) self:F2( Vec2 ) local ZoneVec2 = self:GetVec2() @@ -6603,10 +6648,10 @@ end -- @param #ZONE_RADIUS self -- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. -- @return #boolean true if the point is within the zone. -function ZONE_RADIUS:IsPointVec3InZone( Vec3 ) +function ZONE_RADIUS:IsVec3InZone( Vec3 ) self:F2( Vec3 ) - local InZone = self:IsPointVec2InZone( { x = Vec3.x, y = Vec3.z } ) + local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) return InZone end @@ -6925,7 +6970,7 @@ end -- @param #ZONE_POLYGON_BASE self -- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_POLYGON_BASE:IsPointVec2InZone( Vec2 ) +function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) local Next @@ -6967,7 +7012,7 @@ function ZONE_POLYGON_BASE:GetRandomVec2() while Vec2Found == false do Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } self:T2( Vec2 ) - if self:IsPointVec2InZone( Vec2 ) then + if self:IsVec2InZone( Vec2 ) then Vec2Found = true end end @@ -11040,6 +11085,20 @@ function MESSAGE:ToCoalition( CoalitionSide ) return self end +--- Sends a MESSAGE to a Coalition if the given Condition is true. +-- @param #MESSAGE self +-- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. +-- @return #MESSAGE +function MESSAGE:ToCoalitionIf( CoalitionSide, Condition ) + self:F( CoalitionSide ) + + if Condition and Condition == true then + self:ToCoalition( CoalitionSide ) + end + + return self +end + --- Sends a MESSAGE to all players. -- @param #MESSAGE self -- @return #MESSAGE @@ -11052,10 +11111,24 @@ end -- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ) -- MessageAll:ToAll() function MESSAGE:ToAll() - self:F() + self:F() - self:ToCoalition( coalition.side.RED ) - self:ToCoalition( coalition.side.BLUE ) + self:ToCoalition( coalition.side.RED ) + self:ToCoalition( coalition.side.BLUE ) + + return self +end + + +--- Sends a MESSAGE to all players if the given Condition is true. +-- @param #MESSAGE self +-- @return #MESSAGE +function MESSAGE:ToAllIf( Condition ) + + if Condition and Condition == true then + self:ToCoalition( coalition.side.RED ) + self:ToCoalition( coalition.side.BLUE ) + end return self end @@ -12495,13 +12568,10 @@ function IDENTIFIABLE:GetCallsign() end +function IDENTIFIABLE:GetThreatLevel() - - - - - - + return 0, "Scenery" +end --- This module contains the POSITIONABLE class. -- -- 1) @{Positionable#POSITIONABLE} class, extends @{Identifiable#IDENTIFIABLE} @@ -15689,8 +15759,7 @@ function GROUP:IsCompletelyInZone( Zone ) for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT - -- TODO: Rename IsPointVec3InZone to IsVec3InZone - if Zone:IsPointVec3InZone( Unit:GetVec3() ) then + if Zone:IsVec3InZone( Unit:GetVec3() ) then else return false end @@ -15708,7 +15777,7 @@ function GROUP:IsPartlyInZone( Zone ) for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsPointVec3InZone( Unit:GetVec3() ) then + if Zone:IsVec3InZone( Unit:GetVec3() ) then return true end end @@ -15725,7 +15794,7 @@ function GROUP:IsNotInZone( Zone ) for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsPointVec3InZone( Unit:GetVec3() ) then + if Zone:IsVec3InZone( Unit:GetVec3() ) then return false end end @@ -16808,7 +16877,7 @@ function UNIT:IsInZone( Zone ) self:F2( { self.UnitName, Zone } ) if self:IsAlive() then - local IsInZone = Zone:IsPointVec3InZone( self:GetVec3() ) + local IsInZone = Zone:IsVec3InZone( self:GetVec3() ) self:T( { IsInZone } ) return IsInZone @@ -16825,7 +16894,7 @@ function UNIT:IsNotInZone( Zone ) self:F2( { self.UnitName, Zone } ) if self:IsAlive() then - local IsInZone = not Zone:IsPointVec3InZone( self:GetVec3() ) + local IsInZone = not Zone:IsVec3InZone( self:GetVec3() ) self:T( { IsInZone } ) return IsInZone @@ -17749,10 +17818,49 @@ end +--- This module contains the SCENERY class. +-- +-- 1) @{Scenery#SCENERY} class, extends @{Positionable#POSITIONABLE} +-- =============================================================== +-- Scenery objects are defined on the map. +-- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: +-- +-- * Wraps the DCS Scenery objects. +-- * Support all DCS Scenery APIs. +-- * Enhance with Scenery specific APIs not in the DCS API set. +-- +-- @module Scenery +-- @author FlightControl + + + +--- The SCENERY class +-- @type SCENERY +-- @extends Wrapper.Positionable#POSITIONABLE +SCENERY = { + ClassName = "SCENERY", +} + + +function SCENERY:Register( SceneryName, SceneryObject ) + local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) + self.SceneryName = SceneryName + self.SceneryObject = SceneryObject + return self +end + +function SCENERY:GetDCSObject() + return self.SceneryObject +end + +function SCENERY:GetThreatLevel() + + return 0, "Scenery" +end --- Single-Player:**Yes** / Multi-Player:**Yes** / Core:**Yes** -- **Administer the scoring of player achievements, -- and create a CSV file logging the scoring events for use at team or squadron websites.** -- --- -- ![Banner Image](..\Presentations\AI_Balancer\Dia1.JPG) +-- ![Banner Image](..\Presentations\SCORING\Dia1.JPG) -- -- === -- @@ -17760,14 +17868,154 @@ end -- -- The @{#SCORING} class administers the scoring of player achievements, -- and creates a CSV file logging the scoring events for use at team or squadron websites. --- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions. -- --- This scoring class calculates the hits and kills that players make within a simulation session. --- Scoring is calculated using a defined algorithm. --- With a small change in MissionScripting.lua, the scoring can also be logged in a CSV file, that can then be uploaded --- to a database or a BI tool to publish the scoring results to the player community. +-- The scores are calculated by scoring the hits and destroys of objects that players make, +-- which are @{Unit} and @{Static) objects within your mission. +-- +-- Scores are calculated based on the threat level of the objects involved. +-- The threat level of a unit can be a value between 0 and 10. +-- A calculated score takes the threat level of the target divided by the threat level of the player unit. +-- This provides a value between 0.1 and 10. +-- The stronger or the higher the threat of the player unit, the less score will be given in destroys. +-- That value can then be multiplied by a multiplier. A specific multiplier can be set for enemies and friendlies destroys. +-- +-- If multiple players hit the same target, and finally the target gets destroyed, each player who contributed to the target +-- destruction, will receive a score. This is important for targets that require significant damage before it can be destroyed, like +-- ships or heavy planes. +-- +-- **Additional scores** can be granted to **specific objects**, when the player(s) destroy these objects. +-- **Various @{Zone}s** can be defined for which scores are also granted when objects in that @{Zone} are destroyed. +-- This is **specifically useful** to designate **scenery targets on the map** that will generate points when destroyed. +-- +-- With a small change in MissionScripting.lua, the scoring can also be logged in a CSV file. +-- The CSV files can be used to: +-- +-- * Upload scoring to a database or a BI tool to publish the scoring results to the player community. +-- * Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results. +-- * Share scoring amoung players after the mission to discuss mission results. +-- +-- Scores can be reported. Menu options are automatically added to each group when a player joins a client slot or a CA unit. +-- Use the radio menu F10 to consult the scores while running the mission. +-- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. +-- +-- ## 1.1) Set the destroy score or penalty multiplier +-- +-- Score multipliers can be set for scores granted when enemies or friendlies are destroyed. +-- Use the method @{#SCORING.SetMultiplierDestroyScore}() to set the multiplier of enemy destroys (positive destroys). +-- Use the method @{#SCORING.SetMultiplierDestroyPenalty}() to set the multiplier of friendly destroys (negative destroys). +-- +-- local Scoring = SCORING:New( "Scoring File" ) +-- Scoring:SetMultiplierDestroyScore( 10 ) +-- Scoring:SetMultiplierDestroyPenalty( 40 ) +-- +-- The above sets the multiplier for valid scores to 10. So scores will be given in a scale from 0 to 10. +-- The penalties will be given in a scale from 0 to 40. +-- +-- ## 1.2) Define special targets that will give extra scores. +-- +-- Special targets can be set that will give extra scores to the players when these are destroyed. +-- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Unit}s. +-- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Static}s. +-- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Group}s. +-- +-- local Scoring = SCORING:New( "Scoring File" ) +-- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) +-- Scoring:AddStaticScore( STATIC:FindByName( "Static #1" ), 100 ) +-- +-- The above grants an additional score of 200 points for Unit #001 and an additional 100 points of Static #1 if these are destroyed. +-- Note that later in the mission, one can remove these scores set, for example, when the a goal achievement time limit is over. +-- For example, this can be done as follows: +-- +-- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) ) +-- +-- +-- +-- ## 1.3) Define destruction zones that will give extra scores. +-- +-- Define zones of destruction. Any object destroyed within the zone of the given category will give extra points. +-- Use the method @{#SCORING.AddZoneScore}() to add a @{Zone} for additional scoring. +-- Use the method @{#SCORING.RemoveZoneScore}() to remove a @{Zone} for additional scoring. +-- There are interesting variations that can be achieved with this functionality. For example, if the @{Zone} is a @{Zone#ZONE_UNIT}, +-- then the zone is a moving zone, and anything destroyed within that @{Zone} will generate points. +-- The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a @{Zone}, +-- just large enough around that building. +-- +-- ## 1.4) Configure fratricide level. +-- +-- When a player commits too much damage to friendlies, his penalty score will reach a certain level. +-- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked. +-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. +-- +-- ## 1.5) Penalty score when a player changes the coalition. +-- +-- When a player changes the coalition, he can receive a penalty score. +-- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. +-- By default, the penalty for changing coalition is the default penalty multiplier. +-- +-- ## 1.8) Define output CSV files. +-- +-- The CSV file is given the name of the string given in the @{#SCORING.New}{} constructor, followed by the .csv extension. +-- The file is incrementally saved in the **\\Saved Games\\DCS\\Logs** folder, and has a time stamp indicating each mission run. +-- See the following example: +-- +-- local ScoringFirstMission = SCORING:New( "FirstMission" ) +-- local ScoringSecondMission = SCORING:New( "SecondMission" ) +-- +-- The above documents that 2 Scoring objects are created, ScoringFirstMission and ScoringSecondMission. +-- +-- ## 1.9) Configure messages. +-- +-- When players hit or destroy targets, messages are sent. +-- Various methods exist to configure: +-- +-- * Which messages are sent upon the event. +-- * Which audience receives the message. +-- +-- ### 1.9.1) Configure the messages sent upon the event. +-- +-- Use the following methods to configure when to send messages. By default, all messages are sent. +-- +-- * @{#SCORING.SetMessagesHit}(): Configure to send messages after a target has been hit. +-- * @{#SCORING.SetMessagesDestroy}(): Configure to send messages after a target has been destroyed. +-- * @{#SCORING.SetMessagesAddon}(): Configure to send messages for additional score, after a target has been destroyed. +-- * @{#SCORING.SetMessagesZone}(): Configure to send messages for additional score, after a target has been destroyed within a given zone. +-- +-- ### 1.9.2) Configure the audience of the messages. +-- +-- Use the following methods to configure the audience of the messages. By default, the messages are sent to all players in the mission. +-- +-- * @{#SCORING.SetMessagesToAll}(): Configure to send messages to all players. +-- * @{#SCORING.SetMessagesToCoalition}(): Configure to send messages to only those players within the same coalition as the player. +-- +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- 2017-02-26: Initial class and API. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * **Wingthor**: Testing & Advice. +-- * **Dutch-Baron**: Testing & Advice. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- -- @module Scoring --- @author FlightControl --- The Scoring class @@ -17805,7 +18053,7 @@ local _SCORINGCategory = function SCORING:New( GameName ) -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #SCORING if GameName then self.GameName = GameName @@ -17813,54 +18061,310 @@ function SCORING:New( GameName ) error( "A game name must be given to register the scoring results" ) end + -- Multipliers + self.MultiplierDestroyScore = 10 + self.MultiplierDestroyPenalty = 20 + -- Additional Object scores + self.ScoringObjects = {} + + -- Additional Zone scores. + self.ScoringZones = {} + + -- Configure Messages + self:SetMessagesToAll() + self:SetMessagesHit( true ) + self:SetMessagesDestroy( true ) + self:SetMessagesScore( true ) + self:SetMessagesZone( true ) + + -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). + self:SetFratricide( self.MultiplierDestroyPenalty * 2 ) + + -- Default penalty when a player changes coalition. + self:SetCoalitionChangePenalty( self.MultiplierDestroyPenalty ) + + -- Event handlers self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Hit, self._EventOnHit ) + self:HandleEvent( EVENTS.PlayerEnterUnit ) + self:HandleEvent( EVENTS.PlayerLeaveUnit ) - --self.SchedulerId = routines.scheduleFunction( SCORING._FollowPlayersScheduled, { self }, 0, 5 ) - --self.SchedulerId = SCHEDULER:New( self, self._FollowPlayersScheduled, {}, 0, 5 ) - - self:ScoreMenu() - - self:OpenCSV( GameName) + -- Create the CSV file. + self:OpenCSV( GameName ) return self end ---- Creates a score radio menu. Can be accessed using Radio -> F10. +--- Set the multiplier for scoring valid destroys (enemy destroys). +-- A calculated score is a value between 0.1 and 10. +-- The multiplier magnifies the scores given to the players. -- @param #SCORING self --- @return #SCORING self -function SCORING:ScoreMenu() - self.Menu = MENU_MISSION:New( 'Scoring' ) - self.AllScoresMenu = MENU_MISSION_COMMAND:New( 'Score All Active Players', self.Menu, SCORING.ReportScoreAll, self ) - --- = COMMANDMENU:New('Your Current Score', ReportScore, SCORING.ReportScorePlayer, self ) +-- @param #number Multiplier The multiplier of the score given. +function SCORING:SetMultiplierDestroyScore( Multiplier ) + + self.MultiplierDestroyScore = Multiplier + return self end ---- Follows new players entering Clients within the DCSRTE. --- TODO: Need to see if i can catch this also with an event. It will eliminate the schedule ... -function SCORING:_FollowPlayersScheduled() - self:F3( "_FollowPlayersScheduled" ) +--- Set the multiplier for scoring penalty destroys (friendly destroys). +-- A calculated score is a value between 0.1 and 10. +-- The multiplier magnifies the scores given to the players. +-- @param #SCORING self +-- @param #number Multiplier The multiplier of the score given. +-- @return #SCORING +function SCORING:SetMultiplierDestroyPenalty( Multiplier ) - local ClientUnit = 0 - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } - local unitId - local unitData - local AlivePlayerUnits = {} + self.MultiplierDestroyPenalty = Multiplier + + return self +end - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - self:T3( { "_FollowPlayersScheduled", CoalitionData } ) - for UnitId, UnitData in pairs( CoalitionData ) do - self:_AddPlayerFromUnit( UnitData ) - end +--- Add a @{Unit} for additional scoring when the @{Unit} is destroyed. +-- Note that if there was already a @{Unit} declared within the scoring with the same name, +-- then the old @{Unit} will be replaced with the new @{Unit}. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddUnitScore( ScoreUnit, Score ) + + local UnitName = ScoreUnit:GetName() + + self.ScoringObjects[UnitName] = Score + + return self +end + +--- Removes a @{Unit} for additional scoring when the @{Unit} is destroyed. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. +-- @return #SCORING +function SCORING:RemoveUnitScore( ScoreUnit ) + + local UnitName = ScoreUnit:GetName() + + self.ScoringObjects[UnitName] = nil + + return self +end + +--- Add a @{Static} for additional scoring when the @{Static} is destroyed. +-- Note that if there was already a @{Static} declared within the scoring with the same name, +-- then the old @{Static} will be replaced with the new @{Static}. +-- @param #SCORING self +-- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddStaticScore( ScoreStatic, Score ) + + local StaticName = ScoreStatic:GetName() + + self.ScoringObjects[StaticName] = Score + + return self +end + +--- Removes a @{Static} for additional scoring when the @{Static} is destroyed. +-- @param #SCORING self +-- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. +-- @return #SCORING +function SCORING:RemoveStaticScore( ScoreStatic ) + + local StaticName = ScoreStatic:GetName() + + self.ScoringObjects[StaticName] = nil + + return self +end + + +--- Specify a special additional score for a @{Group}. +-- @param #SCORING self +-- @param Wrapper.Group#GROUP ScoreGroup The @{Group} for which each @{Unit} a Score is given. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddScoreGroup( ScoreGroup, Score ) + + local ScoreUnits = ScoreGroup:GetUnits() + + for ScoreUnitID, ScoreUnit in pairs( ScoreUnits ) do + local UnitName = ScoreUnit:GetName() + self.ScoringObjects[UnitName] = Score end - return true + return self +end + +--- Add a @{Zone} to define additional scoring when any object is destroyed in that zone. +-- Note that if a @{Zone} with the same name is already within the scoring added, the @{Zone} (type) and Score will be replaced! +-- This allows for a dynamic destruction zone evolution within your mission. +-- @param #SCORING self +-- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. +-- Note that a zone can be a polygon or a moving zone. +-- @param #number Score The Score value. +-- @return #SCORING +function SCORING:AddZoneScore( ScoreZone, Score ) + + local ZoneName = ScoreZone:GetName() + + self.ScoringZones[ZoneName] = {} + self.ScoringZones[ZoneName].ScoreZone = ScoreZone + self.ScoringZones[ZoneName].Score = Score + + return self +end + +--- Remove a @{Zone} for additional scoring. +-- The scoring will search if any @{Zone} is added with the given name, and will remove that zone from the scoring. +-- This allows for a dynamic destruction zone evolution within your mission. +-- @param #SCORING self +-- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. +-- Note that a zone can be a polygon or a moving zone. +-- @return #SCORING +function SCORING:RemoveZoneScore( ScoreZone ) + + local ZoneName = ScoreZone:GetName() + + self.ScoringZones[ZoneName] = nil + + return self end +--- Configure to send messages after a target has been hit. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesHit( OnOff ) + + self.MessagesHit = OnOff + return self +end + +--- If to send messages after a target has been hit. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesHit() + + return self.MessagesHit +end + +--- Configure to send messages after a target has been destroyed. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesDestroy( OnOff ) + + self.MessagesDestroy = OnOff + return self +end + +--- If to send messages after a target has been destroyed. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesDestroy() + + return self.MessagesDestroy +end + +--- Configure to send messages after a target has been destroyed and receives additional scores. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesScore( OnOff ) + + self.MessagesScore = OnOff + return self +end + +--- If to send messages after a target has been destroyed and receives additional scores. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesScore() + + return self.MessagesScore +end + +--- Configure to send messages after a target has been hit in a zone, and additional score is received. +-- @param #SCORING self +-- @param #boolean OnOff If true is given, the messages are sent. +-- @return #SCORING +function SCORING:SetMessagesZone( OnOff ) + + self.MessagesZone = OnOff + return self +end + +--- If to send messages after a target has been hit in a zone, and additional score is received. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesZone() + + return self.MessagesZone +end + +--- Configure to send messages to all players. +-- @param #SCORING self +-- @return #SCORING +function SCORING:SetMessagesToAll() + + self.MessagesAudience = 1 + return self +end + +--- If to send messages to all players. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesToAll() + + return self.MessagesAudience == 1 +end + +--- Configure to send messages to only those players within the same coalition as the player. +-- @param #SCORING self +-- @return #SCORING +function SCORING:SetMessagesToCoalition() + + self.MessagesAudience = 2 + return self +end + +--- If to send messages to only those players within the same coalition as the player. +-- @param #SCORING self +-- @return #boolean +function SCORING:IfMessagesToCoalition() + + return self.MessagesAudience == 2 +end + + +--- When a player commits too much damage to friendlies, his penalty score will reach a certain level. +-- Use this method to define the level when a player gets kicked. +-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. +-- @param #SCORING self +-- @param #number Fratricide The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked. +-- @return #SCORING +function SCORING:SetFratricide( Fratricide ) + + self.Fratricide = Fratricide + return self +end + +--- When a player changes the coalition, he can receive a penalty score. +-- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. +-- By default, the penalty for changing coalition is the default penalty multiplier. +-- @param #SCORING self +-- @param #number CoalitionChangePenalty The amount of penalty that is given. +-- @return #SCORING +function SCORING:SetCoalitionChangePenalty( CoalitionChangePenalty ) + + self.CoalitionChangePenalty = CoalitionChangePenalty + return self +end --- Add a new player entering a Unit. @@ -17882,12 +18386,12 @@ function SCORING:_AddPlayerFromUnit( UnitData ) 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].Destroy = {} self.Players[PlayerName].Mission = {} -- for CategoryID, CategoryName in pairs( SCORINGCategory ) do -- self.Players[PlayerName].Hit[CategoryID] = {} - -- self.Players[PlayerName].Kill[CategoryID] = {} + -- self.Players[PlayerName].Destroy[CategoryID] = {} -- end self.Players[PlayerName].HitPlayers = {} self.Players[PlayerName].Score = 0 @@ -17916,7 +18420,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) self.Players[PlayerName].UnitType = UnitTypeName self.Players[PlayerName].UNIT = UnitData - if self.Players[PlayerName].Penalty > 100 then + if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 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 @@ -17925,7 +18429,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) end end - if self.Players[PlayerName].Penalty > 150 then + if self.Players[PlayerName].Penalty > self.Fratricide then local ClientGroup = GROUP:NewFromDCSUnit( UnitData ) ClientGroup:Destroy() MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", @@ -18005,6 +18509,34 @@ function SCORING:_AddMissionScore( Mission, Text, Score ) end end + +--- Handles the OnPlayerEnterUnit event for the scoring. +-- @param #SCORING self +-- @param Core.Event#EVENTDATA Event +function SCORING:OnEventPlayerEnterUnit( Event ) + if Event.IniUnit then + self:_AddPlayerFromUnit( Event.IniUnit ) + local Menu = MENU_GROUP:New( Event.IniGroup, 'Scoring' ) + local ReportGroupSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report players in group', Menu, SCORING.ReportScoreGroupSummary, self, Event.IniGroup ) + local ReportGroupDetailed = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Detailed report players in group', Menu, SCORING.ReportScoreGroupDetailed, self, Event.IniGroup ) + local ReportToAllSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report all players', Menu, SCORING.ReportScoreAllSummary, self, Event.IniGroup ) + self:SetState( Event.IniUnit, "ScoringMenu", Menu ) + end +end + +--- Handles the OnPlayerLeaveUnit event for the scoring. +-- @param #SCORING self +-- @param Core.Event#EVENTDATA Event +function SCORING:OnEventPlayerLeaveUnit( Event ) + if Event.IniUnit then + local Menu = self:GetState( Event.IniUnit, "ScoringMenu" ) -- Core.Menu#MENU_GROUP + if Menu then + Menu:Remove() + end + end +end + + --- Handles the OnHit event for the scoring. -- @param #SCORING self -- @param Core.Event#EVENTDATA Event @@ -18048,11 +18580,12 @@ function SCORING:_EventOnHit( Event ) InitGroupName = Event.IniDCSGroupName InitPlayerName = Event.IniPlayerName - InitCoalition = InitUnit:getCoalition() + InitCoalition = Event.IniCoalition --TODO: Workaround Client DCS Bug --InitCategory = InitUnit:getCategory() - InitCategory = InitUnit:getDesc().category - InitType = InitUnit:getTypeName() + --InitCategory = InitUnit:getDesc().category + InitCategory = Event.IniCategory + InitType = Event.IniTypeName InitUnitCoalition = _SCORINGCoalition[InitCoalition] InitUnitCategory = _SCORINGCategory[InitCategory] @@ -18071,11 +18604,12 @@ function SCORING:_EventOnHit( Event ) TargetGroupName = Event.TgtDCSGroupName TargetPlayerName = Event.TgtPlayerName - TargetCoalition = TargetUnit:getCoalition() + TargetCoalition = Event.TgtCoalition --TODO: Workaround Client DCS Bug --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnit:getDesc().category - TargetType = TargetUnit:getTypeName() + --TargetCategory = TargetUnit:getDesc().category + TargetCategory = Event.TgtCategory + TargetType = Event.TgtTypeName TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] TargetUnitCategory = _SCORINGCategory[TargetCategory] @@ -18125,43 +18659,66 @@ function SCORING:_EventOnHit( Event ) end local Score = 0 - if InitCoalition == TargetCoalition then - Player.Penalty = Player.Penalty + 10 - PlayerHit.Penalty = PlayerHit.Penalty + 10 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 - - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.PenaltyHit .. " times. Penalty: -" .. PlayerHit.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() + + if InitCoalition then -- A coalition object was hit. + if InitCoalition == TargetCoalition then + Player.Penalty = Player.Penalty + 10 + PlayerHit.Penalty = PlayerHit.Penalty + 10 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 + + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. + "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + else + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit a friendly target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. + "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + end + self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.PenaltyHit .. " times. Penalty: -" .. PlayerHit.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() + Player.Score = Player.Score + 1 + PlayerHit.Score = PlayerHit.Score + 1 + PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. + "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + else + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit an enemy target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. + "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + end + self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) end - self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - Player.Score = Player.Score + 1 - PlayerHit.Score = PlayerHit.Score + 1 - PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.ScoreHit .. " times. Score: " .. PlayerHit.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() - else - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerHit.ScoreHit .. " times. Score: " .. PlayerHit.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ):ToAll() - end - self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else -- A scenery object was hit. + MESSAGE + :New( "Player '" .. InitPlayerName .. "' hit a scenery object.", + 2 + ) + :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) + self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end end end @@ -18197,10 +18754,11 @@ function SCORING:_EventOnDeadOrCrash( Event ) TargetGroupName = Event.IniDCSGroupName TargetPlayerName = Event.IniPlayerName - TargetCoalition = TargetUnit:getCoalition() + TargetCoalition = Event.IniCoalition --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnit:getDesc().category -- Workaround - TargetType = TargetUnit:getTypeName() + --TargetCategory = TargetUnit:getDesc().category -- Workaround + TargetCategory = Event.IniCategory + TargetType = Event.IniTypeName TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] TargetUnitCategory = _SCORINGCategory[TargetCategory] @@ -18212,7 +18770,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) -- Player contains the score and reference data for the player. for PlayerName, Player in pairs( self.Players ) do if Player then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got killed" ) + self:T( "Something got destroyed" ) -- Some variables local InitUnitName = Player.UnitName @@ -18224,67 +18782,136 @@ function SCORING:_EventOnDeadOrCrash( Event ) self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - -- What is he hitting? - if TargetCategory then - if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? + -- What is the player destroying? + if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? - - Player.Kill[TargetCategory] = Player.Kill[TargetCategory] or {} - Player.Kill[TargetCategory][TargetType] = Player.Kill[TargetCategory][TargetType] or {} + Player.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} + Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} - -- PlayerKill contains the kill score data per category and target type of the player. - local PlayerKill = Player.Kill[TargetCategory][TargetType] - Player.Kill[TargetCategory][TargetType] = {} - PlayerKill.Score = PlayerKill.Score or 0 - PlayerKill.ScoreKill = PlayerKill.ScoreKill or 0 - PlayerKill.Penalty = PlayerKill.Penalty or 0 - PlayerKill.PenaltyKill = PlayerKill.PenaltyKill or 0 - PlayerKill.UNIT = PlayerKill.UNIT or Player.Hit[TargetCategory][TargetUnitName].UNIT - + -- PlayerDestroy contains the destroy score data per category and target type of the player. + local TargetDestroy = Player.Destroy[TargetCategory][TargetType] + TargetDestroy.Score = TargetDestroy.Score or 0 + TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy or 0 + TargetDestroy.Penalty = TargetDestroy.Penalty or 0 + TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy or 0 + TargetDestroy.UNIT = TargetDestroy.UNIT or Player.Hit[TargetCategory][TargetUnitName].UNIT + + if TargetCoalition then if InitCoalition == TargetCoalition then - local ThreatLevelTarget, ThreatTypeTarget = PlayerKill.UNIT:GetThreatLevel() + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() - local ThreatLevel = math.ceil( ThreatLevelTarget / ThreatLevelPlayer * 100 ) - self:E( { ThreatLevel = ThreatLevel, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) + local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.MultiplierDestroyPenalty ) + self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - Player.Penalty = Player.Penalty + ThreatLevel * 4 - PlayerKill.Penalty = PlayerKill.Penalty + ThreatLevel * 4 - PlayerKill.PenaltyKill = PlayerKill.PenaltyKill + 1 + Player.Penalty = Player.Penalty + ThreatPenalty + TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty + TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 - if Player.HitPlayers[TargetPlayerName] then -- A player killed another player - MESSAGE:New( "Player '" .. PlayerName .. "' killed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.PenaltyKill .. " times. Penalty: -" .. PlayerKill.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. + "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) else - MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.PenaltyKill .. " times. Penalty: -" .. PlayerKill.Penalty .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed a friendly target " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. + "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) end - self:ScoreCSV( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + self:ScoreCSV( PlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) else - - local ThreatLevelTarget, ThreatTypeTarget = PlayerKill.UNIT:GetThreatLevel() + + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() - local ThreatLevel = math.ceil( ThreatLevelTarget / ThreatLevelPlayer * 100 ) - self:E( { ThreatLevel = ThreatLevel, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - - Player.Score = Player.Score + ThreatLevel - PlayerKill.Score = PlayerKill.Score + ThreatLevel - PlayerKill.ScoreKill = PlayerKill.ScoreKill + 1 - if Player.HitPlayers[TargetPlayerName] then -- A player killed another player - MESSAGE:New( "Player '" .. PlayerName .. "' killed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.ScoreKill .. " times. Score: " .. PlayerKill.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.MultiplierDestroyScore ) + self:E( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) + + Player.Score = Player.Score + ThreatScore + TargetDestroy.Score = TargetDestroy.Score + ThreatScore + TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1 + if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. + "Score: " .. TargetDestroy.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) else - MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. - PlayerKill.ScoreKill .. " times. Score: " .. PlayerKill.Score .. - ". Score Total:" .. Player.Score - Player.Penalty, - 5 ):ToAll() + MESSAGE + :New( "Player '" .. PlayerName .. "' destroyed an enemy " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. + "Score: " .. TargetDestroy.Score .. ". Total:" .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) + end + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + + local UnitName = TargetDestroy.UNIT:GetName() + local Score = self.ScoringObjects[UnitName] + if Score then + Player.Score = Player.Score + Score + TargetDestroy.Score = TargetDestroy.Score + Score + MESSAGE + :New( "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " .. + "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + + -- Check if there are Zones where the destruction happened. + for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do + self:E( { ScoringZone = ScoreZoneData } ) + local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE + local Score = ScoreZoneData.Score + if ScoreZone:IsVec2InZone( TargetDestroy.UNIT:GetVec2() ) then + Player.Score = Player.Score + Score + TargetDestroy.Score = TargetDestroy.Score + Score + MESSAGE + :New( "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." .. + "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. + "Total: " .. Player.Score - Player.Penalty, + 15 ) + :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + end + end + + end + else + -- Check if there are Zones where the destruction happened. + for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do + self:E( { ScoringZone = ScoreZoneData } ) + local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE + local Score = ScoreZoneData.Score + if ScoreZone:IsVec2InZone( TargetDestroy.UNIT:GetVec2() ) then + Player.Score = Player.Score + Score + TargetDestroy.Score = TargetDestroy.Score + Score + MESSAGE + :New( "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." .. + "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. + "Total: " .. Player.Score - Player.Penalty, + 15 + ) + :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) + :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) + self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) end - self:ScoreCSV( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) end end end @@ -18293,229 +18920,324 @@ function SCORING:_EventOnDeadOrCrash( Event ) end -function SCORING:ReportScoreAll() - - env.info( "Hello World " ) +--- Produce detailed report of player hit scores. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerHits( PlayerName ) local ScoreMessage = "" - local PlayerMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 - self:T( "Score Report" ) + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - 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 ) + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = ":\n" - - local ScoreMessageHits = "" - - for CategoryID, CategoryName in pairs( _SCORINGCategory ) 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 ) + local ScoreMessageHits = "" + for CategoryID, CategoryName in pairs( _SCORINGCategory ) do + self:T( CategoryName ) + if PlayerData.Hit[CategoryID] then + self:T( "Hit scores exist for player " .. PlayerName ) + local Score = 0 + local ScoreHit = 0 + local Penalty = 0 + local PenaltyHit = 0 + 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 - if ScoreMessageHits ~= "" then - ScoreMessage = ScoreMessage .. " Hits: " .. ScoreMessageHits .. "\n" - end - - local ScoreMessageKills = "" - for CategoryID, CategoryName in pairs( _SCORINGCategory ) 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 + if ScoreMessageHits ~= "" then + ScoreMessage = "Hits: " .. ScoreMessageHits end end - MESSAGE:New( PlayerMessage, 30, "Player Scores" ):ToAll() + + return ScoreMessage, PlayerScore, PlayerPenalty end -function SCORING:ReportScorePlayer() - - env.info( "Hello World " ) +--- Produce detailed report of player destroy scores. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerDestroys( PlayerName ) local ScoreMessage = "" - local PlayerMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 - self:T( "Score Report" ) + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) - 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 ) + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName + local ScoreMessageDestroys = "" + for CategoryID, CategoryName in pairs( _SCORINGCategory ) do + if PlayerData.Destroy[CategoryID] then + self:T( "Destroy scores exist for player " .. PlayerName ) + local Score = 0 + local ScoreDestroy = 0 + local Penalty = 0 + local PenaltyDestroy = 0 - local PlayerScore = 0 - local PlayerPenalty = 0 - - ScoreMessage = "" - - local ScoreMessageHits = "" - - for CategoryID, CategoryName in pairs( _SCORINGCategory ) 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 + for UnitName, UnitData in pairs( PlayerData.Destroy[CategoryID] ) do + self:E( { UnitData = UnitData } ) + if UnitData ~= {} then Score = Score + UnitData.Score - ScoreHit = ScoreHit + UnitData.ScoreHit + ScoreDestroy = ScoreDestroy + UnitData.ScoreDestroy Penalty = Penalty + UnitData.Penalty - PenaltyHit = UnitData.PenaltyHit + PenaltyDestroy = PenaltyDestroy + UnitData.PenaltyDestroy 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( _SCORINGCategory ) 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 + local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageDestroy ) + ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy - 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 + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) 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 + if ScoreMessageDestroys ~= "" then + ScoreMessage = "Destroys: " .. ScoreMessageDestroys + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + +--- Produce detailed report of player penalty scores because of changing the coalition. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerCoalitionChanges( PlayerName ) + + local ScoreMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 + + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) + + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName + + 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 + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + +--- Produce detailed report of player penalty scores because of changing the coalition. +-- @param #SCORING self +-- @param #string PlayerName The name of the player. +-- @return #string The report. +function SCORING:ReportDetailedPlayerMissions( PlayerName ) + + local ScoreMessage = "" + local PlayerScore = 0 + local PlayerPenalty = 0 + + local PlayerData = self.Players[PlayerName] + if PlayerData then -- This should normally not happen, but i'll test it anyway. + self:T( "Score Player: " .. PlayerName ) + + -- Some variables + local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] + local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] + local InitUnitType = PlayerData.UnitType + local InitUnitName = PlayerData.UnitName + + 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 + + if ScoreMessageMission ~= "" then + ScoreMessage = ScoreMessage .. "Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")" + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + + +--- Report Group Score Summary +-- @param #SCORING self +-- @param Wrapper.Group#GROUP PlayerGroup The player group. +function SCORING:ReportScoreGroupSummary( PlayerGroup ) + + local PlayerMessage = "" + + self:T( "Report Score Group Summary" ) + + local PlayerUnits = PlayerGroup:GetUnits() + for UnitID, PlayerUnit in pairs( PlayerUnits ) do + local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + + if PlayerName then + + local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) + ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits + self:E( { ReportHits, ScoreHits, PenaltyHits } ) + local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) + ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys + self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) + local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) + ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges + self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty + ) + MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) + end + end + +end + +--- Report Group Score Detailed +-- @param #SCORING self +-- @param Wrapper.Group#GROUP PlayerGroup The player group. +function SCORING:ReportScoreGroupDetailed( PlayerGroup ) + + local PlayerMessage = "" + + self:T( "Report Score Group Detailed" ) + + local PlayerUnits = PlayerGroup:GetUnits() + for UnitID, PlayerUnit in pairs( PlayerUnits ) do + local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + + if PlayerName then + + local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) + ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits + self:E( { ReportHits, ScoreHits, PenaltyHits } ) + local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) + ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys + self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) + local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) + ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges + self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty, + ReportHits, + ReportDestroys, + ReportCoalitionChanges, + ReportMissions + ) + MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) + end + end + +end + +--- Report all players score +-- @param #SCORING self +-- @param Wrapper.Group#GROUP PlayerGroup The player group. +function SCORING:ReportScoreAllSummary( PlayerGroup ) + + local PlayerMessage = "" + + self:T( "Report Score All Players" ) + + for PlayerName, PlayerData in pairs( self.Players ) do + + if PlayerName then + + local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) + ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits + self:E( { ReportHits, ScoreHits, PenaltyHits } ) + local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) + ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys + self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) + local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) + ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges + self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty + ) + MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) end end - MESSAGE:New( PlayerMessage, 30, "Player Scores" ):ToAll() end @@ -18616,21 +19338,10 @@ function SCORING:ScoreCSV( PlayerName, ScoreType, ScoreTimes, ScoreAmount, Playe 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 + TargetUnitCoalition = TargetUnitCoalition or "" + TargetUnitCategory = TargetUnitCategory or "" + TargetUnitType = TargetUnitType or "" + TargetUnitName = TargetUnitName or "" if lfs and io and os then self.CSVFile:write( @@ -32290,6 +33001,7 @@ Include.File( "Wrapper/Unit" ) Include.File( "Wrapper/Client" ) Include.File( "Wrapper/Static" ) Include.File( "Wrapper/Airbase" ) +Include.File( "Wrapper/Scenery" ) --- Functional Classes Include.File( "Functional/Scoring" ) diff --git a/Moose Mission Setup/Moose_Create.bat b/Moose Mission Setup/Moose_Create.bat index ea27cb4f9..4e3a251d4 100644 --- a/Moose Mission Setup/Moose_Create.bat +++ b/Moose Mission Setup/Moose_Create.bat @@ -65,6 +65,7 @@ COPY /b Moose.lua + %1\Wrapper\Unit.lua Moose.lua COPY /b Moose.lua + %1\Wrapper\Client.lua Moose.lua COPY /b Moose.lua + %1\Wrapper\Static.lua Moose.lua COPY /b Moose.lua + %1\Wrapper\Airbase.lua Moose.lua +COPY /b Moose.lua + %1\Wrapper\Scenery.lua Moose.lua rem Functional Classes COPY /b Moose.lua + %1\Functional\Scoring.lua Moose.lua diff --git a/Moose Presentations/LED Score Board.png b/Moose Presentations/LED Score Board.png new file mode 100644 index 000000000..673d1c74b Binary files /dev/null and b/Moose Presentations/LED Score Board.png differ diff --git a/Moose Presentations/SCORING.pptx b/Moose Presentations/SCORING.pptx new file mode 100644 index 000000000..35021c0c8 Binary files /dev/null and b/Moose Presentations/SCORING.pptx differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua index 7c2f7a85e..256715d4d 100644 --- a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua +++ b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua @@ -18,4 +18,24 @@ local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) local Scoring = SCORING:New( "Detect Demo" ) +Scoring:SetMultiplierDestroyScore( 10 ) + +Scoring:SetMultiplierDestroyPenalty( 40 ) + +Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) + +-- Test for zone scores. +-- First declare the zone objects. + +-- This one is to test scoring on normal units. +local ShootingRangeZone = ZONE:New( "ScoringZone1" ) + +-- This one is to test scoring on scenery. +-- Note that you can only destroy scenery with heavy weapons. +local SceneryZone = ZONE:New( "ScoringZone2" ) + +-- We add the zones to the scoring object, to add points when one of the objects are hit within the zone. +Scoring:AddZoneScore( ShootingRangeZone, 200 ) +Scoring:AddZoneScore( SceneryZone, 200 ) + diff --git a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz index 3889bddfc..405d18c6c 100644 Binary files a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz and b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz index f91a56ba3..582a56b5b 100644 Binary files a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz and b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz differ diff --git a/docs/Documentation/AI_Balancer.html b/docs/Documentation/AI_Balancer.html index 7b1a36cfe..4a168b45f 100644 --- a/docs/Documentation/AI_Balancer.html +++ b/docs/Documentation/AI_Balancer.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -71,7 +72,7 @@

    Module AI_Balancer

    -

    Single-Player:No / Mulit-Player:Yes / AI:Yes / Human:No / Types:All -- AI Balancing will replace in multi player missions +

    Single-Player:No / Multi-Player:Yes / AI:Yes / Human:No / Types:All -- AI Balancing will replace in multi player missions non-occupied human slots with AI groups, in order to provide an engaging simulation environment, even when there are hardly any players in the mission.

    diff --git a/docs/Documentation/AI_Cap.html b/docs/Documentation/AI_Cap.html index ec34c1a56..8751f3703 100644 --- a/docs/Documentation/AI_Cap.html +++ b/docs/Documentation/AI_Cap.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -71,7 +72,7 @@

    Module AI_Cap

    -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:Air -- Execute Combat Air Patrol (CAP).

    +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- Execute Combat Air Patrol (CAP).

    Banner Image

    diff --git a/docs/Documentation/AI_Cas.html b/docs/Documentation/AI_Cas.html index a18c40a16..d40e0f8d1 100644 --- a/docs/Documentation/AI_Cas.html +++ b/docs/Documentation/AI_Cas.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -71,7 +72,7 @@

    Module AI_Cas

    -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:Air -- +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- Provide Close Air Support to friendly ground troops.

    Banner Image

    @@ -307,7 +308,7 @@ It can be notified to go RTB through the RTB event.

    - AI_CAS_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType, EngageZone) + AI_CAS_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType)

    Creates a new AICASZONE object

    @@ -706,7 +707,7 @@ It can be notified to go RTB through the RTB event.

    -AI_CAS_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType, EngageZone) +AI_CAS_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType)
    @@ -747,13 +748,14 @@ The maximum speed of the Controllable in km/h.
  • -

    Dcs.DCSTypes#AltitudeType PatrolAltType : -The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO

    +

    Core.Zone#ZONE_BASE EngageZone : +The zone where the engage will happen.

  • -

    Core.Zone#ZONE EngageZone :

    +

    Dcs.DCSTypes#AltitudeType PatrolAltType : +The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO

  • diff --git a/docs/Documentation/AI_Patrol.html b/docs/Documentation/AI_Patrol.html index c2d613229..51fe4286e 100644 --- a/docs/Documentation/AI_Patrol.html +++ b/docs/Documentation/AI_Patrol.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -71,7 +72,7 @@

    Module AI_Patrol

    -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:Air -- +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- Air Patrolling or Staging.

    Banner Image

    @@ -880,6 +881,9 @@ Use the method AIPATROLZONE.M + +

    This table contains the targets detected during patrol.

    +
    diff --git a/docs/Documentation/Account.html b/docs/Documentation/Account.html index 5b145b1df..ca5687216 100644 --- a/docs/Documentation/Account.html +++ b/docs/Documentation/Account.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Airbase.html b/docs/Documentation/Airbase.html index 65dbb7cab..b1cb49a7f 100644 --- a/docs/Documentation/Airbase.html +++ b/docs/Documentation/Airbase.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/AirbasePolice.html b/docs/Documentation/AirbasePolice.html index 7abdaf4aa..e80898903 100644 --- a/docs/Documentation/AirbasePolice.html +++ b/docs/Documentation/AirbasePolice.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Assign.html b/docs/Documentation/Assign.html index 93bdb60ae..fd741ec8a 100644 --- a/docs/Documentation/Assign.html +++ b/docs/Documentation/Assign.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Base.html b/docs/Documentation/Base.html index 39c311970..6f2cc7de4 100644 --- a/docs/Documentation/Base.html +++ b/docs/Documentation/Base.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Cargo.html b/docs/Documentation/Cargo.html index fb98d54a5..59202c294 100644 --- a/docs/Documentation/Cargo.html +++ b/docs/Documentation/Cargo.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -71,7 +72,7 @@

    Module Cargo

    -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:Ground --
    +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Ground --
    Management of logical cargo objects, that can be transported from and to transportation carriers.

    Banner Image

    diff --git a/docs/Documentation/CleanUp.html b/docs/Documentation/CleanUp.html index 41e24b2d7..0fc27c9c6 100644 --- a/docs/Documentation/CleanUp.html +++ b/docs/Documentation/CleanUp.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Client.html b/docs/Documentation/Client.html index ebcef655e..799a07533 100644 --- a/docs/Documentation/Client.html +++ b/docs/Documentation/Client.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/CommandCenter.html b/docs/Documentation/CommandCenter.html index 23bc8a52b..2dddeca01 100644 --- a/docs/Documentation/CommandCenter.html +++ b/docs/Documentation/CommandCenter.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Controllable.html b/docs/Documentation/Controllable.html index c9540f79e..e400ce576 100644 --- a/docs/Documentation/Controllable.html +++ b/docs/Documentation/Controllable.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Database.html b/docs/Documentation/Database.html index 82e423ee5..be41b649d 100644 --- a/docs/Documentation/Database.html +++ b/docs/Documentation/Database.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Detection.html b/docs/Documentation/Detection.html index f53ea7c43..4c3e89eab 100644 --- a/docs/Documentation/Detection.html +++ b/docs/Documentation/Detection.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/DetectionManager.html b/docs/Documentation/DetectionManager.html index 95ed04d86..0c48152fb 100644 --- a/docs/Documentation/DetectionManager.html +++ b/docs/Documentation/DetectionManager.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Escort.html b/docs/Documentation/Escort.html index f3d622cfe..e2da40c9e 100644 --- a/docs/Documentation/Escort.html +++ b/docs/Documentation/Escort.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Event.html b/docs/Documentation/Event.html index 79c16f3a2..791e6382f 100644 --- a/docs/Documentation/Event.html +++ b/docs/Documentation/Event.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -115,7 +116,7 @@ in the correct processing order.

    Objects

    -

    For most DCS events, the above order of updating will be followed.1

    +

    For most DCS events, the above order of updating will be followed.

    Objects

    @@ -206,6 +207,23 @@ There are basically 4 main categories of information stored in the EVENTDATA str

    Objects

    +

    IMPORTANT NOTE: Some events can involve not just UNIT objects, but also STATIC objects!!! +In that case the initiator or target unit fields will refer to a STATIC object! +In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated. +The fields IniObjectCategory and TgtObjectCategory contain the indicator which kind of object is involved in the event. +You can use the enumerator Object.Category.UNIT and Object.Category.STATIC to check on IniObjectCategory and TgtObjectCategory. +Example code snippet:

    + +
     if Event.IniObjectCategory == Object.Category.UNIT then
    +  ...
    + end
    + if Event.IniObjectCategory == Object.Category.STATIC then
    +  ...
    + end 
    +
    + +

    When a static object is involved in the event, the Group and Player fields won't be populated.

    +

    API CHANGE HISTORY

    @@ -571,81 +589,182 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    Type EVENTDATA

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -669,25 +788,28 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    EVENTDATA.IniCategory +
      (UNIT) The category of the initiator.
    +
    +
    EVENTDATA.IniCoalition +
     (UNIT) The coalition of the initiator.
    +
    +
    EVENTDATA.IniDCSGroup - +
      (UNIT) The initiating {Dcs.DCSGroup#Group}.
    +
    EVENTDATA.IniDCSGroupName - +

    (UNIT) The initiating Group name.

    EVENTDATA.IniDCSUnit - +
       (UNIT/STATIC) The initiating <a href="Dcs.DCSUnit.html##(Unit)">Dcs.DCSUnit#Unit</a> or <a href="Dcs.DCSStaticObject.html##(StaticObject)">Dcs.DCSStaticObject#StaticObject</a>.
    +
    EVENTDATA.IniDCSUnitName - +

    (UNIT/STATIC) The initiating Unit name.

    EVENTDATA.IniGroup +
         (UNIT) The initiating MOOSE wrapper <a href="Wrapper.Group.html##(GROUP)">Wrapper.Group#GROUP</a> of the initiator Group object.
    +
    +
    EVENTDATA.IniGroupName +
     (UNIT) The initiating GROUP name (same as IniDCSGroupName).
    +
    +
    EVENTDATA.IniObjectCategory +

    (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ).

    +
    EVENTDATA.IniPlayerName +
    (UNIT) The name of the initiating player in case the Unit is a client or player slot.
    +
    +
    EVENTDATA.IniTypeName +
      (UNIT) The type name of the initiator.
    +
    EVENTDATA.IniUnit - +
          (UNIT/STATIC) The initiating MOOSE wrapper <a href="Wrapper.Unit.html##(UNIT)">Wrapper.Unit#UNIT</a> of the initiator Unit object.
    +
    EVENTDATA.IniUnitName - +
      (UNIT/STATIC) The initiating UNIT name (same as IniDCSUnitName).
    +
    +
    EVENTDATA.TgtCategory +
      (UNIT) The category of the target.
    +
    +
    EVENTDATA.TgtCoalition +
     (UNIT) The coalition of the target.
    +
    EVENTDATA.TgtDCSGroup - +
      (UNIT) The target {Dcs.DCSGroup#Group}.
    +
    EVENTDATA.TgtDCSGroupName - +

    (UNIT) The target Group name.

    EVENTDATA.TgtDCSUnit - +
       (UNIT/STATIC) The target <a href="Dcs.DCSUnit.html##(Unit)">Dcs.DCSUnit#Unit</a> or <a href="Dcs.DCSStaticObject.html##(StaticObject)">Dcs.DCSStaticObject#StaticObject</a>.
    +
    EVENTDATA.TgtDCSUnitName +

    (UNIT/STATIC) The target Unit name.

    +
    EVENTDATA.TgtGroup +
         (UNIT) The target MOOSE wrapper <a href="Wrapper.Group.html##(GROUP)">Wrapper.Group#GROUP</a> of the target Group object.
    +
    +
    EVENTDATA.TgtGroupName +
     (UNIT) The target GROUP name (same as TgtDCSGroupName).
    +
    +
    EVENTDATA.TgtObjectCategory +
      (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ).
    +
    +
    EVENTDATA.TgtPlayerName +
    (UNIT) The name of the target player in case the Unit is a client or player slot.
    +
    +
    EVENTDATA.TgtTypeName +
      (UNIT) The type name of the target.
    +
    EVENTDATA.TgtUnit - +
          (UNIT/STATIC) The target MOOSE wrapper <a href="Wrapper.Unit.html##(UNIT)">Wrapper.Unit#UNIT</a> of the target Unit object.
    +
    EVENTDATA.TgtUnitName - +
      (UNIT/STATIC) The target UNIT name (same as TgtDCSUnitName).
    +
    EVENTDATA.id +

    The identifier of the event.

    EVENTDATA.initiator - +
        (UNIT/STATIC/SCENERY) The initiating <a href="Dcs.DCSUnit.html##(Unit)">Dcs.DCSUnit#Unit</a> or <a href="Dcs.DCSStaticObject.html##(StaticObject)">Dcs.DCSStaticObject#StaticObject</a>.
    +
    EVENTDATA.target - +
           (UNIT/STATIC) The target <a href="Dcs.DCSUnit.html##(Unit)">Dcs.DCSUnit#Unit</a> or <a href="Dcs.DCSStaticObject.html##(StaticObject)">Dcs.DCSStaticObject#StaticObject</a>.
    +
    EVENTDATA.weapon - +

    The weapon used during the event.

    @@ -2438,71 +2560,179 @@ The self instance of the class for which the event is.

    Type EVENTDATA

    -

    The Event structure

    +

    The Event structure +Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event:

    + +
      +
    • A (Object.Category.)UNIT : A UNIT object type is involved in the Event.
    • +
    + + +
      +
    • A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ +
    • +

    Field(s)

    + Dcs.DCSUnit#Unit.Category + +EVENTDATA.IniCategory + +
    +
    + +
      (UNIT) The category of the initiator.
    +
    + +
    +
    +
    +
    + + Dcs.DCScoalition#coalition.side + +EVENTDATA.IniCoalition + +
    +
    + +
     (UNIT) The coalition of the initiator.
    +
    + +
    +
    +
    +
    + + Dcs.DCSGroup#Group EVENTDATA.IniDCSGroup
    - +
      (UNIT) The initiating {Dcs.DCSGroup#Group}.
    +
    + #string EVENTDATA.IniDCSGroupName
    - +

    (UNIT) The initiating Group name.

    + Dcs.DCSUnit#Unit EVENTDATA.IniDCSUnit
    - +
       (UNIT/STATIC) The initiating <a href="Dcs.DCSUnit.html##(Unit)">Dcs.DCSUnit#Unit</a> or <a href="Dcs.DCSStaticObject.html##(StaticObject)">Dcs.DCSStaticObject#StaticObject</a>.
    +
    + #string EVENTDATA.IniDCSUnitName
    - +

    (UNIT/STATIC) The initiating Unit name.

    - + Wrapper.Group#GROUP EVENTDATA.IniGroup
    +
         (UNIT) The initiating MOOSE wrapper <a href="Wrapper.Group.html##(GROUP)">Wrapper.Group#GROUP</a> of the initiator Group object.
    +
    + +
    +
    +
    +
    + + #string + +EVENTDATA.IniGroupName + +
    +
    + +
     (UNIT) The initiating GROUP name (same as IniDCSGroupName).
    +
    + +
    +
    +
    +
    + + Dcs.DCSObject#Object.Category + +EVENTDATA.IniObjectCategory + +
    +
    + +

    (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ).

    + +
    +
    +
    +
    + + #string + +EVENTDATA.IniPlayerName + +
    +
    + +
    (UNIT) The name of the initiating player in case the Unit is a client or player slot.
    +
    + +
    +
    +
    +
    + + #string + +EVENTDATA.IniTypeName + +
    +
    + +
      (UNIT) The type name of the initiator.
    +
    @@ -2517,7 +2747,8 @@ The self instance of the class for which the event is.

    - +
          (UNIT/STATIC) The initiating MOOSE wrapper <a href="Wrapper.Unit.html##(UNIT)">Wrapper.Unit#UNIT</a> of the initiator Unit object.
    +
    @@ -2531,58 +2762,171 @@ The self instance of the class for which the event is.

    - +
      (UNIT/STATIC) The initiating UNIT name (same as IniDCSUnitName).
    +
    + Dcs.DCSUnit#Unit.Category + +EVENTDATA.TgtCategory + +
    +
    + +
      (UNIT) The category of the target.
    +
    + +
    +
    +
    +
    + + Dcs.DCScoalition#coalition.side + +EVENTDATA.TgtCoalition + +
    +
    + +
     (UNIT) The coalition of the target.
    +
    + +
    +
    +
    +
    + + Dcs.DCSGroup#Group EVENTDATA.TgtDCSGroup
    - +
      (UNIT) The target {Dcs.DCSGroup#Group}.
    +
    + #string EVENTDATA.TgtDCSGroupName
    - +

    (UNIT) The target Group name.

    + Dcs.DCSUnit#Unit EVENTDATA.TgtDCSUnit
    - +
       (UNIT/STATIC) The target <a href="Dcs.DCSUnit.html##(Unit)">Dcs.DCSUnit#Unit</a> or <a href="Dcs.DCSStaticObject.html##(StaticObject)">Dcs.DCSStaticObject#StaticObject</a>.
    +
    + #string EVENTDATA.TgtDCSUnitName
    +

    (UNIT/STATIC) The target Unit name.

    + +
    +
    +
    +
    + + Wrapper.Group#GROUP + +EVENTDATA.TgtGroup + +
    +
    + +
         (UNIT) The target MOOSE wrapper <a href="Wrapper.Group.html##(GROUP)">Wrapper.Group#GROUP</a> of the target Group object.
    +
    + +
    +
    +
    +
    + + #string + +EVENTDATA.TgtGroupName + +
    +
    + +
     (UNIT) The target GROUP name (same as TgtDCSGroupName).
    +
    + +
    +
    +
    +
    + + Dcs.DCSObject#Object.Category + +EVENTDATA.TgtObjectCategory + +
    +
    + +
      (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ).
    +
    + +
    +
    +
    +
    + + #string + +EVENTDATA.TgtPlayerName + +
    +
    + +
    (UNIT) The name of the target player in case the Unit is a client or player slot.
    +
    + +
    +
    +
    +
    + + #string + +EVENTDATA.TgtTypeName + +
    +
    + +
      (UNIT) The type name of the target.
    +
    @@ -2597,7 +2941,8 @@ The self instance of the class for which the event is.

    - +
          (UNIT/STATIC) The target MOOSE wrapper <a href="Wrapper.Unit.html##(UNIT)">Wrapper.Unit#UNIT</a> of the target Unit object.
    +
    @@ -2611,7 +2956,8 @@ The self instance of the class for which the event is.

    - +
      (UNIT/STATIC) The target UNIT name (same as TgtDCSUnitName).
    +
    @@ -2657,12 +3003,14 @@ The self instance of the class for which the event is.

    + #number EVENTDATA.id
    +

    The identifier of the event.

    @@ -2670,26 +3018,30 @@ The self instance of the class for which the event is.

    + Dcs.DCSUnit#Unit EVENTDATA.initiator
    - +
        (UNIT/STATIC/SCENERY) The initiating <a href="Dcs.DCSUnit.html##(Unit)">Dcs.DCSUnit#Unit</a> or <a href="Dcs.DCSStaticObject.html##(StaticObject)">Dcs.DCSStaticObject#StaticObject</a>.
    +
    + Dcs.DCSUnit#Unit EVENTDATA.target
    - +
           (UNIT/STATIC) The target <a href="Dcs.DCSUnit.html##(Unit)">Dcs.DCSUnit#Unit</a> or <a href="Dcs.DCSStaticObject.html##(StaticObject)">Dcs.DCSStaticObject#StaticObject</a>.
    +
    @@ -2702,7 +3054,7 @@ The self instance of the class for which the event is.

    - +

    The weapon used during the event.

    diff --git a/docs/Documentation/Fsm.html b/docs/Documentation/Fsm.html index 4992d02fb..0e7e9df7f 100644 --- a/docs/Documentation/Fsm.html +++ b/docs/Documentation/Fsm.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -1505,7 +1506,7 @@ A string defining the start state.

    - #string + FSM._StartState @@ -1804,6 +1805,7 @@ A string defining the start state.

    + FSM.current diff --git a/docs/Documentation/Group.html b/docs/Documentation/Group.html index 519182d0f..73c99a059 100644 --- a/docs/Documentation/Group.html +++ b/docs/Documentation/Group.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Identifiable.html b/docs/Documentation/Identifiable.html index d40418a42..33086e9bf 100644 --- a/docs/Documentation/Identifiable.html +++ b/docs/Documentation/Identifiable.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -164,6 +165,12 @@ IDENTIFIABLE:GetName()

    Returns DCS Identifiable object name.

    + + + + IDENTIFIABLE:GetThreatLevel() + + @@ -398,6 +405,19 @@ The DCS Identifiable is not existing or alive.

    + +
    +
    +
    + + +IDENTIFIABLE:GetThreatLevel() + +
    +
    + + +
    diff --git a/docs/Documentation/MOVEMENT.html b/docs/Documentation/MOVEMENT.html index 8d474972f..aed446e5c 100644 --- a/docs/Documentation/MOVEMENT.html +++ b/docs/Documentation/MOVEMENT.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Menu.html b/docs/Documentation/Menu.html index 07adea583..ddf19fdc5 100644 --- a/docs/Documentation/Menu.html +++ b/docs/Documentation/Menu.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Message.html b/docs/Documentation/Message.html index 87363f12e..35e799486 100644 --- a/docs/Documentation/Message.html +++ b/docs/Documentation/Message.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -133,6 +134,12 @@ To send messages, you need to use the To functions.

    MESSAGE:ToAll()

    Sends a MESSAGE to all players.

    + + + + MESSAGE:ToAllIf(Condition) + +

    Sends a MESSAGE to all players if the given Condition is true.

    @@ -151,6 +158,12 @@ To send messages, you need to use the To functions.

    MESSAGE:ToCoalition(CoalitionSide)

    Sends a MESSAGE to a Coalition.

    + + + + MESSAGE:ToCoalitionIf(CoalitionSide, Condition) + +

    Sends a MESSAGE to a Coalition if the given Condition is true.

    @@ -309,6 +322,32 @@ or MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ) MessageAll:ToAll() + +
    +
    +
    + + +MESSAGE:ToAllIf(Condition) + +
    +
    + +

    Sends a MESSAGE to all players if the given Condition is true.

    + +

    Parameter

    +
      +
    • + +

      Condition :

      + +
    • +
    +

    Return value

    + +

    #MESSAGE:

    + +
    @@ -418,6 +457,38 @@ or MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) MessageRED:ToCoalition( coalition.side.RED ) + +
    +
    +
    + + +MESSAGE:ToCoalitionIf(CoalitionSide, Condition) + +
    +
    + +

    Sends a MESSAGE to a Coalition if the given Condition is true.

    + +

    Parameters

    +
      +
    • + +

      CoalitionSide : +needs to be filled out by the defined structure of the standard scripting engine coalition.side.

      + +
    • +
    • + +

      Condition :

      + +
    • +
    +

    Return value

    + +

    #MESSAGE:

    + +
    diff --git a/docs/Documentation/MissileTrainer.html b/docs/Documentation/MissileTrainer.html index 3a8e89a6a..b51e024ab 100644 --- a/docs/Documentation/MissileTrainer.html +++ b/docs/Documentation/MissileTrainer.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Mission.html b/docs/Documentation/Mission.html index ca0aeff1c..1ad2c406d 100644 --- a/docs/Documentation/Mission.html +++ b/docs/Documentation/Mission.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Object.html b/docs/Documentation/Object.html index cabd59cca..6be6e7aa6 100644 --- a/docs/Documentation/Object.html +++ b/docs/Documentation/Object.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Point.html b/docs/Documentation/Point.html index 576c45b98..426274bd2 100644 --- a/docs/Documentation/Point.html +++ b/docs/Documentation/Point.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Positionable.html b/docs/Documentation/Positionable.html index d5a2d3300..0aeaabf15 100644 --- a/docs/Documentation/Positionable.html +++ b/docs/Documentation/Positionable.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Process_JTAC.html b/docs/Documentation/Process_JTAC.html index 06e2a3e2d..16ebd156c 100644 --- a/docs/Documentation/Process_JTAC.html +++ b/docs/Documentation/Process_JTAC.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Process_Pickup.html b/docs/Documentation/Process_Pickup.html index 385792385..c39875791 100644 --- a/docs/Documentation/Process_Pickup.html +++ b/docs/Documentation/Process_Pickup.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Route.html b/docs/Documentation/Route.html index 375de7d0f..3425dc182 100644 --- a/docs/Documentation/Route.html +++ b/docs/Documentation/Route.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Scenery.html b/docs/Documentation/Scenery.html new file mode 100644 index 000000000..f7258b5b4 --- /dev/null +++ b/docs/Documentation/Scenery.html @@ -0,0 +1,220 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module Scenery

    + +

    This module contains the SCENERY class.

    + + + +

    1) Scenery#SCENERY class, extends Positionable#POSITIONABLE

    +

    Scenery objects are defined on the map. +The Scenery#SCENERY class is a wrapper class to handle the DCS Scenery objects:

    + +
      +
    • Wraps the DCS Scenery objects.
    • +
    • Support all DCS Scenery APIs.
    • +
    • Enhance with Scenery specific APIs not in the DCS API set.
    • +
    + + +

    Global(s)

    + + + + + +
    SCENERY + +
    +

    Type SCENERY

    + + + + + + + + + + + + + + + + + +
    SCENERY.ClassName + +
    SCENERY:GetDCSObject() + +
    SCENERY:GetThreatLevel() + +
    SCENERY:Register(SceneryName, SceneryObject) + +
    + +

    Global(s)

    +
    +
    + + #SCENERY + +SCENERY + +
    +
    + + + +
    +
    +

    Type Scenery

    + +

    Type SCENERY

    + +

    The SCENERY class

    + +

    Field(s)

    +
    +
    + + #string + +SCENERY.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +SCENERY:GetDCSObject() + +
    +
    + + + +
    +
    +
    +
    + + +SCENERY:GetThreatLevel() + +
    +
    + + + +
    +
    +
    +
    + + +SCENERY:Register(SceneryName, SceneryObject) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      SceneryName :

      + +
    • +
    • + +

      SceneryObject :

      + +
    • +
    +
    +
    + +
    + +
    + + diff --git a/docs/Documentation/ScheduleDispatcher.html b/docs/Documentation/ScheduleDispatcher.html index 890b6ca4f..2d0c07db7 100644 --- a/docs/Documentation/ScheduleDispatcher.html +++ b/docs/Documentation/ScheduleDispatcher.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Scheduler.html b/docs/Documentation/Scheduler.html index e4d0d899f..eb654041b 100644 --- a/docs/Documentation/Scheduler.html +++ b/docs/Documentation/Scheduler.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Scoring.html b/docs/Documentation/Scoring.html index 1e707ab2a..9c627b16d 100644 --- a/docs/Documentation/Scoring.html +++ b/docs/Documentation/Scoring.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -71,22 +72,173 @@

    Module Scoring

    -

    Scoring system for MOOSE.

    +

    Single-Player:Yes / Multi-Player:Yes / Core:Yes -- Administer the scoring of player achievements, +and create a CSV file logging the scoring events for use at team or squadron websites.

    + +

    Banner Image

    + +
    + +

    1) Scoring#SCORING class, extends Base#BASE

    + +

    The #SCORING class administers the scoring of player achievements, +and creates a CSV file logging the scoring events for use at team or squadron websites.

    -

    This scoring class calculates the hits and kills that players make within a simulation session. -Scoring is calculated using a defined algorithm. -With a small change in MissionScripting.lua, the scoring can also be logged in a CSV file, that can then be uploaded -to a database or a BI tool to publish the scoring results to the player community.

    + +

    The scores are calculated by scoring the hits and destroys of objects that players make, +which are Unit and Statics** can be defined for which scores are also granted when objects in that Zone are destroyed. +This is specifically useful to designate scenery targets on the map that will generate points when destroyed.

    + +

    With a small change in MissionScripting.lua, the scoring can also be logged in a CSV file.
    +The CSV files can be used to:

    + +
      +
    • Upload scoring to a database or a BI tool to publish the scoring results to the player community.
    • +
    • Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results.
    • +
    • Share scoring amoung players after the mission to discuss mission results.
    • +
    + +

    Scores can be reported. Menu options are automatically added to each group when a player joins a client slot or a CA unit. +Use the radio menu F10 to consult the scores while running the mission. +Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission.

    + +

    1.1) Set the destroy score or penalty multiplier

    + +

    Score multipliers can be set for scores granted when enemies or friendlies are destroyed. +Use the method SCORING.SetMultiplierDestroyScore() to set the multiplier of enemy destroys (positive destroys). +Use the method SCORING.SetMultiplierDestroyPenalty() to set the multiplier of friendly destroys (negative destroys).

    + +
     local Scoring = SCORING:New( "Scoring File" )
    + Scoring:SetMultiplierDestroyScore( 10 )
    + Scoring:SetMultiplierDestroyPenalty( 40 )
    +
    + +

    The above sets the multiplier for valid scores to 10. So scores will be given in a scale from 0 to 10. +The penalties will be given in a scale from 0 to 40.

    + +

    1.2) Define special targets that will give extra scores.

    + +

    Special targets can be set that will give extra scores to the players when these are destroyed. +Use the methods SCORING.AddUnitScore() and SCORING.RemoveUnitScore() to specify a special additional score for a specific Units.
    +Use the methods SCORING.AddStaticScore() and SCORING.RemoveStaticScore() to specify a special additional score for a specific Statics.
    +Use the method SCORING.SetGroupGroup() to specify a special additional score for a specific Groups.

    + +
     local Scoring = SCORING:New( "Scoring File" )
    + Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 )
    + Scoring:AddStaticScore( STATIC:FindByName( "Static #1" ), 100 )
    +
    + +

    The above grants an additional score of 200 points for Unit #001 and an additional 100 points of Static #1 if these are destroyed. +Note that later in the mission, one can remove these scores set, for example, when the a goal achievement time limit is over. +For example, this can be done as follows:

    + +
     Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) )
    +
    + + + +

    1.3) Define destruction zones that will give extra scores.

    + +

    Define zones of destruction. Any object destroyed within the zone of the given category will give extra points. +Use the method SCORING.AddZoneScore() to add a Zone for additional scoring.
    +Use the method SCORING.RemoveZoneScore() to remove a Zone for additional scoring.
    +There are interesting variations that can be achieved with this functionality. For example, if the Zone is a Zone#ZONE_UNIT, +then the zone is a moving zone, and anything destroyed within that Zone will generate points. +The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a Zone, +just large enough around that building.

    + +

    1.4) Configure fratricide level.

    + +

    When a player commits too much damage to friendlies, his penalty score will reach a certain level. +Use the method SCORING.SetFratricide() to define the level when a player gets kicked.
    +By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score.

    + +

    1.5) Penalty score when a player changes the coalition.

    + +

    When a player changes the coalition, he can receive a penalty score. +Use the method SCORING.SetCoalitionChangePenalty() to define the penalty when a player changes coalition. +By default, the penalty for changing coalition is the default penalty multiplier.

    + +

    1.8) Define output CSV files.

    + +

    The CSV file is given the name of the string given in the SCORING.New{} constructor, followed by the .csv extension. +The file is incrementally saved in the **\Saved Games\DCS\Logs** folder, and has a time stamp indicating each mission run. +See the following example:

    + +
    local ScoringFirstMission = SCORING:New( "FirstMission" )
    +local ScoringSecondMission = SCORING:New( "SecondMission" )
    +
    + +

    The above documents that 2 Scoring objects are created, ScoringFirstMission and ScoringSecondMission.

    + +

    1.9) Configure messages.

    + +

    When players hit or destroy targets, messages are sent. +Various methods exist to configure:

    + +
      +
    • Which messages are sent upon the event.
    • +
    • Which audience receives the message.
    • +
    + +

    1.9.1) Configure the messages sent upon the event.

    + +

    Use the following methods to configure when to send messages. By default, all messages are sent.

    + + + +

    1.9.2) Configure the audience of the messages.

    + +

    Use the following methods to configure the audience of the messages. By default, the messages are sent to all players in the mission.

    + + + + +
    + +

    API CHANGE HISTORY

    + +

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    + +
      +
    • Added parts are expressed in bold type face.
    • +
    • Removed parts are expressed in italic type face.
    • +
    + +

    Hereby the change log:

    + +

    2017-02-26: Initial class and API.

    + +
    + +

    AUTHORS and CONTRIBUTIONS

    + +

    Contributions:

    + +
      +
    • Wingthor: Testing & Advice.
    • +
    • Dutch-Baron: Testing & Advice.
    • +
    • **Whisper: Testing.
    • +
    + +

    Authors:

    + +
      +
    • FlightControl: Concept, Design & Programming.
    • +
    +

    Global(s)

    - - - - + + + + @@ -226,6 +233,19 @@ Name of the DCS Static as defined within the Mission Editor.

    + +
    +
    + + +STATIC:GetThreatLevel() + +
    +
    + + +
    diff --git a/docs/Documentation/Task.html b/docs/Documentation/Task.html index 10561a59c..af3bf3c78 100644 --- a/docs/Documentation/Task.html +++ b/docs/Documentation/Task.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Task_A2G.html b/docs/Documentation/Task_A2G.html index 640bb4c66..97aa39bb4 100644 --- a/docs/Documentation/Task_A2G.html +++ b/docs/Documentation/Task_A2G.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Task_PICKUP.html b/docs/Documentation/Task_PICKUP.html index f1cbaadbf..fd8241229 100644 --- a/docs/Documentation/Task_PICKUP.html +++ b/docs/Documentation/Task_PICKUP.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Task_SEAD.html b/docs/Documentation/Task_SEAD.html index 8aac84683..e443fdc31 100644 --- a/docs/Documentation/Task_SEAD.html +++ b/docs/Documentation/Task_SEAD.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Unit.html b/docs/Documentation/Unit.html index 7a731ad48..39cf4d1d7 100644 --- a/docs/Documentation/Unit.html +++ b/docs/Documentation/Unit.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Utils.html b/docs/Documentation/Utils.html index c4f2efc23..33c940e11 100644 --- a/docs/Documentation/Utils.html +++ b/docs/Documentation/Utils.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Zone.html b/docs/Documentation/Zone.html index a1a076180..9e2cce3f3 100644 --- a/docs/Documentation/Zone.html +++ b/docs/Documentation/Zone.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -119,8 +120,8 @@

    1.2) Each zone implements two polymorphic functions defined in Zone#ZONE_BASE:

    1.3) A zone has a probability factor that can be set to randomize a selection between zones:

    @@ -249,9 +250,16 @@ This class implements the inherited functions from
    - + - + @@ -540,7 +548,7 @@ This class implements the inherited functions from ZONE_POLYGON_BASE:IsPointVec2InZone(Vec2) + @@ -616,13 +624,13 @@ This class implements the inherited functions from ZONE_RADIUS:IsPointVec2InZone(Vec2) + - + @@ -1020,8 +1028,8 @@ A value between 0 and 1. 0 = 0% and 1 = 100% probability.

    - -ZONE_BASE:IsPointVec2InZone(Vec2) + +ZONE_BASE:IsVec2InZone(Vec2)
    @@ -1047,8 +1055,8 @@ true if the location is within the zone.

    - -ZONE_BASE:IsPointVec3InZone(Vec3) + +ZONE_BASE:IsVec3InZone(Vec3)
    @@ -1510,8 +1518,8 @@ The Vec2 coordinate.

    - -ZONE_POLYGON_BASE:IsPointVec2InZone(Vec2) + +ZONE_POLYGON_BASE:IsVec2InZone(Vec2)
    @@ -1845,8 +1853,8 @@ The point of the zone.

    - -ZONE_RADIUS:IsPointVec2InZone(Vec2) + +ZONE_RADIUS:IsVec2InZone(Vec2)
    @@ -1872,8 +1880,8 @@ true if the location is within the zone.

    - -ZONE_RADIUS:IsPointVec3InZone(Vec3) + +ZONE_RADIUS:IsVec3InZone(Vec3)
    diff --git a/docs/Documentation/index.html b/docs/Documentation/index.html index bc5b59a13..1f36c0c2d 100644 --- a/docs/Documentation/index.html +++ b/docs/Documentation/index.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -74,7 +75,7 @@
    + + + + @@ -334,7 +341,17 @@ following a given priority.

    @@ -358,7 +375,7 @@ following a given priority.

    ClientGroup - -
    SCORING @@ -115,9 +267,27 @@ to a database or a BI tool to publish the scoring results to the player communit

    Type SCORING

    - + + + + + + + + + + + + + @@ -139,7 +309,97 @@ to a database or a BI tool to publish the scoring results to the player communit - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -148,6 +408,18 @@ to a database or a BI tool to publish the scoring results to the player communit + + + + + + + + @@ -163,15 +435,63 @@ to a database or a BI tool to publish the scoring results to the player communit - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -187,13 +507,19 @@ to a database or a BI tool to publish the scoring results to the player communit - + - + + + + + @@ -202,6 +528,66 @@ to a database or a BI tool to publish the scoring results to the player communit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -232,12 +618,6 @@ to a database or a BI tool to publish the scoring results to the player communit - - - -
    SCORING.AllScoresMenuSCORING:AddScoreGroup(ScoreGroup, Score) - +

    Specify a special additional score for a Group.

    +
    SCORING:AddStaticScore(ScoreStatic, Score) +

    Add a Static for additional scoring when the Static is destroyed.

    +
    SCORING:AddUnitScore(ScoreUnit, Score) +

    Add a Unit for additional scoring when the Unit is destroyed.

    +
    SCORING:AddZoneScore(ScoreZone, Score) +

    Add a Zone to define additional scoring when any object is destroyed in that zone.

    SCORING.MenuSCORING.CoalitionChangePenalty + +
    SCORING.Fratricide + +
    SCORING.GameName + +
    SCORING:IfMessagesDestroy() +

    If to send messages after a target has been destroyed.

    +
    SCORING:IfMessagesHit() +

    If to send messages after a target has been hit.

    +
    SCORING:IfMessagesScore() +

    If to send messages after a target has been destroyed and receives additional scores.

    +
    SCORING:IfMessagesToAll() +

    If to send messages to all players.

    +
    SCORING:IfMessagesToCoalition() +

    If to send messages to only those players within the same coalition as the player.

    +
    SCORING:IfMessagesZone() +

    If to send messages after a target has been hit in a zone, and additional score is received.

    +
    SCORING.MessagesAudience + +
    SCORING.MessagesDestroy + +
    SCORING.MessagesHit + +
    SCORING.MessagesScore + +
    SCORING.MessagesZone + +
    SCORING.MultiplierDestroyPenalty + +
    SCORING.MultiplierDestroyScore SCORING:New(GameName)

    Creates a new SCORING object to administer the scoring achieved by players.

    +
    SCORING:OnEventPlayerEnterUnit(Event) +

    Handles the OnPlayerEnterUnit event for the scoring.

    +
    SCORING:OnEventPlayerLeaveUnit(Event) +

    Handles the OnPlayerLeaveUnit event for the scoring.

    SCORING:ReportScoreAll()SCORING:RemoveStaticScore(ScoreStatic) - +

    Removes a Static for additional scoring when the Static is destroyed.

    SCORING:ReportScorePlayer()SCORING:RemoveUnitScore(ScoreUnit) - +

    Removes a Unit for additional scoring when the Unit is destroyed.

    +
    SCORING:RemoveZoneScore(ScoreZone) +

    Remove a Zone for additional scoring.

    +
    SCORING:ReportDetailedPlayerCoalitionChanges(PlayerName) +

    Produce detailed report of player penalty scores because of changing the coalition.

    +
    SCORING:ReportDetailedPlayerDestroys(PlayerName) +

    Produce detailed report of player destroy scores.

    +
    SCORING:ReportDetailedPlayerHits(PlayerName) +

    Produce detailed report of player hit scores.

    +
    SCORING:ReportDetailedPlayerMissions(PlayerName) +

    Produce detailed report of player penalty scores because of changing the coalition.

    +
    SCORING:ReportScoreAllSummary(PlayerGroup) +

    Report all players score

    +
    SCORING:ReportScoreGroupDetailed(PlayerGroup) +

    Report Group Score Detailed

    +
    SCORING:ReportScoreGroupSummary(PlayerGroup) +

    Report Group Score Summary

    SCORING:ScoreMenu()SCORING.ScoringCSV -

    Creates a score radio menu.

    +
    SCORING.ScoringCSVSCORING.ScoringObjects + +
    SCORING.ScoringZones SCORING:SecondsToClock(sSeconds) +
    SCORING:SetCoalitionChangePenalty(CoalitionChangePenalty) +

    When a player changes the coalition, he can receive a penalty score.

    +
    SCORING:SetFratricide(Fratricide) +

    When a player commits too much damage to friendlies, his penalty score will reach a certain level.

    +
    SCORING:SetMessagesDestroy(OnOff) +

    Configure to send messages after a target has been destroyed.

    +
    SCORING:SetMessagesHit(OnOff) +

    Configure to send messages after a target has been hit.

    +
    SCORING:SetMessagesScore(OnOff) +

    Configure to send messages after a target has been destroyed and receives additional scores.

    +
    SCORING:SetMessagesToAll() +

    Configure to send messages to all players.

    +
    SCORING:SetMessagesToCoalition() +

    Configure to send messages to only those players within the same coalition as the player.

    +
    SCORING:SetMessagesZone(OnOff) +

    Configure to send messages after a target has been hit in a zone, and additional score is received.

    +
    SCORING:SetMultiplierDestroyPenalty(Multiplier) +

    Set the multiplier for scoring penalty destroys (friendly destroys).

    +
    SCORING:SetMultiplierDestroyScore(Multiplier) +

    Set the multiplier for scoring valid destroys (enemy destroys).

    SCORING:_EventOnHit(Event)

    Handles the OnHit event for the scoring.

    -
    SCORING:_FollowPlayersScheduled() -

    Follows new players entering Clients within the DCSRTE.

    @@ -246,20 +626,6 @@ to a database or a BI tool to publish the scoring results to the player communit
    - - -ClientGroup - -
    -
    - - - -
    -
    -
    -
    - #SCORING SCORING @@ -323,13 +689,144 @@ to a database or a BI tool to publish the scoring results to the player communit
    - - -SCORING.AllScoresMenu + +SCORING:AddScoreGroup(ScoreGroup, Score)
    +

    Specify a special additional score for a Group.

    + +

    Parameters

    + +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:AddStaticScore(ScoreStatic, Score) + +
    +
    + +

    Add a Static for additional scoring when the Static is destroyed.

    + + +

    Note that if there was already a Static declared within the scoring with the same name, +then the old Static will be replaced with the new Static.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Static#UNIT ScoreStatic : +The Static for which the Score needs to be given.

      + +
    • +
    • + +

      #number Score : +The Score value.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:AddUnitScore(ScoreUnit, Score) + +
    +
    + +

    Add a Unit for additional scoring when the Unit is destroyed.

    + + +

    Note that if there was already a Unit declared within the scoring with the same name, +then the old Unit will be replaced with the new Unit.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Unit#UNIT ScoreUnit : +The Unit for which the Score needs to be given.

      + +
    • +
    • + +

      #number Score : +The Score value.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:AddZoneScore(ScoreZone, Score) + +
    +
    + +

    Add a Zone to define additional scoring when any object is destroyed in that zone.

    + + +

    Note that if a Zone with the same name is already within the scoring added, the Zone (type) and Score will be replaced! +This allows for a dynamic destruction zone evolution within your mission.

    + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE_BASE ScoreZone : +The Zone which defines the destruction score perimeters. +Note that a zone can be a polygon or a moving zone.

      + +
    • +
    • + +

      #number Score : +The Score value.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    @@ -379,8 +876,242 @@ to a database or a BI tool to publish the scoring results to the player communit
    - -SCORING.Menu + +SCORING.CoalitionChangePenalty + +
    +
    + + + +
    +
    +
    +
    + + + +SCORING.Fratricide + +
    +
    + + + +
    +
    +
    +
    + + + +SCORING.GameName + +
    +
    + + + +
    +
    +
    +
    + + +SCORING:IfMessagesDestroy() + +
    +
    + +

    If to send messages after a target has been destroyed.

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +SCORING:IfMessagesHit() + +
    +
    + +

    If to send messages after a target has been hit.

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +SCORING:IfMessagesScore() + +
    +
    + +

    If to send messages after a target has been destroyed and receives additional scores.

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +SCORING:IfMessagesToAll() + +
    +
    + +

    If to send messages to all players.

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +SCORING:IfMessagesToCoalition() + +
    +
    + +

    If to send messages to only those players within the same coalition as the player.

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +SCORING:IfMessagesZone() + +
    +
    + +

    If to send messages after a target has been hit in a zone, and additional score is received.

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + #number + +SCORING.MessagesAudience + +
    +
    + + + +
    +
    +
    +
    + + + +SCORING.MessagesDestroy + +
    +
    + + + +
    +
    +
    +
    + + + +SCORING.MessagesHit + +
    +
    + + + +
    +
    +
    +
    + + + +SCORING.MessagesScore + +
    +
    + + + +
    +
    +
    +
    + + + +SCORING.MessagesZone + +
    +
    + + + +
    +
    +
    +
    + + + +SCORING.MultiplierDestroyPenalty + +
    +
    + + + +
    +
    +
    +
    + + + +SCORING.MultiplierDestroyScore
    @@ -423,6 +1154,48 @@ ScoringObject = SCORING:New( "Gori Valley" )
    + +SCORING:OnEventPlayerEnterUnit(Event) + +
    +
    + +

    Handles the OnPlayerEnterUnit event for the scoring.

    + +

    Parameter

    + +
    +
    +
    +
    + + +SCORING:OnEventPlayerLeaveUnit(Event) + +
    +
    + +

    Handles the OnPlayerLeaveUnit event for the scoring.

    + +

    Parameter

    + +
    +
    +
    +
    + SCORING:OpenCSV(ScoringCSV) @@ -467,12 +1240,26 @@ ScoringObject:OpenCSV( "Player Scores" )
    - -SCORING:ReportScoreAll() + +SCORING:RemoveStaticScore(ScoreStatic)
    +

    Removes a Static for additional scoring when the Static is destroyed.

    + +

    Parameter

    + +

    Return value

    + +

    #SCORING:

    @@ -480,14 +1267,234 @@ ScoringObject:OpenCSV( "Player Scores" )
    - -SCORING:ReportScorePlayer() + +SCORING:RemoveUnitScore(ScoreUnit)
    +

    Removes a Unit for additional scoring when the Unit is destroyed.

    + +

    Parameter

    + +

    Return value

    + +

    #SCORING:

    +
    +
    +
    +
    + + +SCORING:RemoveZoneScore(ScoreZone) + +
    +
    + +

    Remove a Zone for additional scoring.

    + + +

    The scoring will search if any Zone is added with the given name, and will remove that zone from the scoring. +This allows for a dynamic destruction zone evolution within your mission.

    + +

    Parameter

    +
      +
    • + +

      Core.Zone#ZONE_BASE ScoreZone : +The Zone which defines the destruction score perimeters. +Note that a zone can be a polygon or a moving zone.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:ReportDetailedPlayerCoalitionChanges(PlayerName) + +
    +
    + +

    Produce detailed report of player penalty scores because of changing the coalition.

    + +

    Parameter

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    +

    Return value

    + +

    #string: +The report.

    + +
    +
    +
    +
    + + +SCORING:ReportDetailedPlayerDestroys(PlayerName) + +
    +
    + +

    Produce detailed report of player destroy scores.

    + +

    Parameter

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    +

    Return value

    + +

    #string: +The report.

    + +
    +
    +
    +
    + + +SCORING:ReportDetailedPlayerHits(PlayerName) + +
    +
    + +

    Produce detailed report of player hit scores.

    + +

    Parameter

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    +

    Return value

    + +

    #string: +The report.

    + +
    +
    +
    +
    + + +SCORING:ReportDetailedPlayerMissions(PlayerName) + +
    +
    + +

    Produce detailed report of player penalty scores because of changing the coalition.

    + +

    Parameter

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    +

    Return value

    + +

    #string: +The report.

    + +
    +
    +
    +
    + + +SCORING:ReportScoreAllSummary(PlayerGroup) + +
    +
    + +

    Report all players score

    + +

    Parameter

    + +
    +
    +
    +
    + + +SCORING:ReportScoreGroupDetailed(PlayerGroup) + +
    +
    + +

    Report Group Score Detailed

    + +

    Parameter

    + +
    +
    +
    +
    + + +SCORING:ReportScoreGroupSummary(PlayerGroup) + +
    +
    + +

    Report Group Score Summary

    + +

    Parameter

    +
    @@ -595,27 +1602,6 @@ The type of the target unit.

    #SCORING: self

    -
    -
    -
    -
    - - -SCORING:ScoreMenu() - -
    -
    - -

    Creates a score radio menu.

    - - -

    Can be accessed using Radio -> F10.

    - -

    Return value

    - -

    #SCORING: -self

    -
    @@ -630,6 +1616,40 @@ self

    + +
    +
    +
    + + + +SCORING.ScoringObjects + +
    +
    + + + + +

    Additional Object scores

    + +
    +
    +
    +
    + + + +SCORING.ScoringZones + +
    +
    + + + + +

    Additional Zone scores.

    +
    @@ -656,6 +1676,269 @@ self

    + +SCORING:SetCoalitionChangePenalty(CoalitionChangePenalty) + +
    +
    + +

    When a player changes the coalition, he can receive a penalty score.

    + + +

    Use the method SCORING.SetCoalitionChangePenalty() to define the penalty when a player changes coalition. +By default, the penalty for changing coalition is the default penalty multiplier.

    + +

    Parameter

    +
      +
    • + +

      #number CoalitionChangePenalty : +The amount of penalty that is given.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetFratricide(Fratricide) + +
    +
    + +

    When a player commits too much damage to friendlies, his penalty score will reach a certain level.

    + + +

    Use this method to define the level when a player gets kicked.
    +By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score.

    + +

    Parameter

    +
      +
    • + +

      #number Fratricide : +The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetMessagesDestroy(OnOff) + +
    +
    + +

    Configure to send messages after a target has been destroyed.

    + +

    Parameter

    +
      +
    • + +

      #boolean OnOff : +If true is given, the messages are sent.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetMessagesHit(OnOff) + +
    +
    + +

    Configure to send messages after a target has been hit.

    + +

    Parameter

    +
      +
    • + +

      #boolean OnOff : +If true is given, the messages are sent.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetMessagesScore(OnOff) + +
    +
    + +

    Configure to send messages after a target has been destroyed and receives additional scores.

    + +

    Parameter

    +
      +
    • + +

      #boolean OnOff : +If true is given, the messages are sent.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetMessagesToAll() + +
    +
    + +

    Configure to send messages to all players.

    + +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetMessagesToCoalition() + +
    +
    + +

    Configure to send messages to only those players within the same coalition as the player.

    + +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetMessagesZone(OnOff) + +
    +
    + +

    Configure to send messages after a target has been hit in a zone, and additional score is received.

    + +

    Parameter

    +
      +
    • + +

      #boolean OnOff : +If true is given, the messages are sent.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetMultiplierDestroyPenalty(Multiplier) + +
    +
    + +

    Set the multiplier for scoring penalty destroys (friendly destroys).

    + + +

    A calculated score is a value between 0.1 and 10. +The multiplier magnifies the scores given to the players.

    + +

    Parameter

    +
      +
    • + +

      #number Multiplier : +The multiplier of the score given.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetMultiplierDestroyScore(Multiplier) + +
    +
    + +

    Set the multiplier for scoring valid destroys (enemy destroys).

    + + +

    A calculated score is a value between 0.1 and 10. +The multiplier magnifies the scores given to the players.

    + +

    Parameter

    +
      +
    • + +

      #number Multiplier : +The multiplier of the score given.

      + +
    • +
    +
    +
    +
    +
    + SCORING:_AddMissionScore(Mission, PlayerUnit, Text, Score) @@ -740,7 +2023,7 @@ self

    @@ -786,22 +2069,6 @@ self

    - -
    -
    -
    - - -SCORING:_FollowPlayersScheduled() - -
    -
    - -

    Follows new players entering Clients within the DCSRTE.

    - - -

    TODO: Need to see if i can catch this also with an event. It will eliminate the schedule ...

    -
    diff --git a/docs/Documentation/Sead.html b/docs/Documentation/Sead.html index 88c8dfb7f..be4e3bf31 100644 --- a/docs/Documentation/Sead.html +++ b/docs/Documentation/Sead.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Set.html b/docs/Documentation/Set.html index e998dfd0e..fbe786ae1 100644 --- a/docs/Documentation/Set.html +++ b/docs/Documentation/Set.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Smoke.html b/docs/Documentation/Smoke.html index 1761f1166..0089621d3 100644 --- a/docs/Documentation/Smoke.html +++ b/docs/Documentation/Smoke.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Documentation/Spawn.html b/docs/Documentation/Spawn.html index 42eb4a563..1fae53205 100644 --- a/docs/Documentation/Spawn.html +++ b/docs/Documentation/Spawn.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -71,7 +72,7 @@

    Module Spawn

    -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:All --
    +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:All --
    Spawn groups of units dynamically in your missions.

    Banner Image

    @@ -2552,7 +2553,7 @@ Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 ) -

    Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.

    +

    When the first Spawn executes, all the Groups need to be made visible before start.

    diff --git a/docs/Documentation/Static.html b/docs/Documentation/Static.html index f71da321b..7d906b032 100644 --- a/docs/Documentation/Static.html +++ b/docs/Documentation/Static.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -133,6 +134,12 @@ If the DCS Static object does not exist or is nil, the STATIC methods will retur
    STATIC:GetDCSObject() +
    STATIC:GetThreatLevel() +
    ZONE_BASE:IsPointVec2InZone(Vec2)ZONE_BASE:IsVec2InZone(Vec2)

    Returns if a location is within the zone.

    ZONE_BASE:IsPointVec3InZone(Vec3)ZONE_BASE:IsVec3InZone(Vec3)

    Returns if a point is within the zone.

    ZONE_POLYGON_BASE:IsVec2InZone(Vec2)

    Returns if a location is within the zone.

    ZONE_RADIUS:IsVec2InZone(Vec2)

    Returns if a location is within the zone.

    ZONE_RADIUS:IsPointVec3InZone(Vec3)ZONE_RADIUS:IsVec3InZone(Vec3)

    Returns if a point is within the zone.

    AI_Balancer -

    Single-Player:No / Mulit-Player:Yes / AI:Yes / Human:No / Types:All -- AI Balancing will replace in multi player missions +

    Single-Player:No / Multi-Player:Yes / AI:Yes / Human:No / Types:All -- AI Balancing will replace in multi player missions non-occupied human slots with AI groups, in order to provide an engaging simulation environment, even when there are hardly any players in the mission.

    @@ -91,7 +92,7 @@ CLIENTS in a SET_CLIENT collection, which are not occupied by human players.

    AI_Cap -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:Air -- Execute Combat Air Patrol (CAP).

    +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- Execute Combat Air Patrol (CAP).

    Banner Image

    @@ -106,7 +107,7 @@ and automatically engage any airborne enemies that are within a certain range or
    AI_Cas -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:Air -- +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- Provide Close Air Support to friendly ground troops.

    Banner Image

    @@ -121,7 +122,7 @@ and automatically engage any airborne enemies that are within a certain range or
    AI_Patrol -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:Air -- +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Air -- Air Patrolling or Staging.

    Banner Image

    @@ -166,7 +167,7 @@ and automatically engage any airborne enemies that are within a certain range or
    Cargo -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:Ground --
    +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Ground --
    Management of logical cargo objects, that can be transported from and to transportation carriers.

    Banner Image

    @@ -317,6 +318,12 @@ following a given priority.

    Route

    (SP) (MP) (FSM) Route AI or players through waypoints or to zones.

    +
    Scenery +

    This module contains the SCENERY class.

    Scoring -

    Scoring system for MOOSE.

    +

    Single-Player:Yes / Multi-Player:Yes / Core:Yes -- Administer the scoring of player achievements, +and create a CSV file logging the scoring events for use at team or squadron websites.

    + +

    Banner Image

    + +
    + +

    1) Scoring#SCORING class, extends Base#BASE

    + +

    The #SCORING class administers the scoring of player achievements, +and creates a CSV file logging the scoring events for use at team or squadron websites.

    Spawn -

    Single-Player:Yes / Mulit-Player:Yes / AI:Yes / Human:No / Types:All --
    +

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:All --
    Spawn groups of units dynamically in your missions.

    Banner Image

    diff --git a/docs/Documentation/routines.html b/docs/Documentation/routines.html index 99337161d..3c5b6891b 100644 --- a/docs/Documentation/routines.html +++ b/docs/Documentation/routines.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • diff --git a/docs/Presentations/SCORING.pptx b/docs/Presentations/SCORING.pptx new file mode 100644 index 000000000..511f83f82 Binary files /dev/null and b/docs/Presentations/SCORING.pptx differ diff --git a/docs/Presentations/SCORING/Dia1.JPG b/docs/Presentations/SCORING/Dia1.JPG new file mode 100644 index 000000000..d1b418574 Binary files /dev/null and b/docs/Presentations/SCORING/Dia1.JPG differ