diff --git a/Moose Development/Maths/Scoring.xlsx b/Moose Development/Maths/Scoring.xlsx new file mode 100644 index 000000000..aea394565 Binary files /dev/null and b/Moose Development/Maths/Scoring.xlsx differ diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index c866dd48b..8b6b51cba 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -1,4 +1,4 @@ ---- 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.** -- @@ -116,6 +116,7 @@ function AI_BALANCER:New( SetClient, SpawnAI ) -- Inherits from BASE local self = BASE:Inherit( self, FSM_SET:New( SET_GROUP:New() ) ) -- AI.AI_Balancer#AI_BALANCER + -- TODO: Define the OnAfterSpawned event self:SetStartState( "None" ) self:AddTransition( "*", "Monitor", "Monitoring" ) self:AddTransition( "*", "Spawn", "Spawning" ) @@ -179,15 +180,17 @@ function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName ) -- OK, Spawn a new group from the default SpawnAI object provided. local AIGroup = self.SpawnAI:Spawn() -- Wrapper.Group#GROUP - AIGroup:E( "Spawning new AIGroup" ) - --TODO: need to rework UnitName thing ... - - SetGroup:Add( ClientName, AIGroup ) - self.SpawnQueue[ClientName] = nil - - -- Fire the Spawned event. The first parameter is the AIGroup just Spawned. - -- Mission designers can catch this event to bind further actions to the AIGroup. - self:Spawned( AIGroup ) + if AIGroup then + AIGroup:E( "Spawning new AIGroup" ) + --TODO: need to rework UnitName thing ... + + SetGroup:Add( ClientName, AIGroup ) + self.SpawnQueue[ClientName] = nil + + -- Fire the Spawned event. The first parameter is the AIGroup just Spawned. + -- Mission designers can catch this event to bind further actions to the AIGroup. + self:Spawned( AIGroup ) + end end --- @param #AI_BALANCER self diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index a34cc3997..c76c79c66 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -1,4 +1,4 @@ ---- 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](..\Presentations\AI_CAP\Dia1.JPG) -- diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index 3c2a489de..e6eb23626 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -1,4 +1,4 @@ ---- 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](..\Presentations\AI_CAS\Dia1.JPG) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 63c6e4117..2172045f7 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -1,4 +1,4 @@ ----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](..\Presentations\AI_CARGO\CARGO.JPG) diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index e49b718be..8780b3ac0 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -1,4 +1,4 @@ ---- 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](..\Presentations\AI_PATROL\Dia1.JPG) 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/Point.lua b/Moose Development/Moose/Core/Point.lua index cfb27e42f..3919e164e 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -7,13 +7,56 @@ -- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. -- In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums. -- --- 1.1) POINT_VEC3 constructor --- --------------------------- +-- ## 1.1) POINT_VEC3 constructor +-- -- A new POINT_VEC3 instance can be created with: -- -- * @{Point#POINT_VEC3.New}(): a 3D point. -- * @{Point#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. --- +-- +-- ## 1.2) Manupulate the X, Y, Z coordinates of the point +-- +-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. +-- Methods exist to manupulate these coordinates. +-- +-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively. +-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value. +-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}() +-- to add or substract a value from the current respective axis value. +-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example: +-- +-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3() +-- +-- +-- ## 1.5) Smoke, flare, explode, illuminate +-- +-- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods: +-- +-- ### 1.5.1) Smoke +-- +-- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color. +-- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue. +-- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red. +-- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange. +-- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white. +-- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green. +-- +-- ### 1.5.2) Flare +-- +-- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color. +-- * @{#POINT_VEC3.FlareRed}(): To flare the point in red. +-- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow. +-- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white. +-- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green. +-- +-- ### 1.5.3) Explode +-- +-- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity. +-- +-- ### 1.5.4) Illuminate +-- +-- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point. +-- -- -- 2) @{Point#POINT_VEC2} class, extends @{Point#POINT_VEC3} -- ========================================================= @@ -26,6 +69,19 @@ -- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter. -- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. -- +-- ## 1.2) Manupulate the X, Altitude, Y coordinates of the 2D point +-- +-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. +-- Methods exist to manupulate these coordinates. +-- +-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively. +-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value. +-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}() +-- to add or substract a value from the current respective axis value. +-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example: +-- +-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2() +-- -- === -- -- **API CHANGE HISTORY** @@ -38,9 +94,12 @@ -- -- Hereby the change log: -- --- 2017-02-18: POINT_VEC3:**NewFromVec2( Vec2, LandHeightAdd )** added. +-- 2017-03-03: POINT\_VEC3:**Explosion( ExplosionIntensity )** added. +-- 2017-03-03: POINT\_VEC3:**IlluminationBomb()** added. -- --- 2016-08-12: POINT_VEC3:**Translate( Distance, Angle )** added. +-- 2017-02-18: POINT\_VEC3:**NewFromVec2( Vec2, LandHeightAdd )** added. +-- +-- 2016-08-12: POINT\_VEC3:**Translate( Distance, Angle )** added. -- -- 2016-08-06: Made PointVec3 and Vec3, PointVec2 and Vec2 terminology used in the code consistent. -- @@ -196,21 +255,57 @@ function POINT_VEC3:GetZ() end --- Set the x coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self -- @param #number x The x coordinate. +-- @return #POINT_VEC3 function POINT_VEC3:SetX( x ) self.x = x + return self end --- Set the y coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self -- @param #number y The y coordinate. +-- @return #POINT_VEC3 function POINT_VEC3:SetY( y ) self.y = y + return self end --- Set the z coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self -- @param #number z The z coordinate. +-- @return #POINT_VEC3 function POINT_VEC3:SetZ( z ) self.z = z + return self +end + +--- Add to the x coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #number x The x coordinate value to add to the current x coodinate. +-- @return #POINT_VEC3 +function POINT_VEC3:AddX( x ) + self.x = self.x + x + return self +end + +--- Add to the y coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #number y The y coordinate value to add to the current y coodinate. +-- @return #POINT_VEC3 +function POINT_VEC3:AddY( y ) + self.y = self.y + y + return self +end + +--- Add to the z coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #number z The z coordinate value to add to the current z coodinate. +-- @return #POINT_VEC3 +function POINT_VEC3:AddZ( z ) + self.z = self.z +z + return self end --- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. @@ -501,6 +596,21 @@ function POINT_VEC3:RoutePointGround( Speed, Formation ) return RoutePoint end +--- Creates an explosion at the point of a certain intensity. +-- @param #POINT_VEC3 self +-- @param #number ExplosionIntensity +function POINT_VEC3:Explosion( ExplosionIntensity ) + self:F2( { ExplosionIntensity } ) + trigger.action.explosion( self:GetVec3(), ExplosionIntensity ) +end + +--- Creates an illumination bomb at the point. +-- @param #POINT_VEC3 self +function POINT_VEC3:IlluminationBomb() + self:F2() + trigger.action.illuminationBomb( self:GetVec3() ) +end + --- Smokes the point in a color. -- @param #POINT_VEC3 self @@ -666,15 +776,57 @@ function POINT_VEC2:GetAlt() end --- Set the x coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self -- @param #number x The x coordinate. +-- @return #POINT_VEC2 function POINT_VEC2:SetX( x ) self.x = x + return self end --- Set the y coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self -- @param #number y The y coordinate. +-- @return #POINT_VEC2 function POINT_VEC2:SetY( y ) self.z = y + return self +end + +--- Set the altitude of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set. +-- @return #POINT_VEC2 +function POINT_VEC2:SetAlt( Altitude ) + self.y = Altitude or land.getHeight( { x = self.x, y = self.z } ) + return self +end + +--- Add to the x coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @param #number x The x coordinate. +-- @return #POINT_VEC2 +function POINT_VEC2:AddX( x ) + self.x = self.x + x + return self +end + +--- Add to the y coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @param #number y The y coordinate. +-- @return #POINT_VEC2 +function POINT_VEC2:AddY( y ) + self.z = self.z + y + return self +end + +--- Add to the current land height an altitude. +-- @param #POINT_VEC2 self +-- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set. +-- @return #POINT_VEC2 +function POINT_VEC2:AddAlt( Altitude ) + self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0 + return self end diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index 5abb966b5..a414bcc54 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -64,7 +64,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler 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 7001aca89..c77a3088a 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -1,10 +1,185 @@ ---- Scoring system for MOOSE. --- 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. +--- 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\SCORING\Dia1.JPG) +-- +-- === +-- +-- # 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 and results for use at team or squadron websites. +-- +-- SCORING automatically calculates the threat level of the objects hit and destroyed by players, +-- which can be @{Unit}, @{Static) and @{Scenery} objects. +-- +-- Positive score points are granted when enemy or neutral targets are destroyed. +-- Negative score points or penalties are given when a friendly target is hit or destroyed. +-- This brings a lot of dynamism in the scoring, where players need to take care to inflict damage on the right target. +-- By default, penalties weight heavier in the scoring, to ensure that players don't commit fratricide. +-- The total score of the player is calculated by **adding the scores minus the penalties**. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia4.JPG) +-- +-- The score value is calculated based on the **threat level of the player** and the **threat level of the target**. +-- A calculated score takes the threat level of the target divided by a balanced threat level of the player unit. +-- As such, if the threat level of the target is high, and the player threat level is low, a higher score will be given than +-- if the threat level of the player would be high too. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia5.JPG) +-- +-- When multiple players hit the same target, and finally succeed in destroying the target, then 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. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia13.JPG) +-- +-- Optionally, the score values can be **scaled** by a **scale**. Specific scales can be set for positive cores or negative penalties. +-- The default range of the scores granted is a value between 0 and 10. The default range of penalties given is a value between 0 and 30. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia7.JPG) +-- +-- **Additional scores** can be granted to **specific objects**, when the player(s) destroy these objects. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia9.JPG) +-- +-- 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 results can also be logged in a **CSV file**. +-- These 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 player 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 scale +-- +-- Score scales can be set for scores granted when enemies or friendlies are destroyed. +-- Use the method @{#SCORING.SetScaleDestroyScore}() to set the scale of enemy destroys (positive destroys). +-- Use the method @{#SCORING.SetScaleDestroyPenalty}() to set the scale of friendly destroys (negative destroys). +-- +-- local Scoring = SCORING:New( "Scoring File" ) +-- Scoring:SetScaleDestroyScore( 10 ) +-- Scoring:SetScaleDestroyPenalty( 40 ) +-- +-- The above sets the scale 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) Add extra Goal scores upon an event or a condition. +-- +-- A mission has goals and achievements. The scoring system provides an API to set additional scores when a goal or achievement event happens. +-- Use the method @{#SCORING.AddGoalScore}() to add a score for a Player at any time in your mission. +-- +-- ## 1.5) 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.6) 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 scale. +-- +-- ## 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 @@ -42,7 +217,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 @@ -51,172 +226,340 @@ function SCORING:New( GameName ) end + -- 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 ) + + -- Scales + self:SetScaleDestroyScore( 10 ) + self:SetScaleDestroyPenalty( 30 ) + + -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). + self:SetFratricide( self.ScaleDestroyPenalty * 3 ) + + -- Default penalty when a player changes coalition. + self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty ) + + -- 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 scale for scoring valid destroys (enemy destroys). +-- A default calculated score is a value between 1 and 10. +-- The scale 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 Scale The scale of the score given. +function SCORING:SetScaleDestroyScore( Scale ) + + self.ScaleDestroyScore = Scale + 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 scale for scoring penalty destroys (friendly destroys). +-- A default calculated penalty is a value between 1 and 10. +-- The scale magnifies the scores given to the players. +-- @param #SCORING self +-- @param #number Scale The scale of the score given. +-- @return #SCORING +function SCORING:SetScaleDestroyPenalty( Scale ) - local ClientUnit = 0 - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } - local unitId - local unitData - local AlivePlayerUnits = {} + self.ScaleDestroyPenalty = Scale + + 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 - ---- Track DEAD or CRASH events for the scoring. +--- 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.Event#EVENTDATA Event -function SCORING:_EventOnDeadOrCrash( Event ) - self:F( { Event } ) +-- @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 TargetUnit = nil - local TargetGroup = nil - local TargetUnitName = "" - local TargetGroupName = "" - local TargetPlayerName = "" - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil + local ZoneName = ScoreZone:GetName() - if Event.IniDCSUnit then - - TargetUnit = Event.IniDCSUnit - TargetUnitName = Event.IniDCSUnitName - TargetGroup = Event.IniDCSGroup - TargetGroupName = Event.IniDCSGroupName - TargetPlayerName = Event.IniPlayerName - - TargetCoalition = TargetUnit:getCoalition() - --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnit:getDesc().category -- Workaround - TargetType = TargetUnit:getTypeName() - - TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] - TargetUnitCategory = _SCORINGCategory[TargetCategory] - TargetUnitType = TargetType - - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) - end - - for PlayerName, PlayerData in pairs( self.Players ) do - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got killed" ) - - -- Some variables - local InitUnitName = PlayerData.UnitName - local InitUnitType = PlayerData.UnitType - local InitCoalition = PlayerData.UnitCoalition - local InitCategory = PlayerData.UnitCategory - local InitUnitCoalition = _SCORINGCoalition[InitCoalition] - local InitUnitCategory = _SCORINGCategory[InitCategory] - - self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - - -- What is he hitting? - if TargetCategory then - if PlayerData and PlayerData.Hit and PlayerData.Hit[TargetCategory] and PlayerData.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? - if not PlayerData.Kill[TargetCategory] then - PlayerData.Kill[TargetCategory] = {} - end - if not PlayerData.Kill[TargetCategory][TargetType] then - PlayerData.Kill[TargetCategory][TargetType] = {} - PlayerData.Kill[TargetCategory][TargetType].Score = 0 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = 0 - PlayerData.Kill[TargetCategory][TargetType].Penalty = 0 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = 0 - end - - if InitCoalition == TargetCoalition then - PlayerData.Penalty = PlayerData.Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].Penalty = PlayerData.Kill[TargetCategory][TargetType].Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = PlayerData.Kill[TargetCategory][TargetType].PenaltyKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill .. " times. Penalty: -" .. PlayerData.Kill[TargetCategory][TargetType].Penalty .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - 5 ):ToAll() - self:ScoreCSV( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - PlayerData.Score = PlayerData.Score + 10 - PlayerData.Kill[TargetCategory][TargetType].Score = PlayerData.Kill[TargetCategory][TargetType].Score + 10 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = PlayerData.Kill[TargetCategory][TargetType].ScoreKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].ScoreKill .. " times. Score: " .. PlayerData.Kill[TargetCategory][TargetType].Score .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - 5 ):ToAll() - self:ScoreCSV( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end - end - end - end + 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 scale. +-- @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. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT UnitData function SCORING:_AddPlayerFromUnit( UnitData ) self:F( UnitData ) - if UnitData and UnitData:isExist() then - local UnitName = UnitData:getName() - local PlayerName = UnitData:getPlayerName() - local UnitDesc = UnitData:getDesc() + if UnitData:IsAlive() then + local UnitName = UnitData:GetName() + local PlayerName = UnitData:GetPlayerName() + local UnitDesc = UnitData:GetDesc() local UnitCategory = UnitDesc.category - local UnitCoalition = UnitData:getCoalition() - local UnitTypeName = UnitData:getTypeName() + local UnitCoalition = UnitData:GetCoalition() + local UnitTypeName = UnitData:GetTypeName() self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... self.Players[PlayerName] = {} self.Players[PlayerName].Hit = {} - self.Players[PlayerName].Kill = {} + self.Players[PlayerName].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].HitUnits = {} self.Players[PlayerName].Score = 0 self.Players[PlayerName].Penalty = 0 self.Players[PlayerName].PenaltyCoalition = 0 @@ -234,26 +577,26 @@ function SCORING:_AddPlayerFromUnit( UnitData ) 2 ):ToAll() self:ScoreCSV( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, - UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:getTypeName() ) + UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() ) end end self.Players[PlayerName].UnitName = UnitName self.Players[PlayerName].UnitCoalition = UnitCoalition self.Players[PlayerName].UnitCategory = UnitCategory self.Players[PlayerName].UnitType = UnitTypeName + 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, + MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, 30 ):ToAll() self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 end end - if self.Players[PlayerName].Penalty > 150 then - ClientGroup = GROUP:NewFromDCSUnit( UnitData ) - ClientGroup:Destroy() + if self.Players[PlayerName].Penalty > self.Fratricide then + UnitData:Destroy() MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", 10 ):ToAll() @@ -263,6 +606,37 @@ function SCORING:_AddPlayerFromUnit( UnitData ) end +--- Add a goal score for a player. +-- The method takes the PlayerUnit for which the Goal score needs to be set. +-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. +-- A free text can be given that is shown to the players. +-- The Score can be both positive and negative. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the Player. Other Properties for the scoring are taken from this PlayerUnit, like coalition, type etc. +-- @param #string GoalTag The string or identifier that is used in the CSV file to identify the goal (sort or group later in Excel). +-- @param #string Text A free text that is shown to the players. +-- @param #number Score The score can be both positive or negative ( Penalty ). +function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score ) + + local PlayerName = PlayerUnit:GetPlayerName() + + self:E( { PlayerUnit.UnitName, PlayerName, GoalTag, Text, Score } ) + + -- PlayerName can be nil, if the Unit with the player crashed or due to another reason. + if PlayerName then + local PlayerData = self.Players[PlayerName] + + PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 } + PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score + PlayerData.Score = PlayerData.Score + Score + + MESSAGE:New( Text, 30 ):ToAll() + + self:ScoreCSV( PlayerName, "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() ) + end +end + + --- Registers Scores the players completing a Mission Task. -- @param #SCORING self -- @param Tasking.Mission#MISSION Mission @@ -331,6 +705,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 @@ -338,6 +740,7 @@ function SCORING:_EventOnHit( Event ) self:F( { Event } ) local InitUnit = nil + local InitUNIT = nil local InitUnitName = "" local InitGroup = nil local InitGroupName = "" @@ -351,10 +754,11 @@ function SCORING:_EventOnHit( Event ) local InitUnitType = nil local TargetUnit = nil + local TargetUNIT = nil local TargetUnitName = "" local TargetGroup = nil local TargetGroupName = "" - local TargetPlayerName = "" + local TargetPlayerName = nil local TargetCoalition = nil local TargetCategory = nil @@ -366,16 +770,18 @@ function SCORING:_EventOnHit( Event ) if Event.IniDCSUnit then InitUnit = Event.IniDCSUnit + InitUNIT = Event.IniUnit InitUnitName = Event.IniDCSUnitName InitGroup = Event.IniDCSGroup 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] @@ -388,16 +794,18 @@ function SCORING:_EventOnHit( Event ) if Event.TgtDCSUnit then TargetUnit = Event.TgtDCSUnit + TargetUNIT = Event.TgtUnit TargetUnitName = Event.TgtDCSUnitName TargetGroup = Event.TgtDCSGroup 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] @@ -407,279 +815,682 @@ function SCORING:_EventOnHit( Event ) end if InitPlayerName ~= nil then -- It is a player that is hitting something - self:_AddPlayerFromUnit( InitUnit ) + self:_AddPlayerFromUnit( InitUNIT ) if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( TargetUnit ) - self.Players[InitPlayerName].HitPlayers = self.Players[InitPlayerName].HitPlayers + 1 - end + self:_AddPlayerFromUnit( TargetUNIT ) + end - self:T( "Hitting Something" ) - -- What is he hitting? - if TargetCategory then - if not self.Players[InitPlayerName].Hit[TargetCategory] then - self.Players[InitPlayerName].Hit[TargetCategory] = {} + self:T( "Hitting Something" ) + + -- What is he hitting? + if TargetCategory then + + -- A target got hit, score it. + -- Player contains the score data from self.Players[InitPlayerName] + local Player = self.Players[InitPlayerName] + + -- Ensure there is a hit table per TargetCategory and TargetUnitName. + Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} + Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} + + -- PlayerHit contains the score counters and data per unit that was hit. + local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] + + PlayerHit.Score = PlayerHit.Score or 0 + PlayerHit.Penalty = PlayerHit.Penalty or 0 + PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 + PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 + PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT + + -- Only grant hit scores if there was more than one second between the last hit. + if timer.getTime() - PlayerHit.TimeStamp > 1 then + PlayerHit.TimeStamp = timer.getTime() + + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + + -- Ensure there is a Player to Player hit reference table. + Player.HitPlayers[TargetPlayerName] = true + end + + local Score = 0 + + 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 + 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 + 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 - if not self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] then - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] = {} - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = 0 - end - local Score = 0 - if InitCoalition == TargetCoalition then - self.Players[InitPlayerName].Penalty = self.Players[InitPlayerName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: -" .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - 2 - ):ToAll() - self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - self.Players[InitPlayerName].Score = self.Players[InitPlayerName].Score + 1 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score + 1 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit .. " times. Score: " .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - 2 - ):ToAll() - self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end end elseif InitPlayerName == nil then -- It is an AI hitting a player??? end end +--- Track DEAD or CRASH events for the scoring. +-- @param #SCORING self +-- @param Core.Event#EVENTDATA Event +function SCORING:_EventOnDeadOrCrash( Event ) + self:F( { Event } ) -function SCORING:ReportScoreAll() + local TargetUnit = nil + local TargetGroup = nil + local TargetUnitName = "" + local TargetGroupName = "" + local TargetPlayerName = "" + local TargetCoalition = nil + local TargetCategory = nil + local TargetType = nil + local TargetUnitCoalition = nil + local TargetUnitCategory = nil + local TargetUnitType = nil - env.info( "Hello World " ) + if Event.IniDCSUnit then - local ScoreMessage = "" - local PlayerMessage = "" + TargetUnit = Event.IniDCSUnit + TargetUnitName = Event.IniDCSUnitName + TargetGroup = Event.IniDCSGroup + TargetGroupName = Event.IniDCSGroupName + TargetPlayerName = Event.IniPlayerName - self:T( "Score Report" ) + TargetCoalition = Event.IniCoalition + --TargetCategory = TargetUnit:getCategory() + --TargetCategory = TargetUnit:getDesc().category -- Workaround + TargetCategory = Event.IniCategory + TargetType = Event.IniTypeName - 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 ) + TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] + TargetUnitCategory = _SCORINGCategory[TargetCategory] + TargetUnitType = TargetType + + self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) + end + + -- 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 destroyed" ) -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName + local InitUnitName = Player.UnitName + local InitUnitType = Player.UnitType + local InitCoalition = Player.UnitCoalition + local InitCategory = Player.UnitCategory + local InitUnitCoalition = _SCORINGCoalition[InitCoalition] + local InitUnitCategory = _SCORINGCategory[InitCategory] - local PlayerScore = 0 - local PlayerPenalty = 0 + self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - ScoreMessage = ":\n" + -- 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.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} + Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} - local ScoreMessageHits = "" + -- 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 - 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 + if TargetCoalition then + if InitCoalition == TargetCoalition then + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() + local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 ) + self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) + + Player.Penalty = Player.Penalty + ThreatPenalty + TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty + TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 + + 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 .. "' 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, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() + local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) + + 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 .. "' 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 - 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 ) + -- 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 + end end 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 end - MESSAGE:New( PlayerMessage, 30, "Player Scores" ):ToAll() end -function SCORING:ReportScorePlayer() - - 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 = "" - - 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( "\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 ) + 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 .. "\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 - - for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do - Score = Score + UnitData.Score - ScoreKill = ScoreKill + UnitData.ScoreKill - Penalty = Penalty + UnitData.Penalty - PenaltyKill = PenaltyKill + UnitData.PenaltyKill - end - - local ScoreMessageKill = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill ) - self:T( ScoreMessageKill ) - ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill - - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageKills ~= "" then - ScoreMessage = ScoreMessage .. "\n Kills: " .. ScoreMessageKills .. " " - end - - local ScoreMessageCoalitionChangePenalties = "" - if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) - PlayerPenalty = PlayerPenalty + PlayerData.Penalty - end - if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. "\n Coalition: " .. ScoreMessageCoalitionChangePenalties .. " " - end - - local ScoreMessageMission = "" - local ScoreMission = 0 - local ScoreTask = 0 - for MissionName, MissionData in pairs( PlayerData.Mission ) do - ScoreMission = ScoreMission + MissionData.ScoreMission - ScoreTask = ScoreTask + MissionData.ScoreTask - ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " - end - PlayerScore = PlayerScore + ScoreMission + ScoreTask - - if ScoreMessageMission ~= "" then - ScoreMessage = ScoreMessage .. "\n Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") " - end - - PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties ):%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) + end + if ScoreMessageHits ~= "" then + ScoreMessage = "Hits: " .. ScoreMessageHits + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + + +--- 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 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 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 + + for UnitName, UnitData in pairs( PlayerData.Destroy[CategoryID] ) do + self:E( { UnitData = UnitData } ) + if UnitData ~= {} then + Score = Score + UnitData.Score + ScoreDestroy = ScoreDestroy + UnitData.ScoreDestroy + Penalty = Penalty + UnitData.Penalty + PenaltyDestroy = PenaltyDestroy + UnitData.PenaltyDestroy + end + end + + local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageDestroy ) + ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy + + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + 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 goal scores. +-- @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 ScoreMessageGoal = "" + local ScoreGoal = 0 + local ScoreTask = 0 + for GoalName, GoalData in pairs( PlayerData.Goals ) do + ScoreGoal = ScoreGoal + GoalData.Score + ScoreMessageGoal = ScoreMessageGoal .. "'" .. GoalName .. "':" .. GoalData.Score .. "; " + end + PlayerScore = PlayerScore + ScoreGoal + + if ScoreMessageGoal ~= "" then + ScoreMessage = "Goals: " .. ScoreMessageGoal + 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 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 = "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 ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) + ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals + self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) + + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + 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 ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) + ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals + self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) + + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty, + ReportHits, + ReportDestroys, + ReportCoalitionChanges, + ReportGoals, + 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 ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) + ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals + self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) + + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + 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 @@ -780,21 +1591,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/Functional/Spawn.lua b/Moose Development/Moose/Functional/Spawn.lua index 48ceb1e74..85e2f9fbd 100644 --- a/Moose Development/Moose/Functional/Spawn.lua +++ b/Moose Development/Moose/Functional/Spawn.lua @@ -1,4 +1,4 @@ ---- 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](..\Presentations\SPAWN\SPAWN.JPG) @@ -1304,10 +1304,12 @@ function SPAWN:_RandomizeTemplate( SpawnIndex ) self.SpawnGroups[SpawnIndex].SpawnTemplate.x = self.SpawnTemplate.x self.SpawnGroups[SpawnIndex].SpawnTemplate.y = self.SpawnTemplate.y self.SpawnGroups[SpawnIndex].SpawnTemplate.start_time = self.SpawnTemplate.start_time + local OldX = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].x + local OldY = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].y for UnitID = 1, #self.SpawnGroups[SpawnIndex].SpawnTemplate.units do self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].heading = self.SpawnTemplate.units[1].heading - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x = self.SpawnTemplate.units[1].x - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y = self.SpawnTemplate.units[1].y + self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x = self.SpawnTemplate.units[1].x + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x - OldX ) + self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y = self.SpawnTemplate.units[1].y + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y - OldY ) self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].alt = self.SpawnTemplate.units[1].alt end end 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/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index b3c154192..4151d6e98 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -14,6 +14,7 @@ -- * @{#TASK.HasStateMachine}():Enquire if the task has a @{Fsm} -- * @{#TASK.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK}. -- * @{#TASK.UnAssignFromUnit}(): Unassign the task from a unit. +-- * @{#TASK.SetTimeOut}(): Set timer in seconds before task gets cancelled if not assigned. -- -- 1.2) Set and enquire task status (beyond the task state machine processing). -- ---------------------------------------------------------------------------- @@ -70,6 +71,7 @@ TASK = { FsmTemplate = nil, Mission = nil, CommandCenter = nil, + TimeOut = 0, } --- FSM PlayerAborted event handler prototype for TASK. @@ -163,6 +165,7 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self:AddTransition( "*", "PlayerAborted", "*" ) self:AddTransition( "*", "PlayerDead", "*" ) self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" ) + self:AddTransition( "*", "TimeOut", "Cancelled" ) self:E( "New TASK " .. TaskName ) @@ -404,6 +407,17 @@ function TASK:UnAssignFromUnit( TaskUnit ) return self end +--- Sets the TimeOut for the @{Task}. If @{Task} stayed planned for longer than TimeOut, it gets into Cancelled status. +-- @param #TASK self +-- @param #integer Timer in seconds +-- @return #TASK self +function TASK:SetTimeOut ( Timer ) + self:F( Timer ) + self.TimeOut = Timer + self:__TimeOut( self.TimeOut ) + return self +end + --- Send a message of the @{Task} to the assigned @{Group}s. -- @param #TASK self function TASK:MessageToGroups( Message ) @@ -935,6 +949,30 @@ function TASK:onstatechange( From, Event, To ) end +--- FSM function for a TASK +-- @param #TASK self +-- @param #string Event +-- @param #string From +-- @param #string To +function TASK:onenterPlanned( From, Event, To) + if not self.TimeOut == 0 then + self.__TimeOut( self.TimeOut ) + end +end + +--- FSM function for a TASK +-- @param #TASK self +-- @param #string Event +-- @param #string From +-- @param #string To +function TASK:onbeforeTimeOut( From, Event, To ) + if From == "Planned" then + self:RemoveMenu() + return true + end + return false +end + do -- Reporting --- Create a summary report of the Task. 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/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index efadcaf4d..0a4ba9c80 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -79,3 +79,8 @@ function STATIC:GetDCSObject() return nil end + +function STATIC:GetThreatLevel() + + return 1, "Static" +end \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 101174af0..ad0fc58be 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -536,45 +536,129 @@ end -- * Threat level 8: Unit is a Short Range SAM, radar guided. -- * Threat level 9: Unit is a Medium Range SAM, radar guided. -- * Threat level 10: Unit is a Long Range SAM, radar guided. +-- @param #UNIT self function UNIT:GetThreatLevel() local Attributes = self:GetDesc().attributes + self:E( Attributes ) + local ThreatLevel = 0 + local ThreatText = "" + + if self:IsGround() then - local ThreatLevels = { - "Unarmed", - "Infantry", - "Old Tanks & APCs", - "Tanks & IFVs without ATGM", - "Tanks & IFV with ATGM", - "Modern Tanks", - "AAA", - "IR Guided SAMs", - "SR SAMs", - "MR SAMs", - "LR SAMs" - } + self:E( "Ground" ) - self:T2( Attributes ) + local ThreatLevels = { + "Unarmed", + "Infantry", + "Old Tanks & APCs", + "Tanks & IFVs without ATGM", + "Tanks & IFV with ATGM", + "Modern Tanks", + "AAA", + "IR Guided SAMs", + "SR SAMs", + "MR SAMs", + "LR SAMs" + } + + + if Attributes["LR SAM"] then ThreatLevel = 10 + elseif Attributes["MR SAM"] then ThreatLevel = 9 + elseif Attributes["SR SAM"] and + not Attributes["IR Guided SAM"] then ThreatLevel = 8 + elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and + Attributes["IR Guided SAM"] then ThreatLevel = 7 + elseif Attributes["AAA"] then ThreatLevel = 6 + elseif Attributes["Modern Tanks"] then ThreatLevel = 5 + elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and + Attributes["ATGM"] then ThreatLevel = 4 + elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and + not Attributes["ATGM"] then ThreatLevel = 3 + elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 + elseif Attributes["Infantry"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] + end - if Attributes["LR SAM"] then ThreatLevel = 10 - elseif Attributes["MR SAM"] then ThreatLevel = 9 - elseif Attributes["SR SAM"] and - not Attributes["IR Guided SAM"] then ThreatLevel = 8 - elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and - Attributes["IR Guided SAM"] then ThreatLevel = 7 - elseif Attributes["AAA"] then ThreatLevel = 6 - elseif Attributes["Modern Tanks"] then ThreatLevel = 5 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - Attributes["ATGM"] then ThreatLevel = 4 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - not Attributes["ATGM"] then ThreatLevel = 3 - elseif Attributes["Old Tanks"] or Attributes["APC"] then ThreatLevel = 2 - elseif Attributes["Infantry"] then ThreatLevel = 1 + if self:IsAir() then + + self:E( "Air" ) + + local ThreatLevels = { + "Unarmed", + "Tanker", + "AWACS", + "Transport Helicpter", + "UAV", + "Bomber", + "Strategic Bomber", + "Attack Helicopter", + "Interceptor", + "Multirole Fighter", + "Fighter" + } + + + if Attributes["Fighters"] then ThreatLevel = 10 + elseif Attributes["Multirole fighters"] then ThreatLevel = 9 + elseif Attributes["Battleplanes"] then ThreatLevel = 8 + elseif Attributes["Attack helicopters"] then ThreatLevel = 7 + elseif Attributes["Strategic bombers"] then ThreatLevel = 6 + elseif Attributes["Bombers"] then ThreatLevel = 5 + elseif Attributes["UAVs"] then ThreatLevel = 4 + elseif Attributes["Transport helicopters"] then ThreatLevel = 3 + elseif Attributes["AWACS"] then ThreatLevel = 2 + elseif Attributes["Tankers"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] + end + + if self:IsShip() then + + self:E( "Ship" ) + +--["Aircraft Carriers"] = {"Heavy armed ships",}, +--["Cruisers"] = {"Heavy armed ships",}, +--["Destroyers"] = {"Heavy armed ships",}, +--["Frigates"] = {"Heavy armed ships",}, +--["Corvettes"] = {"Heavy armed ships",}, +--["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",}, +--["Light armed ships"] = {"Armed ships","NonArmoredUnits"}, +--["Armed ships"] = {"Ships"}, +--["Unarmed ships"] = {"Ships","HeavyArmoredUnits",}, + + local ThreatLevels = { + "Unarmed ship", + "Light armed ships", + "Corvettes", + "", + "Frigates", + "", + "Cruiser", + "", + "Destroyer", + "", + "Aircraft Carrier" + } + + + if Attributes["Aircraft Carriers"] then ThreatLevel = 10 + elseif Attributes["Destroyers"] then ThreatLevel = 8 + elseif Attributes["Cruisers"] then ThreatLevel = 6 + elseif Attributes["Frigates"] then ThreatLevel = 4 + elseif Attributes["Corvettes"] then ThreatLevel = 2 + elseif Attributes["Light armed ships"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] end self:T2( ThreatLevel ) - return ThreatLevel, ThreatLevels[ThreatLevel+1] + return ThreatLevel, ThreatText end @@ -589,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 @@ -606,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 748b350e2..565435c5b 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: 20170224_1222' ) +env.info( 'Moose Generation Timestamp: 20170303_1333' ) local base = _G Include = {} @@ -3875,7 +3875,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -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 @@ -10132,13 +10177,56 @@ end -- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. -- In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums. -- --- 1.1) POINT_VEC3 constructor --- --------------------------- +-- ## 1.1) POINT_VEC3 constructor +-- -- A new POINT_VEC3 instance can be created with: -- -- * @{Point#POINT_VEC3.New}(): a 3D point. -- * @{Point#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. --- +-- +-- ## 1.2) Manupulate the X, Y, Z coordinates of the point +-- +-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. +-- Methods exist to manupulate these coordinates. +-- +-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively. +-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value. +-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}() +-- to add or substract a value from the current respective axis value. +-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example: +-- +-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3() +-- +-- +-- ## 1.5) Smoke, flare, explode, illuminate +-- +-- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods: +-- +-- ### 1.5.1) Smoke +-- +-- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color. +-- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue. +-- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red. +-- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange. +-- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white. +-- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green. +-- +-- ### 1.5.2) Flare +-- +-- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color. +-- * @{#POINT_VEC3.FlareRed}(): To flare the point in red. +-- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow. +-- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white. +-- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green. +-- +-- ### 1.5.3) Explode +-- +-- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity. +-- +-- ### 1.5.4) Illuminate +-- +-- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point. +-- -- -- 2) @{Point#POINT_VEC2} class, extends @{Point#POINT_VEC3} -- ========================================================= @@ -10151,6 +10239,19 @@ end -- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter. -- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. -- +-- ## 1.2) Manupulate the X, Altitude, Y coordinates of the 2D point +-- +-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. +-- Methods exist to manupulate these coordinates. +-- +-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively. +-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value. +-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}() +-- to add or substract a value from the current respective axis value. +-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example: +-- +-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2() +-- -- === -- -- **API CHANGE HISTORY** @@ -10163,9 +10264,12 @@ end -- -- Hereby the change log: -- --- 2017-02-18: POINT_VEC3:**NewFromVec2( Vec2, LandHeightAdd )** added. +-- 2017-03-03: POINT\_VEC3:**Explosion( ExplosionIntensity )** added. +-- 2017-03-03: POINT\_VEC3:**IlluminationBomb()** added. -- --- 2016-08-12: POINT_VEC3:**Translate( Distance, Angle )** added. +-- 2017-02-18: POINT\_VEC3:**NewFromVec2( Vec2, LandHeightAdd )** added. +-- +-- 2016-08-12: POINT\_VEC3:**Translate( Distance, Angle )** added. -- -- 2016-08-06: Made PointVec3 and Vec3, PointVec2 and Vec2 terminology used in the code consistent. -- @@ -10321,21 +10425,57 @@ function POINT_VEC3:GetZ() end --- Set the x coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self -- @param #number x The x coordinate. +-- @return #POINT_VEC3 function POINT_VEC3:SetX( x ) self.x = x + return self end --- Set the y coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self -- @param #number y The y coordinate. +-- @return #POINT_VEC3 function POINT_VEC3:SetY( y ) self.y = y + return self end --- Set the z coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self -- @param #number z The z coordinate. +-- @return #POINT_VEC3 function POINT_VEC3:SetZ( z ) self.z = z + return self +end + +--- Add to the x coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #number x The x coordinate value to add to the current x coodinate. +-- @return #POINT_VEC3 +function POINT_VEC3:AddX( x ) + self.x = self.x + x + return self +end + +--- Add to the y coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #number y The y coordinate value to add to the current y coodinate. +-- @return #POINT_VEC3 +function POINT_VEC3:AddY( y ) + self.y = self.y + y + return self +end + +--- Add to the z coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #number z The z coordinate value to add to the current z coodinate. +-- @return #POINT_VEC3 +function POINT_VEC3:AddZ( z ) + self.z = self.z +z + return self end --- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. @@ -10626,6 +10766,21 @@ function POINT_VEC3:RoutePointGround( Speed, Formation ) return RoutePoint end +--- Creates an explosion at the point of a certain intensity. +-- @param #POINT_VEC3 self +-- @param #number ExplosionIntensity +function POINT_VEC3:Explosion( ExplosionIntensity ) + self:F2( { ExplosionIntensity } ) + trigger.action.explosion( self:GetVec3(), ExplosionIntensity ) +end + +--- Creates an illumination bomb at the point. +-- @param #POINT_VEC3 self +function POINT_VEC3:IlluminationBomb() + self:F2() + trigger.action.illuminationBomb( self:GetVec3() ) +end + --- Smokes the point in a color. -- @param #POINT_VEC3 self @@ -10791,15 +10946,57 @@ function POINT_VEC2:GetAlt() end --- Set the x coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self -- @param #number x The x coordinate. +-- @return #POINT_VEC2 function POINT_VEC2:SetX( x ) self.x = x + return self end --- Set the y coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self -- @param #number y The y coordinate. +-- @return #POINT_VEC2 function POINT_VEC2:SetY( y ) self.z = y + return self +end + +--- Set the altitude of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set. +-- @return #POINT_VEC2 +function POINT_VEC2:SetAlt( Altitude ) + self.y = Altitude or land.getHeight( { x = self.x, y = self.z } ) + return self +end + +--- Add to the x coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @param #number x The x coordinate. +-- @return #POINT_VEC2 +function POINT_VEC2:AddX( x ) + self.x = self.x + x + return self +end + +--- Add to the y coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @param #number y The y coordinate. +-- @return #POINT_VEC2 +function POINT_VEC2:AddY( y ) + self.z = self.z + y + return self +end + +--- Add to the current land height an altitude. +-- @param #POINT_VEC2 self +-- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set. +-- @return #POINT_VEC2 +function POINT_VEC2:AddAlt( Altitude ) + self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0 + return self end @@ -11040,6 +11237,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 +11263,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 +12720,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 +15911,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 +15929,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 +15946,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 @@ -16671,45 +16892,129 @@ end -- * Threat level 8: Unit is a Short Range SAM, radar guided. -- * Threat level 9: Unit is a Medium Range SAM, radar guided. -- * Threat level 10: Unit is a Long Range SAM, radar guided. +-- @param #UNIT self function UNIT:GetThreatLevel() local Attributes = self:GetDesc().attributes + self:E( Attributes ) + local ThreatLevel = 0 + local ThreatText = "" + + if self:IsGround() then - local ThreatLevels = { - "Unarmed", - "Infantry", - "Old Tanks & APCs", - "Tanks & IFVs without ATGM", - "Tanks & IFV with ATGM", - "Modern Tanks", - "AAA", - "IR Guided SAMs", - "SR SAMs", - "MR SAMs", - "LR SAMs" - } + self:E( "Ground" ) - self:T2( Attributes ) + local ThreatLevels = { + "Unarmed", + "Infantry", + "Old Tanks & APCs", + "Tanks & IFVs without ATGM", + "Tanks & IFV with ATGM", + "Modern Tanks", + "AAA", + "IR Guided SAMs", + "SR SAMs", + "MR SAMs", + "LR SAMs" + } + + + if Attributes["LR SAM"] then ThreatLevel = 10 + elseif Attributes["MR SAM"] then ThreatLevel = 9 + elseif Attributes["SR SAM"] and + not Attributes["IR Guided SAM"] then ThreatLevel = 8 + elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and + Attributes["IR Guided SAM"] then ThreatLevel = 7 + elseif Attributes["AAA"] then ThreatLevel = 6 + elseif Attributes["Modern Tanks"] then ThreatLevel = 5 + elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and + Attributes["ATGM"] then ThreatLevel = 4 + elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and + not Attributes["ATGM"] then ThreatLevel = 3 + elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 + elseif Attributes["Infantry"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] + end - if Attributes["LR SAM"] then ThreatLevel = 10 - elseif Attributes["MR SAM"] then ThreatLevel = 9 - elseif Attributes["SR SAM"] and - not Attributes["IR Guided SAM"] then ThreatLevel = 8 - elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and - Attributes["IR Guided SAM"] then ThreatLevel = 7 - elseif Attributes["AAA"] then ThreatLevel = 6 - elseif Attributes["Modern Tanks"] then ThreatLevel = 5 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - Attributes["ATGM"] then ThreatLevel = 4 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - not Attributes["ATGM"] then ThreatLevel = 3 - elseif Attributes["Old Tanks"] or Attributes["APC"] then ThreatLevel = 2 - elseif Attributes["Infantry"] then ThreatLevel = 1 + if self:IsAir() then + + self:E( "Air" ) + + local ThreatLevels = { + "Unarmed", + "Tanker", + "AWACS", + "Transport Helicpter", + "UAV", + "Bomber", + "Strategic Bomber", + "Attack Helicopter", + "Interceptor", + "Multirole Fighter", + "Fighter" + } + + + if Attributes["Fighters"] then ThreatLevel = 10 + elseif Attributes["Multirole fighters"] then ThreatLevel = 9 + elseif Attributes["Battleplanes"] then ThreatLevel = 8 + elseif Attributes["Attack helicopters"] then ThreatLevel = 7 + elseif Attributes["Strategic bombers"] then ThreatLevel = 6 + elseif Attributes["Bombers"] then ThreatLevel = 5 + elseif Attributes["UAVs"] then ThreatLevel = 4 + elseif Attributes["Transport helicopters"] then ThreatLevel = 3 + elseif Attributes["AWACS"] then ThreatLevel = 2 + elseif Attributes["Tankers"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] + end + + if self:IsShip() then + + self:E( "Ship" ) + +--["Aircraft Carriers"] = {"Heavy armed ships",}, +--["Cruisers"] = {"Heavy armed ships",}, +--["Destroyers"] = {"Heavy armed ships",}, +--["Frigates"] = {"Heavy armed ships",}, +--["Corvettes"] = {"Heavy armed ships",}, +--["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",}, +--["Light armed ships"] = {"Armed ships","NonArmoredUnits"}, +--["Armed ships"] = {"Ships"}, +--["Unarmed ships"] = {"Ships","HeavyArmoredUnits",}, + + local ThreatLevels = { + "Unarmed ship", + "Light armed ships", + "Corvettes", + "", + "Frigates", + "", + "Cruiser", + "", + "Destroyer", + "", + "Aircraft Carrier" + } + + + if Attributes["Aircraft Carriers"] then ThreatLevel = 10 + elseif Attributes["Destroyers"] then ThreatLevel = 8 + elseif Attributes["Cruisers"] then ThreatLevel = 6 + elseif Attributes["Frigates"] then ThreatLevel = 4 + elseif Attributes["Corvettes"] then ThreatLevel = 2 + elseif Attributes["Light armed ships"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] end self:T2( ThreatLevel ) - return ThreatLevel, ThreatLevels[ThreatLevel+1] + return ThreatLevel, ThreatText end @@ -16724,7 +17029,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 @@ -16741,7 +17046,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 @@ -17552,7 +17857,11 @@ function STATIC:GetDCSObject() return nil end ---- This module contains the AIRBASE classes. + +function STATIC:GetThreatLevel() + + return 1, "Static" +end--- This module contains the AIRBASE classes. -- -- === -- @@ -17661,15 +17970,229 @@ end ---- Scoring system for MOOSE. --- 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. --- @module Scoring +--- 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\SCORING\Dia1.JPG) +-- +-- === +-- +-- # 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 and results for use at team or squadron websites. +-- +-- SCORING automatically calculates the threat level of the objects hit and destroyed by players, +-- which can be @{Unit}, @{Static) and @{Scenery} objects. +-- +-- Positive score points are granted when enemy or neutral targets are destroyed. +-- Negative score points or penalties are given when a friendly target is hit or destroyed. +-- This brings a lot of dynamism in the scoring, where players need to take care to inflict damage on the right target. +-- By default, penalties weight heavier in the scoring, to ensure that players don't commit fratricide. +-- The total score of the player is calculated by **adding the scores minus the penalties**. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia4.JPG) +-- +-- The score value is calculated based on the **threat level of the player** and the **threat level of the target**. +-- A calculated score takes the threat level of the target divided by a balanced threat level of the player unit. +-- As such, if the threat level of the target is high, and the player threat level is low, a higher score will be given than +-- if the threat level of the player would be high too. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia5.JPG) +-- +-- When multiple players hit the same target, and finally succeed in destroying the target, then 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. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia13.JPG) +-- +-- Optionally, the score values can be **scaled** by a **scale**. Specific scales can be set for positive cores or negative penalties. +-- The default range of the scores granted is a value between 0 and 10. The default range of penalties given is a value between 0 and 30. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia7.JPG) +-- +-- **Additional scores** can be granted to **specific objects**, when the player(s) destroy these objects. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia9.JPG) +-- +-- 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 results can also be logged in a **CSV file**. +-- These 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 player 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 scale +-- +-- Score scales can be set for scores granted when enemies or friendlies are destroyed. +-- Use the method @{#SCORING.SetScaleDestroyScore}() to set the scale of enemy destroys (positive destroys). +-- Use the method @{#SCORING.SetScaleDestroyPenalty}() to set the scale of friendly destroys (negative destroys). +-- +-- local Scoring = SCORING:New( "Scoring File" ) +-- Scoring:SetScaleDestroyScore( 10 ) +-- Scoring:SetScaleDestroyPenalty( 40 ) +-- +-- The above sets the scale 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) Add extra Goal scores upon an event or a condition. +-- +-- A mission has goals and achievements. The scoring system provides an API to set additional scores when a goal or achievement event happens. +-- Use the method @{#SCORING.AddGoalScore}() to add a score for a Player at any time in your mission. +-- +-- ## 1.5) 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.6) 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 scale. +-- +-- ## 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 + + --- The Scoring class -- @type SCORING -- @field Players A collection of the current players that have joined the game. @@ -17705,7 +18228,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 @@ -17714,172 +18237,340 @@ function SCORING:New( GameName ) end + -- 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 ) + + -- Scales + self:SetScaleDestroyScore( 10 ) + self:SetScaleDestroyPenalty( 30 ) + + -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). + self:SetFratricide( self.ScaleDestroyPenalty * 3 ) + + -- Default penalty when a player changes coalition. + self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty ) + + -- 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 scale for scoring valid destroys (enemy destroys). +-- A default calculated score is a value between 1 and 10. +-- The scale 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 Scale The scale of the score given. +function SCORING:SetScaleDestroyScore( Scale ) + + self.ScaleDestroyScore = Scale + 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 scale for scoring penalty destroys (friendly destroys). +-- A default calculated penalty is a value between 1 and 10. +-- The scale magnifies the scores given to the players. +-- @param #SCORING self +-- @param #number Scale The scale of the score given. +-- @return #SCORING +function SCORING:SetScaleDestroyPenalty( Scale ) - local ClientUnit = 0 - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } - local unitId - local unitData - local AlivePlayerUnits = {} + self.ScaleDestroyPenalty = Scale + + 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 - ---- Track DEAD or CRASH events for the scoring. +--- 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.Event#EVENTDATA Event -function SCORING:_EventOnDeadOrCrash( Event ) - self:F( { Event } ) +-- @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 TargetUnit = nil - local TargetGroup = nil - local TargetUnitName = "" - local TargetGroupName = "" - local TargetPlayerName = "" - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil + local ZoneName = ScoreZone:GetName() - if Event.IniDCSUnit then - - TargetUnit = Event.IniDCSUnit - TargetUnitName = Event.IniDCSUnitName - TargetGroup = Event.IniDCSGroup - TargetGroupName = Event.IniDCSGroupName - TargetPlayerName = Event.IniPlayerName - - TargetCoalition = TargetUnit:getCoalition() - --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnit:getDesc().category -- Workaround - TargetType = TargetUnit:getTypeName() - - TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] - TargetUnitCategory = _SCORINGCategory[TargetCategory] - TargetUnitType = TargetType - - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) - end - - for PlayerName, PlayerData in pairs( self.Players ) do - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got killed" ) - - -- Some variables - local InitUnitName = PlayerData.UnitName - local InitUnitType = PlayerData.UnitType - local InitCoalition = PlayerData.UnitCoalition - local InitCategory = PlayerData.UnitCategory - local InitUnitCoalition = _SCORINGCoalition[InitCoalition] - local InitUnitCategory = _SCORINGCategory[InitCategory] - - self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - - -- What is he hitting? - if TargetCategory then - if PlayerData and PlayerData.Hit and PlayerData.Hit[TargetCategory] and PlayerData.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? - if not PlayerData.Kill[TargetCategory] then - PlayerData.Kill[TargetCategory] = {} - end - if not PlayerData.Kill[TargetCategory][TargetType] then - PlayerData.Kill[TargetCategory][TargetType] = {} - PlayerData.Kill[TargetCategory][TargetType].Score = 0 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = 0 - PlayerData.Kill[TargetCategory][TargetType].Penalty = 0 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = 0 - end - - if InitCoalition == TargetCoalition then - PlayerData.Penalty = PlayerData.Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].Penalty = PlayerData.Kill[TargetCategory][TargetType].Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = PlayerData.Kill[TargetCategory][TargetType].PenaltyKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill .. " times. Penalty: -" .. PlayerData.Kill[TargetCategory][TargetType].Penalty .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - 5 ):ToAll() - self:ScoreCSV( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - PlayerData.Score = PlayerData.Score + 10 - PlayerData.Kill[TargetCategory][TargetType].Score = PlayerData.Kill[TargetCategory][TargetType].Score + 10 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = PlayerData.Kill[TargetCategory][TargetType].ScoreKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].ScoreKill .. " times. Score: " .. PlayerData.Kill[TargetCategory][TargetType].Score .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - 5 ):ToAll() - self:ScoreCSV( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end - end - end - end + 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 scale. +-- @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. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT UnitData function SCORING:_AddPlayerFromUnit( UnitData ) self:F( UnitData ) - if UnitData and UnitData:isExist() then - local UnitName = UnitData:getName() - local PlayerName = UnitData:getPlayerName() - local UnitDesc = UnitData:getDesc() + if UnitData:IsAlive() then + local UnitName = UnitData:GetName() + local PlayerName = UnitData:GetPlayerName() + local UnitDesc = UnitData:GetDesc() local UnitCategory = UnitDesc.category - local UnitCoalition = UnitData:getCoalition() - local UnitTypeName = UnitData:getTypeName() + local UnitCoalition = UnitData:GetCoalition() + local UnitTypeName = UnitData:GetTypeName() self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... self.Players[PlayerName] = {} self.Players[PlayerName].Hit = {} - self.Players[PlayerName].Kill = {} + self.Players[PlayerName].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].HitUnits = {} self.Players[PlayerName].Score = 0 self.Players[PlayerName].Penalty = 0 self.Players[PlayerName].PenaltyCoalition = 0 @@ -17897,26 +18588,26 @@ function SCORING:_AddPlayerFromUnit( UnitData ) 2 ):ToAll() self:ScoreCSV( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, - UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:getTypeName() ) + UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() ) end end self.Players[PlayerName].UnitName = UnitName self.Players[PlayerName].UnitCoalition = UnitCoalition self.Players[PlayerName].UnitCategory = UnitCategory self.Players[PlayerName].UnitType = UnitTypeName + 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, + MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, 30 ):ToAll() self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 end end - if self.Players[PlayerName].Penalty > 150 then - ClientGroup = GROUP:NewFromDCSUnit( UnitData ) - ClientGroup:Destroy() + if self.Players[PlayerName].Penalty > self.Fratricide then + UnitData:Destroy() MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", 10 ):ToAll() @@ -17926,6 +18617,37 @@ function SCORING:_AddPlayerFromUnit( UnitData ) end +--- Add a goal score for a player. +-- The method takes the PlayerUnit for which the Goal score needs to be set. +-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. +-- A free text can be given that is shown to the players. +-- The Score can be both positive and negative. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the Player. Other Properties for the scoring are taken from this PlayerUnit, like coalition, type etc. +-- @param #string GoalTag The string or identifier that is used in the CSV file to identify the goal (sort or group later in Excel). +-- @param #string Text A free text that is shown to the players. +-- @param #number Score The score can be both positive or negative ( Penalty ). +function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score ) + + local PlayerName = PlayerUnit:GetPlayerName() + + self:E( { PlayerUnit.UnitName, PlayerName, GoalTag, Text, Score } ) + + -- PlayerName can be nil, if the Unit with the player crashed or due to another reason. + if PlayerName then + local PlayerData = self.Players[PlayerName] + + PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 } + PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score + PlayerData.Score = PlayerData.Score + Score + + MESSAGE:New( Text, 30 ):ToAll() + + self:ScoreCSV( PlayerName, "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() ) + end +end + + --- Registers Scores the players completing a Mission Task. -- @param #SCORING self -- @param Tasking.Mission#MISSION Mission @@ -17994,6 +18716,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 @@ -18001,6 +18751,7 @@ function SCORING:_EventOnHit( Event ) self:F( { Event } ) local InitUnit = nil + local InitUNIT = nil local InitUnitName = "" local InitGroup = nil local InitGroupName = "" @@ -18014,10 +18765,11 @@ function SCORING:_EventOnHit( Event ) local InitUnitType = nil local TargetUnit = nil + local TargetUNIT = nil local TargetUnitName = "" local TargetGroup = nil local TargetGroupName = "" - local TargetPlayerName = "" + local TargetPlayerName = nil local TargetCoalition = nil local TargetCategory = nil @@ -18029,16 +18781,18 @@ function SCORING:_EventOnHit( Event ) if Event.IniDCSUnit then InitUnit = Event.IniDCSUnit + InitUNIT = Event.IniUnit InitUnitName = Event.IniDCSUnitName InitGroup = Event.IniDCSGroup 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] @@ -18051,16 +18805,18 @@ function SCORING:_EventOnHit( Event ) if Event.TgtDCSUnit then TargetUnit = Event.TgtDCSUnit + TargetUNIT = Event.TgtUnit TargetUnitName = Event.TgtDCSUnitName TargetGroup = Event.TgtDCSGroup 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] @@ -18070,279 +18826,682 @@ function SCORING:_EventOnHit( Event ) end if InitPlayerName ~= nil then -- It is a player that is hitting something - self:_AddPlayerFromUnit( InitUnit ) + self:_AddPlayerFromUnit( InitUNIT ) if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( TargetUnit ) - self.Players[InitPlayerName].HitPlayers = self.Players[InitPlayerName].HitPlayers + 1 - end + self:_AddPlayerFromUnit( TargetUNIT ) + end - self:T( "Hitting Something" ) - -- What is he hitting? - if TargetCategory then - if not self.Players[InitPlayerName].Hit[TargetCategory] then - self.Players[InitPlayerName].Hit[TargetCategory] = {} + self:T( "Hitting Something" ) + + -- What is he hitting? + if TargetCategory then + + -- A target got hit, score it. + -- Player contains the score data from self.Players[InitPlayerName] + local Player = self.Players[InitPlayerName] + + -- Ensure there is a hit table per TargetCategory and TargetUnitName. + Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} + Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} + + -- PlayerHit contains the score counters and data per unit that was hit. + local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] + + PlayerHit.Score = PlayerHit.Score or 0 + PlayerHit.Penalty = PlayerHit.Penalty or 0 + PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 + PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 + PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT + + -- Only grant hit scores if there was more than one second between the last hit. + if timer.getTime() - PlayerHit.TimeStamp > 1 then + PlayerHit.TimeStamp = timer.getTime() + + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + + -- Ensure there is a Player to Player hit reference table. + Player.HitPlayers[TargetPlayerName] = true + end + + local Score = 0 + + 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 + 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 + 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 - if not self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] then - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] = {} - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = 0 - end - local Score = 0 - if InitCoalition == TargetCoalition then - self.Players[InitPlayerName].Penalty = self.Players[InitPlayerName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: -" .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - 2 - ):ToAll() - self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - self.Players[InitPlayerName].Score = self.Players[InitPlayerName].Score + 1 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score + 1 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit .. " times. Score: " .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - 2 - ):ToAll() - self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end end elseif InitPlayerName == nil then -- It is an AI hitting a player??? end end +--- Track DEAD or CRASH events for the scoring. +-- @param #SCORING self +-- @param Core.Event#EVENTDATA Event +function SCORING:_EventOnDeadOrCrash( Event ) + self:F( { Event } ) -function SCORING:ReportScoreAll() + local TargetUnit = nil + local TargetGroup = nil + local TargetUnitName = "" + local TargetGroupName = "" + local TargetPlayerName = "" + local TargetCoalition = nil + local TargetCategory = nil + local TargetType = nil + local TargetUnitCoalition = nil + local TargetUnitCategory = nil + local TargetUnitType = nil - env.info( "Hello World " ) + if Event.IniDCSUnit then - local ScoreMessage = "" - local PlayerMessage = "" + TargetUnit = Event.IniDCSUnit + TargetUnitName = Event.IniDCSUnitName + TargetGroup = Event.IniDCSGroup + TargetGroupName = Event.IniDCSGroupName + TargetPlayerName = Event.IniPlayerName - self:T( "Score Report" ) + TargetCoalition = Event.IniCoalition + --TargetCategory = TargetUnit:getCategory() + --TargetCategory = TargetUnit:getDesc().category -- Workaround + TargetCategory = Event.IniCategory + TargetType = Event.IniTypeName - 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 ) + TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] + TargetUnitCategory = _SCORINGCategory[TargetCategory] + TargetUnitType = TargetType + + self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) + end + + -- 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 destroyed" ) -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName + local InitUnitName = Player.UnitName + local InitUnitType = Player.UnitType + local InitCoalition = Player.UnitCoalition + local InitCategory = Player.UnitCategory + local InitUnitCoalition = _SCORINGCoalition[InitCoalition] + local InitUnitCategory = _SCORINGCategory[InitCategory] - local PlayerScore = 0 - local PlayerPenalty = 0 + self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - ScoreMessage = ":\n" + -- 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.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} + Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} - local ScoreMessageHits = "" + -- 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 - 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 + if TargetCoalition then + if InitCoalition == TargetCoalition then + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() + local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 ) + self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) + + Player.Penalty = Player.Penalty + ThreatPenalty + TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty + TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 + + 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 .. "' 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, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() + local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) + + 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 .. "' 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 - 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 ) + -- 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 + end end 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 end - MESSAGE:New( PlayerMessage, 30, "Player Scores" ):ToAll() end -function SCORING:ReportScorePlayer() - - 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 = "" - - 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( "\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 ) + 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 .. "\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 - - for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do - Score = Score + UnitData.Score - ScoreKill = ScoreKill + UnitData.ScoreKill - Penalty = Penalty + UnitData.Penalty - PenaltyKill = PenaltyKill + UnitData.PenaltyKill - end - - local ScoreMessageKill = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill ) - self:T( ScoreMessageKill ) - ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill - - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageKills ~= "" then - ScoreMessage = ScoreMessage .. "\n Kills: " .. ScoreMessageKills .. " " - end - - local ScoreMessageCoalitionChangePenalties = "" - if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) - PlayerPenalty = PlayerPenalty + PlayerData.Penalty - end - if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. "\n Coalition: " .. ScoreMessageCoalitionChangePenalties .. " " - end - - local ScoreMessageMission = "" - local ScoreMission = 0 - local ScoreTask = 0 - for MissionName, MissionData in pairs( PlayerData.Mission ) do - ScoreMission = ScoreMission + MissionData.ScoreMission - ScoreTask = ScoreTask + MissionData.ScoreTask - ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " - end - PlayerScore = PlayerScore + ScoreMission + ScoreTask - - if ScoreMessageMission ~= "" then - ScoreMessage = ScoreMessage .. "\n Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") " - end - - PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties ):%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) + end + if ScoreMessageHits ~= "" then + ScoreMessage = "Hits: " .. ScoreMessageHits + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + + +--- 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 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 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 + + for UnitName, UnitData in pairs( PlayerData.Destroy[CategoryID] ) do + self:E( { UnitData = UnitData } ) + if UnitData ~= {} then + Score = Score + UnitData.Score + ScoreDestroy = ScoreDestroy + UnitData.ScoreDestroy + Penalty = Penalty + UnitData.Penalty + PenaltyDestroy = PenaltyDestroy + UnitData.PenaltyDestroy + end + end + + local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageDestroy ) + ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy + + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + 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 goal scores. +-- @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 ScoreMessageGoal = "" + local ScoreGoal = 0 + local ScoreTask = 0 + for GoalName, GoalData in pairs( PlayerData.Goals ) do + ScoreGoal = ScoreGoal + GoalData.Score + ScoreMessageGoal = ScoreMessageGoal .. "'" .. GoalName .. "':" .. GoalData.Score .. "; " + end + PlayerScore = PlayerScore + ScoreGoal + + if ScoreMessageGoal ~= "" then + ScoreMessage = "Goals: " .. ScoreMessageGoal + 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 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 = "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 ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) + ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals + self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) + + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + 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 ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) + ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals + self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) + + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty, + ReportHits, + ReportDestroys, + ReportCoalitionChanges, + ReportGoals, + 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 ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) + ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals + self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) + + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + 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 @@ -18443,21 +19602,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( @@ -18809,7 +19957,7 @@ function CLEANUP:_CleanUpScheduler() return true end ---- 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](..\Presentations\SPAWN\SPAWN.JPG) @@ -20115,10 +21263,12 @@ function SPAWN:_RandomizeTemplate( SpawnIndex ) self.SpawnGroups[SpawnIndex].SpawnTemplate.x = self.SpawnTemplate.x self.SpawnGroups[SpawnIndex].SpawnTemplate.y = self.SpawnTemplate.y self.SpawnGroups[SpawnIndex].SpawnTemplate.start_time = self.SpawnTemplate.start_time + local OldX = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].x + local OldY = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].y for UnitID = 1, #self.SpawnGroups[SpawnIndex].SpawnTemplate.units do self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].heading = self.SpawnTemplate.units[1].heading - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x = self.SpawnTemplate.units[1].x - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y = self.SpawnTemplate.units[1].y + self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x = self.SpawnTemplate.units[1].x + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x - OldX ) + self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y = self.SpawnTemplate.units[1].y + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y - OldY ) self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].alt = self.SpawnTemplate.units[1].alt end end @@ -24846,7 +25996,7 @@ function DETECTION_AREAS:CreateDetectionSets() end ---- 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.** -- @@ -24964,6 +26114,7 @@ function AI_BALANCER:New( SetClient, SpawnAI ) -- Inherits from BASE local self = BASE:Inherit( self, FSM_SET:New( SET_GROUP:New() ) ) -- AI.AI_Balancer#AI_BALANCER + -- TODO: Define the OnAfterSpawned event self:SetStartState( "None" ) self:AddTransition( "*", "Monitor", "Monitoring" ) self:AddTransition( "*", "Spawn", "Spawning" ) @@ -25027,15 +26178,17 @@ function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName ) -- OK, Spawn a new group from the default SpawnAI object provided. local AIGroup = self.SpawnAI:Spawn() -- Wrapper.Group#GROUP - AIGroup:E( "Spawning new AIGroup" ) - --TODO: need to rework UnitName thing ... - - SetGroup:Add( ClientName, AIGroup ) - self.SpawnQueue[ClientName] = nil - - -- Fire the Spawned event. The first parameter is the AIGroup just Spawned. - -- Mission designers can catch this event to bind further actions to the AIGroup. - self:Spawned( AIGroup ) + if AIGroup then + AIGroup:E( "Spawning new AIGroup" ) + --TODO: need to rework UnitName thing ... + + SetGroup:Add( ClientName, AIGroup ) + self.SpawnQueue[ClientName] = nil + + -- Fire the Spawned event. The first parameter is the AIGroup just Spawned. + -- Mission designers can catch this event to bind further actions to the AIGroup. + self:Spawned( AIGroup ) + end end --- @param #AI_BALANCER self @@ -25149,7 +26302,7 @@ end ---- 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](..\Presentations\AI_PATROL\Dia1.JPG) @@ -26040,7 +27193,7 @@ function AI_PATROL_ZONE:OnPilotDead( EventData ) self:__PilotDead( 1, EventData ) end end ---- 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](..\Presentations\AI_CAS\Dia1.JPG) @@ -26612,7 +27765,7 @@ function AI_CAS_ZONE:OnDead( EventData ) end ---- 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](..\Presentations\AI_CAP\Dia1.JPG) -- @@ -27140,7 +28293,7 @@ function AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To ) end ----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](..\Presentations\AI_CARGO\CARGO.JPG) @@ -30437,6 +31590,7 @@ end -- * @{#TASK.HasStateMachine}():Enquire if the task has a @{Fsm} -- * @{#TASK.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK}. -- * @{#TASK.UnAssignFromUnit}(): Unassign the task from a unit. +-- * @{#TASK.SetTimeOut}(): Set timer in seconds before task gets cancelled if not assigned. -- -- 1.2) Set and enquire task status (beyond the task state machine processing). -- ---------------------------------------------------------------------------- @@ -30493,6 +31647,7 @@ TASK = { FsmTemplate = nil, Mission = nil, CommandCenter = nil, + TimeOut = 0, } --- FSM PlayerAborted event handler prototype for TASK. @@ -30586,6 +31741,7 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self:AddTransition( "*", "PlayerAborted", "*" ) self:AddTransition( "*", "PlayerDead", "*" ) self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" ) + self:AddTransition( "*", "TimeOut", "Cancelled" ) self:E( "New TASK " .. TaskName ) @@ -30827,6 +31983,17 @@ function TASK:UnAssignFromUnit( TaskUnit ) return self end +--- Sets the TimeOut for the @{Task}. If @{Task} stayed planned for longer than TimeOut, it gets into Cancelled status. +-- @param #TASK self +-- @param #integer Timer in seconds +-- @return #TASK self +function TASK:SetTimeOut ( Timer ) + self:F( Timer ) + self.TimeOut = Timer + self:__TimeOut( self.TimeOut ) + return self +end + --- Send a message of the @{Task} to the assigned @{Group}s. -- @param #TASK self function TASK:MessageToGroups( Message ) @@ -31358,6 +32525,30 @@ function TASK:onstatechange( From, Event, To ) end +--- FSM function for a TASK +-- @param #TASK self +-- @param #string Event +-- @param #string From +-- @param #string To +function TASK:onenterPlanned( From, Event, To) + if not self.TimeOut == 0 then + self.__TimeOut( self.TimeOut ) + end +end + +--- FSM function for a TASK +-- @param #TASK self +-- @param #string Event +-- @param #string From +-- @param #string To +function TASK:onbeforeTimeOut( From, Event, To ) + if From == "Planned" then + self:RemoveMenu() + return true + end + return false +end + do -- Reporting --- Create a summary report of the Task. @@ -32115,6 +33306,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 748b350e2..565435c5b 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: 20170224_1222' ) +env.info( 'Moose Generation Timestamp: 20170303_1333' ) local base = _G Include = {} @@ -3875,7 +3875,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -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 @@ -10132,13 +10177,56 @@ end -- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. -- In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums. -- --- 1.1) POINT_VEC3 constructor --- --------------------------- +-- ## 1.1) POINT_VEC3 constructor +-- -- A new POINT_VEC3 instance can be created with: -- -- * @{Point#POINT_VEC3.New}(): a 3D point. -- * @{Point#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. --- +-- +-- ## 1.2) Manupulate the X, Y, Z coordinates of the point +-- +-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. +-- Methods exist to manupulate these coordinates. +-- +-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively. +-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value. +-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}() +-- to add or substract a value from the current respective axis value. +-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example: +-- +-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3() +-- +-- +-- ## 1.5) Smoke, flare, explode, illuminate +-- +-- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods: +-- +-- ### 1.5.1) Smoke +-- +-- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color. +-- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue. +-- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red. +-- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange. +-- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white. +-- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green. +-- +-- ### 1.5.2) Flare +-- +-- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color. +-- * @{#POINT_VEC3.FlareRed}(): To flare the point in red. +-- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow. +-- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white. +-- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green. +-- +-- ### 1.5.3) Explode +-- +-- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity. +-- +-- ### 1.5.4) Illuminate +-- +-- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point. +-- -- -- 2) @{Point#POINT_VEC2} class, extends @{Point#POINT_VEC3} -- ========================================================= @@ -10151,6 +10239,19 @@ end -- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter. -- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. -- +-- ## 1.2) Manupulate the X, Altitude, Y coordinates of the 2D point +-- +-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. +-- Methods exist to manupulate these coordinates. +-- +-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively. +-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value. +-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}() +-- to add or substract a value from the current respective axis value. +-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example: +-- +-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2() +-- -- === -- -- **API CHANGE HISTORY** @@ -10163,9 +10264,12 @@ end -- -- Hereby the change log: -- --- 2017-02-18: POINT_VEC3:**NewFromVec2( Vec2, LandHeightAdd )** added. +-- 2017-03-03: POINT\_VEC3:**Explosion( ExplosionIntensity )** added. +-- 2017-03-03: POINT\_VEC3:**IlluminationBomb()** added. -- --- 2016-08-12: POINT_VEC3:**Translate( Distance, Angle )** added. +-- 2017-02-18: POINT\_VEC3:**NewFromVec2( Vec2, LandHeightAdd )** added. +-- +-- 2016-08-12: POINT\_VEC3:**Translate( Distance, Angle )** added. -- -- 2016-08-06: Made PointVec3 and Vec3, PointVec2 and Vec2 terminology used in the code consistent. -- @@ -10321,21 +10425,57 @@ function POINT_VEC3:GetZ() end --- Set the x coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self -- @param #number x The x coordinate. +-- @return #POINT_VEC3 function POINT_VEC3:SetX( x ) self.x = x + return self end --- Set the y coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self -- @param #number y The y coordinate. +-- @return #POINT_VEC3 function POINT_VEC3:SetY( y ) self.y = y + return self end --- Set the z coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self -- @param #number z The z coordinate. +-- @return #POINT_VEC3 function POINT_VEC3:SetZ( z ) self.z = z + return self +end + +--- Add to the x coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #number x The x coordinate value to add to the current x coodinate. +-- @return #POINT_VEC3 +function POINT_VEC3:AddX( x ) + self.x = self.x + x + return self +end + +--- Add to the y coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #number y The y coordinate value to add to the current y coodinate. +-- @return #POINT_VEC3 +function POINT_VEC3:AddY( y ) + self.y = self.y + y + return self +end + +--- Add to the z coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #number z The z coordinate value to add to the current z coodinate. +-- @return #POINT_VEC3 +function POINT_VEC3:AddZ( z ) + self.z = self.z +z + return self end --- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. @@ -10626,6 +10766,21 @@ function POINT_VEC3:RoutePointGround( Speed, Formation ) return RoutePoint end +--- Creates an explosion at the point of a certain intensity. +-- @param #POINT_VEC3 self +-- @param #number ExplosionIntensity +function POINT_VEC3:Explosion( ExplosionIntensity ) + self:F2( { ExplosionIntensity } ) + trigger.action.explosion( self:GetVec3(), ExplosionIntensity ) +end + +--- Creates an illumination bomb at the point. +-- @param #POINT_VEC3 self +function POINT_VEC3:IlluminationBomb() + self:F2() + trigger.action.illuminationBomb( self:GetVec3() ) +end + --- Smokes the point in a color. -- @param #POINT_VEC3 self @@ -10791,15 +10946,57 @@ function POINT_VEC2:GetAlt() end --- Set the x coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self -- @param #number x The x coordinate. +-- @return #POINT_VEC2 function POINT_VEC2:SetX( x ) self.x = x + return self end --- Set the y coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self -- @param #number y The y coordinate. +-- @return #POINT_VEC2 function POINT_VEC2:SetY( y ) self.z = y + return self +end + +--- Set the altitude of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set. +-- @return #POINT_VEC2 +function POINT_VEC2:SetAlt( Altitude ) + self.y = Altitude or land.getHeight( { x = self.x, y = self.z } ) + return self +end + +--- Add to the x coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @param #number x The x coordinate. +-- @return #POINT_VEC2 +function POINT_VEC2:AddX( x ) + self.x = self.x + x + return self +end + +--- Add to the y coordinate of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @param #number y The y coordinate. +-- @return #POINT_VEC2 +function POINT_VEC2:AddY( y ) + self.z = self.z + y + return self +end + +--- Add to the current land height an altitude. +-- @param #POINT_VEC2 self +-- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set. +-- @return #POINT_VEC2 +function POINT_VEC2:AddAlt( Altitude ) + self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0 + return self end @@ -11040,6 +11237,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 +11263,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 +12720,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 +15911,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 +15929,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 +15946,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 @@ -16671,45 +16892,129 @@ end -- * Threat level 8: Unit is a Short Range SAM, radar guided. -- * Threat level 9: Unit is a Medium Range SAM, radar guided. -- * Threat level 10: Unit is a Long Range SAM, radar guided. +-- @param #UNIT self function UNIT:GetThreatLevel() local Attributes = self:GetDesc().attributes + self:E( Attributes ) + local ThreatLevel = 0 + local ThreatText = "" + + if self:IsGround() then - local ThreatLevels = { - "Unarmed", - "Infantry", - "Old Tanks & APCs", - "Tanks & IFVs without ATGM", - "Tanks & IFV with ATGM", - "Modern Tanks", - "AAA", - "IR Guided SAMs", - "SR SAMs", - "MR SAMs", - "LR SAMs" - } + self:E( "Ground" ) - self:T2( Attributes ) + local ThreatLevels = { + "Unarmed", + "Infantry", + "Old Tanks & APCs", + "Tanks & IFVs without ATGM", + "Tanks & IFV with ATGM", + "Modern Tanks", + "AAA", + "IR Guided SAMs", + "SR SAMs", + "MR SAMs", + "LR SAMs" + } + + + if Attributes["LR SAM"] then ThreatLevel = 10 + elseif Attributes["MR SAM"] then ThreatLevel = 9 + elseif Attributes["SR SAM"] and + not Attributes["IR Guided SAM"] then ThreatLevel = 8 + elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and + Attributes["IR Guided SAM"] then ThreatLevel = 7 + elseif Attributes["AAA"] then ThreatLevel = 6 + elseif Attributes["Modern Tanks"] then ThreatLevel = 5 + elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and + Attributes["ATGM"] then ThreatLevel = 4 + elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and + not Attributes["ATGM"] then ThreatLevel = 3 + elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 + elseif Attributes["Infantry"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] + end - if Attributes["LR SAM"] then ThreatLevel = 10 - elseif Attributes["MR SAM"] then ThreatLevel = 9 - elseif Attributes["SR SAM"] and - not Attributes["IR Guided SAM"] then ThreatLevel = 8 - elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and - Attributes["IR Guided SAM"] then ThreatLevel = 7 - elseif Attributes["AAA"] then ThreatLevel = 6 - elseif Attributes["Modern Tanks"] then ThreatLevel = 5 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - Attributes["ATGM"] then ThreatLevel = 4 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - not Attributes["ATGM"] then ThreatLevel = 3 - elseif Attributes["Old Tanks"] or Attributes["APC"] then ThreatLevel = 2 - elseif Attributes["Infantry"] then ThreatLevel = 1 + if self:IsAir() then + + self:E( "Air" ) + + local ThreatLevels = { + "Unarmed", + "Tanker", + "AWACS", + "Transport Helicpter", + "UAV", + "Bomber", + "Strategic Bomber", + "Attack Helicopter", + "Interceptor", + "Multirole Fighter", + "Fighter" + } + + + if Attributes["Fighters"] then ThreatLevel = 10 + elseif Attributes["Multirole fighters"] then ThreatLevel = 9 + elseif Attributes["Battleplanes"] then ThreatLevel = 8 + elseif Attributes["Attack helicopters"] then ThreatLevel = 7 + elseif Attributes["Strategic bombers"] then ThreatLevel = 6 + elseif Attributes["Bombers"] then ThreatLevel = 5 + elseif Attributes["UAVs"] then ThreatLevel = 4 + elseif Attributes["Transport helicopters"] then ThreatLevel = 3 + elseif Attributes["AWACS"] then ThreatLevel = 2 + elseif Attributes["Tankers"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] + end + + if self:IsShip() then + + self:E( "Ship" ) + +--["Aircraft Carriers"] = {"Heavy armed ships",}, +--["Cruisers"] = {"Heavy armed ships",}, +--["Destroyers"] = {"Heavy armed ships",}, +--["Frigates"] = {"Heavy armed ships",}, +--["Corvettes"] = {"Heavy armed ships",}, +--["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",}, +--["Light armed ships"] = {"Armed ships","NonArmoredUnits"}, +--["Armed ships"] = {"Ships"}, +--["Unarmed ships"] = {"Ships","HeavyArmoredUnits",}, + + local ThreatLevels = { + "Unarmed ship", + "Light armed ships", + "Corvettes", + "", + "Frigates", + "", + "Cruiser", + "", + "Destroyer", + "", + "Aircraft Carrier" + } + + + if Attributes["Aircraft Carriers"] then ThreatLevel = 10 + elseif Attributes["Destroyers"] then ThreatLevel = 8 + elseif Attributes["Cruisers"] then ThreatLevel = 6 + elseif Attributes["Frigates"] then ThreatLevel = 4 + elseif Attributes["Corvettes"] then ThreatLevel = 2 + elseif Attributes["Light armed ships"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] end self:T2( ThreatLevel ) - return ThreatLevel, ThreatLevels[ThreatLevel+1] + return ThreatLevel, ThreatText end @@ -16724,7 +17029,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 @@ -16741,7 +17046,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 @@ -17552,7 +17857,11 @@ function STATIC:GetDCSObject() return nil end ---- This module contains the AIRBASE classes. + +function STATIC:GetThreatLevel() + + return 1, "Static" +end--- This module contains the AIRBASE classes. -- -- === -- @@ -17661,15 +17970,229 @@ end ---- Scoring system for MOOSE. --- 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. --- @module Scoring +--- 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\SCORING\Dia1.JPG) +-- +-- === +-- +-- # 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 and results for use at team or squadron websites. +-- +-- SCORING automatically calculates the threat level of the objects hit and destroyed by players, +-- which can be @{Unit}, @{Static) and @{Scenery} objects. +-- +-- Positive score points are granted when enemy or neutral targets are destroyed. +-- Negative score points or penalties are given when a friendly target is hit or destroyed. +-- This brings a lot of dynamism in the scoring, where players need to take care to inflict damage on the right target. +-- By default, penalties weight heavier in the scoring, to ensure that players don't commit fratricide. +-- The total score of the player is calculated by **adding the scores minus the penalties**. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia4.JPG) +-- +-- The score value is calculated based on the **threat level of the player** and the **threat level of the target**. +-- A calculated score takes the threat level of the target divided by a balanced threat level of the player unit. +-- As such, if the threat level of the target is high, and the player threat level is low, a higher score will be given than +-- if the threat level of the player would be high too. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia5.JPG) +-- +-- When multiple players hit the same target, and finally succeed in destroying the target, then 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. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia13.JPG) +-- +-- Optionally, the score values can be **scaled** by a **scale**. Specific scales can be set for positive cores or negative penalties. +-- The default range of the scores granted is a value between 0 and 10. The default range of penalties given is a value between 0 and 30. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia7.JPG) +-- +-- **Additional scores** can be granted to **specific objects**, when the player(s) destroy these objects. +-- +-- ![Banner Image](..\Presentations\SCORING\Dia9.JPG) +-- +-- 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 results can also be logged in a **CSV file**. +-- These 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 player 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 scale +-- +-- Score scales can be set for scores granted when enemies or friendlies are destroyed. +-- Use the method @{#SCORING.SetScaleDestroyScore}() to set the scale of enemy destroys (positive destroys). +-- Use the method @{#SCORING.SetScaleDestroyPenalty}() to set the scale of friendly destroys (negative destroys). +-- +-- local Scoring = SCORING:New( "Scoring File" ) +-- Scoring:SetScaleDestroyScore( 10 ) +-- Scoring:SetScaleDestroyPenalty( 40 ) +-- +-- The above sets the scale 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) Add extra Goal scores upon an event or a condition. +-- +-- A mission has goals and achievements. The scoring system provides an API to set additional scores when a goal or achievement event happens. +-- Use the method @{#SCORING.AddGoalScore}() to add a score for a Player at any time in your mission. +-- +-- ## 1.5) 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.6) 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 scale. +-- +-- ## 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 + + --- The Scoring class -- @type SCORING -- @field Players A collection of the current players that have joined the game. @@ -17705,7 +18228,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 @@ -17714,172 +18237,340 @@ function SCORING:New( GameName ) end + -- 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 ) + + -- Scales + self:SetScaleDestroyScore( 10 ) + self:SetScaleDestroyPenalty( 30 ) + + -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). + self:SetFratricide( self.ScaleDestroyPenalty * 3 ) + + -- Default penalty when a player changes coalition. + self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty ) + + -- 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 scale for scoring valid destroys (enemy destroys). +-- A default calculated score is a value between 1 and 10. +-- The scale 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 Scale The scale of the score given. +function SCORING:SetScaleDestroyScore( Scale ) + + self.ScaleDestroyScore = Scale + 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 scale for scoring penalty destroys (friendly destroys). +-- A default calculated penalty is a value between 1 and 10. +-- The scale magnifies the scores given to the players. +-- @param #SCORING self +-- @param #number Scale The scale of the score given. +-- @return #SCORING +function SCORING:SetScaleDestroyPenalty( Scale ) - local ClientUnit = 0 - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) } - local unitId - local unitData - local AlivePlayerUnits = {} + self.ScaleDestroyPenalty = Scale + + 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 - ---- Track DEAD or CRASH events for the scoring. +--- 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.Event#EVENTDATA Event -function SCORING:_EventOnDeadOrCrash( Event ) - self:F( { Event } ) +-- @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 TargetUnit = nil - local TargetGroup = nil - local TargetUnitName = "" - local TargetGroupName = "" - local TargetPlayerName = "" - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil + local ZoneName = ScoreZone:GetName() - if Event.IniDCSUnit then - - TargetUnit = Event.IniDCSUnit - TargetUnitName = Event.IniDCSUnitName - TargetGroup = Event.IniDCSGroup - TargetGroupName = Event.IniDCSGroupName - TargetPlayerName = Event.IniPlayerName - - TargetCoalition = TargetUnit:getCoalition() - --TargetCategory = TargetUnit:getCategory() - TargetCategory = TargetUnit:getDesc().category -- Workaround - TargetType = TargetUnit:getTypeName() - - TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] - TargetUnitCategory = _SCORINGCategory[TargetCategory] - TargetUnitType = TargetType - - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) - end - - for PlayerName, PlayerData in pairs( self.Players ) do - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got killed" ) - - -- Some variables - local InitUnitName = PlayerData.UnitName - local InitUnitType = PlayerData.UnitType - local InitCoalition = PlayerData.UnitCoalition - local InitCategory = PlayerData.UnitCategory - local InitUnitCoalition = _SCORINGCoalition[InitCoalition] - local InitUnitCategory = _SCORINGCategory[InitCategory] - - self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - - -- What is he hitting? - if TargetCategory then - if PlayerData and PlayerData.Hit and PlayerData.Hit[TargetCategory] and PlayerData.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered??? - if not PlayerData.Kill[TargetCategory] then - PlayerData.Kill[TargetCategory] = {} - end - if not PlayerData.Kill[TargetCategory][TargetType] then - PlayerData.Kill[TargetCategory][TargetType] = {} - PlayerData.Kill[TargetCategory][TargetType].Score = 0 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = 0 - PlayerData.Kill[TargetCategory][TargetType].Penalty = 0 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = 0 - end - - if InitCoalition == TargetCoalition then - PlayerData.Penalty = PlayerData.Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].Penalty = PlayerData.Kill[TargetCategory][TargetType].Penalty + 25 - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill = PlayerData.Kill[TargetCategory][TargetType].PenaltyKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].PenaltyKill .. " times. Penalty: -" .. PlayerData.Kill[TargetCategory][TargetType].Penalty .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - 5 ):ToAll() - self:ScoreCSV( PlayerName, "KILL_PENALTY", 1, -125, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - PlayerData.Score = PlayerData.Score + 10 - PlayerData.Kill[TargetCategory][TargetType].Score = PlayerData.Kill[TargetCategory][TargetType].Score + 10 - PlayerData.Kill[TargetCategory][TargetType].ScoreKill = PlayerData.Kill[TargetCategory][TargetType].ScoreKill + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' killed an enemy " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - PlayerData.Kill[TargetCategory][TargetType].ScoreKill .. " times. Score: " .. PlayerData.Kill[TargetCategory][TargetType].Score .. - ". Score Total:" .. PlayerData.Score - PlayerData.Penalty, - 5 ):ToAll() - self:ScoreCSV( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end - end - end - end + 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 scale. +-- @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. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT UnitData function SCORING:_AddPlayerFromUnit( UnitData ) self:F( UnitData ) - if UnitData and UnitData:isExist() then - local UnitName = UnitData:getName() - local PlayerName = UnitData:getPlayerName() - local UnitDesc = UnitData:getDesc() + if UnitData:IsAlive() then + local UnitName = UnitData:GetName() + local PlayerName = UnitData:GetPlayerName() + local UnitDesc = UnitData:GetDesc() local UnitCategory = UnitDesc.category - local UnitCoalition = UnitData:getCoalition() - local UnitTypeName = UnitData:getTypeName() + local UnitCoalition = UnitData:GetCoalition() + local UnitTypeName = UnitData:GetTypeName() self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... self.Players[PlayerName] = {} self.Players[PlayerName].Hit = {} - self.Players[PlayerName].Kill = {} + self.Players[PlayerName].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].HitUnits = {} self.Players[PlayerName].Score = 0 self.Players[PlayerName].Penalty = 0 self.Players[PlayerName].PenaltyCoalition = 0 @@ -17897,26 +18588,26 @@ function SCORING:_AddPlayerFromUnit( UnitData ) 2 ):ToAll() self:ScoreCSV( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, - UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:getTypeName() ) + UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() ) end end self.Players[PlayerName].UnitName = UnitName self.Players[PlayerName].UnitCoalition = UnitCoalition self.Players[PlayerName].UnitCategory = UnitCategory self.Players[PlayerName].UnitType = UnitTypeName + 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, + MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, 30 ):ToAll() self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 end end - if self.Players[PlayerName].Penalty > 150 then - ClientGroup = GROUP:NewFromDCSUnit( UnitData ) - ClientGroup:Destroy() + if self.Players[PlayerName].Penalty > self.Fratricide then + UnitData:Destroy() MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", 10 ):ToAll() @@ -17926,6 +18617,37 @@ function SCORING:_AddPlayerFromUnit( UnitData ) end +--- Add a goal score for a player. +-- The method takes the PlayerUnit for which the Goal score needs to be set. +-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. +-- A free text can be given that is shown to the players. +-- The Score can be both positive and negative. +-- @param #SCORING self +-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the Player. Other Properties for the scoring are taken from this PlayerUnit, like coalition, type etc. +-- @param #string GoalTag The string or identifier that is used in the CSV file to identify the goal (sort or group later in Excel). +-- @param #string Text A free text that is shown to the players. +-- @param #number Score The score can be both positive or negative ( Penalty ). +function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score ) + + local PlayerName = PlayerUnit:GetPlayerName() + + self:E( { PlayerUnit.UnitName, PlayerName, GoalTag, Text, Score } ) + + -- PlayerName can be nil, if the Unit with the player crashed or due to another reason. + if PlayerName then + local PlayerData = self.Players[PlayerName] + + PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 } + PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score + PlayerData.Score = PlayerData.Score + Score + + MESSAGE:New( Text, 30 ):ToAll() + + self:ScoreCSV( PlayerName, "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() ) + end +end + + --- Registers Scores the players completing a Mission Task. -- @param #SCORING self -- @param Tasking.Mission#MISSION Mission @@ -17994,6 +18716,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 @@ -18001,6 +18751,7 @@ function SCORING:_EventOnHit( Event ) self:F( { Event } ) local InitUnit = nil + local InitUNIT = nil local InitUnitName = "" local InitGroup = nil local InitGroupName = "" @@ -18014,10 +18765,11 @@ function SCORING:_EventOnHit( Event ) local InitUnitType = nil local TargetUnit = nil + local TargetUNIT = nil local TargetUnitName = "" local TargetGroup = nil local TargetGroupName = "" - local TargetPlayerName = "" + local TargetPlayerName = nil local TargetCoalition = nil local TargetCategory = nil @@ -18029,16 +18781,18 @@ function SCORING:_EventOnHit( Event ) if Event.IniDCSUnit then InitUnit = Event.IniDCSUnit + InitUNIT = Event.IniUnit InitUnitName = Event.IniDCSUnitName InitGroup = Event.IniDCSGroup 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] @@ -18051,16 +18805,18 @@ function SCORING:_EventOnHit( Event ) if Event.TgtDCSUnit then TargetUnit = Event.TgtDCSUnit + TargetUNIT = Event.TgtUnit TargetUnitName = Event.TgtDCSUnitName TargetGroup = Event.TgtDCSGroup 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] @@ -18070,279 +18826,682 @@ function SCORING:_EventOnHit( Event ) end if InitPlayerName ~= nil then -- It is a player that is hitting something - self:_AddPlayerFromUnit( InitUnit ) + self:_AddPlayerFromUnit( InitUNIT ) if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( TargetUnit ) - self.Players[InitPlayerName].HitPlayers = self.Players[InitPlayerName].HitPlayers + 1 - end + self:_AddPlayerFromUnit( TargetUNIT ) + end - self:T( "Hitting Something" ) - -- What is he hitting? - if TargetCategory then - if not self.Players[InitPlayerName].Hit[TargetCategory] then - self.Players[InitPlayerName].Hit[TargetCategory] = {} + self:T( "Hitting Something" ) + + -- What is he hitting? + if TargetCategory then + + -- A target got hit, score it. + -- Player contains the score data from self.Players[InitPlayerName] + local Player = self.Players[InitPlayerName] + + -- Ensure there is a hit table per TargetCategory and TargetUnitName. + Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} + Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} + + -- PlayerHit contains the score counters and data per unit that was hit. + local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] + + PlayerHit.Score = PlayerHit.Score or 0 + PlayerHit.Penalty = PlayerHit.Penalty or 0 + PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 + PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 + PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 + PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT + + -- Only grant hit scores if there was more than one second between the last hit. + if timer.getTime() - PlayerHit.TimeStamp > 1 then + PlayerHit.TimeStamp = timer.getTime() + + if TargetPlayerName ~= nil then -- It is a player hitting another player ... + + -- Ensure there is a Player to Player hit reference table. + Player.HitPlayers[TargetPlayerName] = true + end + + local Score = 0 + + 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 + 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 + 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 - if not self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] then - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] = {} - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = 0 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = 0 - end - local Score = 0 - if InitCoalition == TargetCoalition then - self.Players[InitPlayerName].Penalty = self.Players[InitPlayerName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty + 10 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: -" .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - 2 - ):ToAll() - self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - self.Players[InitPlayerName].Score = self.Players[InitPlayerName].Score + 1 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score + 1 - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit + 1 - MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. - self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit .. " times. Score: " .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score .. - ". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty, - 2 - ):ToAll() - self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - end end elseif InitPlayerName == nil then -- It is an AI hitting a player??? end end +--- Track DEAD or CRASH events for the scoring. +-- @param #SCORING self +-- @param Core.Event#EVENTDATA Event +function SCORING:_EventOnDeadOrCrash( Event ) + self:F( { Event } ) -function SCORING:ReportScoreAll() + local TargetUnit = nil + local TargetGroup = nil + local TargetUnitName = "" + local TargetGroupName = "" + local TargetPlayerName = "" + local TargetCoalition = nil + local TargetCategory = nil + local TargetType = nil + local TargetUnitCoalition = nil + local TargetUnitCategory = nil + local TargetUnitType = nil - env.info( "Hello World " ) + if Event.IniDCSUnit then - local ScoreMessage = "" - local PlayerMessage = "" + TargetUnit = Event.IniDCSUnit + TargetUnitName = Event.IniDCSUnitName + TargetGroup = Event.IniDCSGroup + TargetGroupName = Event.IniDCSGroupName + TargetPlayerName = Event.IniPlayerName - self:T( "Score Report" ) + TargetCoalition = Event.IniCoalition + --TargetCategory = TargetUnit:getCategory() + --TargetCategory = TargetUnit:getDesc().category -- Workaround + TargetCategory = Event.IniCategory + TargetType = Event.IniTypeName - 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 ) + TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] + TargetUnitCategory = _SCORINGCategory[TargetCategory] + TargetUnitType = TargetType + + self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) + end + + -- 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 destroyed" ) -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName + local InitUnitName = Player.UnitName + local InitUnitType = Player.UnitType + local InitCoalition = Player.UnitCoalition + local InitCategory = Player.UnitCategory + local InitUnitCoalition = _SCORINGCoalition[InitCoalition] + local InitUnitCategory = _SCORINGCategory[InitCategory] - local PlayerScore = 0 - local PlayerPenalty = 0 + self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - ScoreMessage = ":\n" + -- 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.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} + Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} - local ScoreMessageHits = "" + -- 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 - 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 + if TargetCoalition then + if InitCoalition == TargetCoalition then + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() + local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 ) + self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) + + Player.Penalty = Player.Penalty + ThreatPenalty + TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty + TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 + + 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 .. "' 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, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) + else + + local ThreatLevelTarget, ThreatTypeTarget = TargetDestroy.UNIT:GetThreatLevel() + local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1 + local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) + + 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 .. "' 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 - 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 ) + -- 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 + end end 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 end - MESSAGE:New( PlayerMessage, 30, "Player Scores" ):ToAll() end -function SCORING:ReportScorePlayer() - - 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 = "" - - 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( "\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 ) + 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 .. "\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 - - for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do - Score = Score + UnitData.Score - ScoreKill = ScoreKill + UnitData.ScoreKill - Penalty = Penalty + UnitData.Penalty - PenaltyKill = PenaltyKill + UnitData.PenaltyKill - end - - local ScoreMessageKill = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill ) - self:T( ScoreMessageKill ) - ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill - - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageKills ~= "" then - ScoreMessage = ScoreMessage .. "\n Kills: " .. ScoreMessageKills .. " " - end - - local ScoreMessageCoalitionChangePenalties = "" - if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) - PlayerPenalty = PlayerPenalty + PlayerData.Penalty - end - if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. "\n Coalition: " .. ScoreMessageCoalitionChangePenalties .. " " - end - - local ScoreMessageMission = "" - local ScoreMission = 0 - local ScoreTask = 0 - for MissionName, MissionData in pairs( PlayerData.Mission ) do - ScoreMission = ScoreMission + MissionData.ScoreMission - ScoreTask = ScoreTask + MissionData.ScoreTask - ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " - end - PlayerScore = PlayerScore + ScoreMission + ScoreTask - - if ScoreMessageMission ~= "" then - ScoreMessage = ScoreMessage .. "\n Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") " - end - - PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties ):%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage ) + end + if ScoreMessageHits ~= "" then + ScoreMessage = "Hits: " .. ScoreMessageHits + end + end + + return ScoreMessage, PlayerScore, PlayerPenalty +end + + +--- 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 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 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 + + for UnitName, UnitData in pairs( PlayerData.Destroy[CategoryID] ) do + self:E( { UnitData = UnitData } ) + if UnitData ~= {} then + Score = Score + UnitData.Score + ScoreDestroy = ScoreDestroy + UnitData.ScoreDestroy + Penalty = Penalty + UnitData.Penalty + PenaltyDestroy = PenaltyDestroy + UnitData.PenaltyDestroy + end + end + + local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) + self:T( ScoreMessageDestroy ) + ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy + + PlayerScore = PlayerScore + Score + PlayerPenalty = PlayerPenalty + Penalty + else + --ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) + end + 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 goal scores. +-- @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 ScoreMessageGoal = "" + local ScoreGoal = 0 + local ScoreTask = 0 + for GoalName, GoalData in pairs( PlayerData.Goals ) do + ScoreGoal = ScoreGoal + GoalData.Score + ScoreMessageGoal = ScoreMessageGoal .. "'" .. GoalName .. "':" .. GoalData.Score .. "; " + end + PlayerScore = PlayerScore + ScoreGoal + + if ScoreMessageGoal ~= "" then + ScoreMessage = "Goals: " .. ScoreMessageGoal + 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 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 = "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 ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) + ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals + self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) + + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + 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 ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) + ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals + self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) + + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions + + PlayerMessage = + string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", + PlayerName, + PlayerScore - PlayerPenalty, + PlayerScore, + PlayerPenalty, + ReportHits, + ReportDestroys, + ReportCoalitionChanges, + ReportGoals, + 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 ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) + ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals + self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) + + local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) + ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions + self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) + + local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions + local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + 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 @@ -18443,21 +19602,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( @@ -18809,7 +19957,7 @@ function CLEANUP:_CleanUpScheduler() return true end ---- 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](..\Presentations\SPAWN\SPAWN.JPG) @@ -20115,10 +21263,12 @@ function SPAWN:_RandomizeTemplate( SpawnIndex ) self.SpawnGroups[SpawnIndex].SpawnTemplate.x = self.SpawnTemplate.x self.SpawnGroups[SpawnIndex].SpawnTemplate.y = self.SpawnTemplate.y self.SpawnGroups[SpawnIndex].SpawnTemplate.start_time = self.SpawnTemplate.start_time + local OldX = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].x + local OldY = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].y for UnitID = 1, #self.SpawnGroups[SpawnIndex].SpawnTemplate.units do self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].heading = self.SpawnTemplate.units[1].heading - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x = self.SpawnTemplate.units[1].x - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y = self.SpawnTemplate.units[1].y + self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x = self.SpawnTemplate.units[1].x + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x - OldX ) + self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y = self.SpawnTemplate.units[1].y + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y - OldY ) self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].alt = self.SpawnTemplate.units[1].alt end end @@ -24846,7 +25996,7 @@ function DETECTION_AREAS:CreateDetectionSets() end ---- 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.** -- @@ -24964,6 +26114,7 @@ function AI_BALANCER:New( SetClient, SpawnAI ) -- Inherits from BASE local self = BASE:Inherit( self, FSM_SET:New( SET_GROUP:New() ) ) -- AI.AI_Balancer#AI_BALANCER + -- TODO: Define the OnAfterSpawned event self:SetStartState( "None" ) self:AddTransition( "*", "Monitor", "Monitoring" ) self:AddTransition( "*", "Spawn", "Spawning" ) @@ -25027,15 +26178,17 @@ function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName ) -- OK, Spawn a new group from the default SpawnAI object provided. local AIGroup = self.SpawnAI:Spawn() -- Wrapper.Group#GROUP - AIGroup:E( "Spawning new AIGroup" ) - --TODO: need to rework UnitName thing ... - - SetGroup:Add( ClientName, AIGroup ) - self.SpawnQueue[ClientName] = nil - - -- Fire the Spawned event. The first parameter is the AIGroup just Spawned. - -- Mission designers can catch this event to bind further actions to the AIGroup. - self:Spawned( AIGroup ) + if AIGroup then + AIGroup:E( "Spawning new AIGroup" ) + --TODO: need to rework UnitName thing ... + + SetGroup:Add( ClientName, AIGroup ) + self.SpawnQueue[ClientName] = nil + + -- Fire the Spawned event. The first parameter is the AIGroup just Spawned. + -- Mission designers can catch this event to bind further actions to the AIGroup. + self:Spawned( AIGroup ) + end end --- @param #AI_BALANCER self @@ -25149,7 +26302,7 @@ end ---- 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](..\Presentations\AI_PATROL\Dia1.JPG) @@ -26040,7 +27193,7 @@ function AI_PATROL_ZONE:OnPilotDead( EventData ) self:__PilotDead( 1, EventData ) end end ---- 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](..\Presentations\AI_CAS\Dia1.JPG) @@ -26612,7 +27765,7 @@ function AI_CAS_ZONE:OnDead( EventData ) end ---- 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](..\Presentations\AI_CAP\Dia1.JPG) -- @@ -27140,7 +28293,7 @@ function AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To ) end ----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](..\Presentations\AI_CARGO\CARGO.JPG) @@ -30437,6 +31590,7 @@ end -- * @{#TASK.HasStateMachine}():Enquire if the task has a @{Fsm} -- * @{#TASK.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK}. -- * @{#TASK.UnAssignFromUnit}(): Unassign the task from a unit. +-- * @{#TASK.SetTimeOut}(): Set timer in seconds before task gets cancelled if not assigned. -- -- 1.2) Set and enquire task status (beyond the task state machine processing). -- ---------------------------------------------------------------------------- @@ -30493,6 +31647,7 @@ TASK = { FsmTemplate = nil, Mission = nil, CommandCenter = nil, + TimeOut = 0, } --- FSM PlayerAborted event handler prototype for TASK. @@ -30586,6 +31741,7 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self:AddTransition( "*", "PlayerAborted", "*" ) self:AddTransition( "*", "PlayerDead", "*" ) self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" ) + self:AddTransition( "*", "TimeOut", "Cancelled" ) self:E( "New TASK " .. TaskName ) @@ -30827,6 +31983,17 @@ function TASK:UnAssignFromUnit( TaskUnit ) return self end +--- Sets the TimeOut for the @{Task}. If @{Task} stayed planned for longer than TimeOut, it gets into Cancelled status. +-- @param #TASK self +-- @param #integer Timer in seconds +-- @return #TASK self +function TASK:SetTimeOut ( Timer ) + self:F( Timer ) + self.TimeOut = Timer + self:__TimeOut( self.TimeOut ) + return self +end + --- Send a message of the @{Task} to the assigned @{Group}s. -- @param #TASK self function TASK:MessageToGroups( Message ) @@ -31358,6 +32525,30 @@ function TASK:onstatechange( From, Event, To ) end +--- FSM function for a TASK +-- @param #TASK self +-- @param #string Event +-- @param #string From +-- @param #string To +function TASK:onenterPlanned( From, Event, To) + if not self.TimeOut == 0 then + self.__TimeOut( self.TimeOut ) + end +end + +--- FSM function for a TASK +-- @param #TASK self +-- @param #string Event +-- @param #string From +-- @param #string To +function TASK:onbeforeTimeOut( From, Event, To ) + if From == "Planned" then + self:RemoveMenu() + return true + end + return false +end + do -- Reporting --- Create a summary report of the Task. @@ -32115,6 +33306,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..576daef1f Binary files /dev/null and b/Moose Presentations/SCORING.pptx differ diff --git a/Moose Presentations/USER MISSIONS.pptx b/Moose Presentations/USER MISSIONS.pptx new file mode 100644 index 000000000..9fa93dbdd Binary files /dev/null and b/Moose Presentations/USER MISSIONS.pptx differ diff --git a/Moose Presentations/USER MISSIONS/Dia1.JPG b/Moose Presentations/USER MISSIONS/Dia1.JPG new file mode 100644 index 000000000..1b1131f73 Binary files /dev/null and b/Moose Presentations/USER MISSIONS/Dia1.JPG differ diff --git a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz b/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz index 0e20cbcd3..60ea1a363 100644 Binary files a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz and b/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz differ diff --git a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz b/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz index dc07686ea..6dee91ca6 100644 Binary files a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz and b/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz differ diff --git a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz b/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz index 28d24e4e8..43f3146ac 100644 Binary files a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz and b/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz index d872350a1..ffbfefc2d 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz index aa4f8e226..d408d2460 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz b/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz index 1c4492333..d4aa83919 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz index 9353388eb..f96b06726 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz index 9a297c5ab..40cba9afb 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz index b19e05759..e52064780 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz index bac809a23..780623ab9 100644 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz and b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz index 2419853a4..2aaae9c61 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz index 7a5945b73..7dcf3bf96 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz index 7426ad373..1eccb9196 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz index 50f74d299..11d20cc2c 100644 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz and b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz index 1ca1864fb..c846b9218 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz index 3524c39b7..9da813323 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz index af3269532..ab5e3909e 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz index bd71181dc..91f2ca69b 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz index b98132418..d2520f117 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz index 8f96a8de8..09225397c 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz index 41d615c83..7724d1def 100644 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz and b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz index 424fc69d7..1e56b34c8 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz index 39093c3a8..57f5b0f19 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz index 35bab5bcc..95d225922 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz and b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz index b6048b1a7..11f36b9a3 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz index 57a5450ea..17676e2c4 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz index a94f8e438..e51b1b35a 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz and b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz index 12e77c3ee..bd3ea746e 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz index bb81369d5..cc3066d43 100644 Binary files a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz and b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz index 96e3b4f32..b4af1d6a3 100644 Binary files a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz and b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz differ diff --git a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz index c9209f6a8..5790dec96 100644 Binary files a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz and b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz differ diff --git a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz index 8cfabb805..cc28f1a8d 100644 Binary files a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz and b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz b/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz index a19a76b5c..6d127352e 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz and b/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-100 - OnEventShot Example/EVT-100 - OnEventShot Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-100 - OnEventShot Example/EVT-100 - OnEventShot Example.miz index b637ee9b6..5515dfa11 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-100 - OnEventShot Example/EVT-100 - OnEventShot Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-100 - OnEventShot Example/EVT-100 - OnEventShot Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-101 - OnEventHit Example/EVT-101 - OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-101 - OnEventHit Example/EVT-101 - OnEventHit Example.miz index 22611a5cc..610ced3f0 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-101 - OnEventHit Example/EVT-101 - OnEventHit Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-101 - OnEventHit Example/EVT-101 - OnEventHit Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-102 - OnEventTakeoff Example/EVT-102 - OnEventTakeoff Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-102 - OnEventTakeoff Example/EVT-102 - OnEventTakeoff Example.miz index 4d770b5a2..6e755d888 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-102 - OnEventTakeoff Example/EVT-102 - OnEventTakeoff Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-102 - OnEventTakeoff Example/EVT-102 - OnEventTakeoff Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-103 - OnEventLand Example/EVT-103 - OnEventLand Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-103 - OnEventLand Example/EVT-103 - OnEventLand Example.miz index b64712fbb..26b14ce7d 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-103 - OnEventLand Example/EVT-103 - OnEventLand Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-103 - OnEventLand Example/EVT-103 - OnEventLand Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-104 - OnEventCrash Example/EVT-104 - OnEventCrash Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-104 - OnEventCrash Example/EVT-104 - OnEventCrash Example.miz index d9440ca5d..29eb6eb41 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-104 - OnEventCrash Example/EVT-104 - OnEventCrash Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-104 - OnEventCrash Example/EVT-104 - OnEventCrash Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz index c7c698501..7eeeab10a 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz and b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz b/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz index e5bc7e14b..8df791219 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz and b/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz b/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz index defc5059d..636a552ae 100644 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz and b/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz differ diff --git a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz b/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz index b373f4627..88139c229 100644 Binary files a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz and b/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz b/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz index 162e39f32..7e21b9059 100644 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz and b/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz b/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz index 4c34aff8b..394ec7b4c 100644 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz and b/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz b/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz index e7d4e701f..c366f3726 100644 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz and b/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz differ diff --git a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz b/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz index 5f0a5e81e..1c050743b 100644 Binary files a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz and b/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz b/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz index 1c1f2d3a5..a278af36e 100644 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz and b/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz b/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz index e57d76be9..95a075d58 100644 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz and b/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz b/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz index 484f47e52..c32270f70 100644 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz and b/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz differ diff --git a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz b/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz index 1eefde417..c924e6c33 100644 Binary files a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz and b/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz differ diff --git a/Moose Test Missions/MOOSE_Test_Template.miz b/Moose Test Missions/MOOSE_Test_Template.miz index 4b6c24edf..5c106af38 100644 Binary files a/Moose Test Missions/MOOSE_Test_Template.miz and b/Moose Test Missions/MOOSE_Test_Template.miz differ diff --git a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz index 3efa876d9..9ea09a6cf 100644 Binary files a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz and b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz index 7a176a499..fcc030cb8 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz index cf24153c3..ef0e4f5e7 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz index 6c0b9d683..87e561815 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz index fcb5d806b..45f4f52d8 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz b/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz index 1908cf767..c76856795 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz and b/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz index a555464e7..021db24c9 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz index 943d5c71d..57e883f55 100644 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz and b/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz 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..b45814d10 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 @@ -14,8 +14,25 @@ local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) -local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) +local CommandCenter = COMMANDCENTER:New( HQ, "Bravo" ) -local Scoring = SCORING:New( "Detect Demo" ) +local Scoring = SCORING:New( "Shooting Range 1" ) + +Scoring:SetMultiplierDestroyScore( 10 ) + +Scoring:SetMultiplierDestroyPenalty( 40 ) + +Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) + +-- Test for zone scores. + +-- This one is to test scoring on normal units. +local ShootingRangeZone = ZONE:New( "ScoringZone1" ) +Scoring:AddZoneScore( ShootingRangeZone, 200 ) + +-- This one is to test scoring on scenery. +-- Note that you can only destroy scenery with heavy weapons. +local SceneryZone = ZONE:New( "ScoringZone2" ) +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 4d904901c..e9c7b30ee 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.lua b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.lua new file mode 100644 index 000000000..704c458a4 --- /dev/null +++ b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.lua @@ -0,0 +1,21 @@ +--- +-- Name: SCO-101 - Scoring Client to Client +-- Author: FlightControl +-- Date Created: 24 Feb 2017 +-- +-- # Situation: +-- +-- A shooting range has been setup to test client to client scoring. +-- +-- # Test cases: +-- +-- 1. Observe the scoring granted to your flight when you hit and kill other clients. + + +local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) + +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + +local Scoring = SCORING:New( "Detect Demo" ) + + 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 new file mode 100644 index 000000000..034819401 Binary files /dev/null and b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz differ diff --git a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz index 858cad23a..e59c196a9 100644 Binary files a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz and b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz differ diff --git a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz b/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz index a1cb15dd5..57d09b219 100644 Binary files a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz and b/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz differ diff --git a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz b/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz index 8d443e8f2..d602aab0a 100644 Binary files a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz and b/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz differ diff --git a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz b/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz index 5b659c515..0d94e23e1 100644 Binary files a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz and b/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz b/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz index c29ae6092..b1c6273f9 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz and b/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz index 32b47b453..81da5afff 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz index 06ad26c79..3e1a960b6 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz and b/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz index 4f97880bd..7e40f7f93 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz and b/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz b/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz index 3a729911d..a2c4e8e94 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz and b/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz b/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz index e44c271db..e07c8dbc6 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz and b/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz b/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz index 622a5272f..c65c93198 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz and b/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz index 41388d05f..d0bff5f9d 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.lua b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.lua index b5db1d32d..4258bd86c 100644 --- a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.lua +++ b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.lua @@ -1,6 +1,7 @@ +--- -- Name: SPA-018 - Ground Ops - Randomize Templates -- Author: FlightControl --- Date Created: 10 January 2017 +-- Date Created: 10 Jan 2017 -- -- # Situation: -- diff --git a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz index 8c895fd58..ed7b4ca2c 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz and b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.lua b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.lua new file mode 100644 index 000000000..e636abb57 --- /dev/null +++ b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.lua @@ -0,0 +1,27 @@ +--- +-- Name: SPA-019 - Ground Ops - Randomize Templates with Waypoints +-- Author: FlightControl +-- Date Created: 24 Feb 2017 +-- +-- # Situation: +-- +-- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. +-- +-- # Test cases: +-- +-- 1. Observe that the ground vehicles are spawned with randomized templates. +-- 2. Observe that the ground vehicles are spread around the spawning area and are not stacked upon each other. + + +-- Tests Gudauta +-- ------------- +-- Create a zone table of the 2 zones. +ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } + +TemplateTable = { "A", "B", "C" } + +Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) + :InitLimit( 10, 10 ) + :InitRandomizeTemplate( TemplateTable ) + :SpawnScheduled( 5, .5 ) + diff --git a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz new file mode 100644 index 000000000..f64f4951d Binary files /dev/null and b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.lua b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.lua new file mode 100644 index 000000000..ba61228a3 --- /dev/null +++ b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.lua @@ -0,0 +1,29 @@ +--- +-- Name: SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints +-- Author: FlightControl +-- Date Created: 24 Feb 2017 +-- +-- # Situation: +-- +-- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. +-- +-- # Test cases: +-- +-- 1. Observe that the ground vehicles are spawned with randomized templates. +-- 2. Observe that the ground vehicles are spread around the spawning area and are not stacked upon each other. +-- 3. Observe that the ground vehicles are spread over the random zones, and that the initial templates formations are kept. + + +-- Tests Gudauta +-- ------------- +-- Create a zone table of the 2 zones. +ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } + +TemplateTable = { "A", "B", "C" } + +Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) + :InitLimit( 100, 10 ) + :InitRandomizeTemplate( TemplateTable ) + :InitRandomizeZones( ZoneTable ) + :SpawnScheduled( 5, .5 ) + diff --git a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz new file mode 100644 index 000000000..80a250c80 Binary files /dev/null and b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz index f3ace0dc8..e44bc0c85 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz and b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz index ae69fe7e2..c03fb8028 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz index 9746819a5..fce398284 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz and b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz index d139c6b5c..45c4342f2 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz and b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz index cdc18395d..ca06ee687 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz and b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz b/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz index 32bd53bf3..d4216ed3d 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz and b/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz b/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz index 442f5f413..1983df943 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz and b/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz index 13b31ad93..37a49a932 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz and b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz b/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz index 17dde8081..3801eb0d6 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz and b/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz b/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz index 1ac683ef3..4f281fd64 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz and b/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz b/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz index 8c5d86986..6b6cfe47e 100644 Binary files a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz and b/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz index e51665a8a..bd664e2a6 100644 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz and b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching Demo/TAD-010 - Task Dispatching Demo.miz differ diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz index 9ed58ca2c..3912d4a9c 100644 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz and b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz differ diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz b/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz index 96334fffe..c5601b2b5 100644 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz and b/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz index 0d41439b2..45bf7e431 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz index b86c3f37e..a732c6b44 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz index a07a164dc..183dd2550 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz index 6e7bffdfe..0aced7bfe 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz index 3e972792a..6d7a52222 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz index 36ae67f4e..cfded0ba3 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz index 154f1a3a8..f949bb170 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz index e21687408..c71270a4d 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz index f8e87f7f6..9beaf5c5d 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz and b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz index ca9c15903..221bf4ee2 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz and b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz differ diff --git a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz index b4f9c49a8..53974b022 100644 Binary files a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz and b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz differ diff --git a/README.md b/README.md index d779ca5a9..93ca78473 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ MOOSE works with DCS world 1.5. and 2.0. The goal of MOOSE is to allow mission designers to enhance their scripting with mission orchestration objects, which can be instantiated from defined classes within the framework. This will allow to write mission scripts with minimal code embedded. Of course, the richness of the framework will determine the richness of the misson scenarios. The MOOSE is a service that is produced while being consumed ... , it will evolve further as more classes are developed for the framework, and as more users are using it. -MOOSE is meant to be a one-man show, it is meant to evolve within a growing community around the framework. +MOOSE is not a one-man show, it is a collaborative effort and meant to evolve within a growing community around the framework. Within the community, key users will start supporting, documenting, explaining and even creating new classes for the framework. It is the ambition to grow this framework as a de-facto standard for mission designers to use. 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..3d4f402b1 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

    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..efcd3f237 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

    @@ -2424,6 +2425,7 @@ The UNIT carrying the package.

    + AI_CARGO_UNIT.CargoCarrier 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..0f56b2bba 100644 --- a/docs/Documentation/Fsm.html +++ b/docs/Documentation/Fsm.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • 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..c5db0acdf 100644 --- a/docs/Documentation/Point.html +++ b/docs/Documentation/Point.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -82,6 +83,7 @@ In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums.

    1.1) POINT_VEC3 constructor

    +

    A new POINT_VEC3 instance can be created with:

    +

    1.2) Manupulate the X, Y, Z coordinates of the point

    + +

    A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. +Methods exist to manupulate these coordinates.

    + +

    The current X, Y, Z axis can be retrieved with the methods POINT_VEC3.GetX(), POINT_VEC3.GetY(), POINT_VEC3.GetZ() respectively. +The methods POINT_VEC3.SetX(), POINT_VEC3.SetY(), POINT_VEC3.SetZ() change the respective axis with a new value. +The current axis values can be changed by using the methods POINT_VEC3.AddX(), POINT_VEC3.AddY(), POINT_VEC3.AddZ() +to add or substract a value from the current respective axis value. +Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:

    + +
     local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
    +
    + + +

    1.5) Smoke, flare, explode, illuminate

    + +

    At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods:

    + +

    1.5.1) Smoke

    + + + +

    1.5.2) Flare

    + + + +

    1.5.3) Explode

    + + + +

    1.5.4) Illuminate

    + + +

    2) Point#POINT_VEC2 class, extends Point#POINT_VEC3

    The Point#POINT_VEC2 class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.

    @@ -101,6 +155,20 @@ In order to keep the credibility of the the author, I want to emphasize that the
  • Point#POINT_VEC2.NewFromVec2(): a 2D point created from a DCSTypes#Vec2.
  • +

    1.2) Manupulate the X, Altitude, Y coordinates of the 2D point

    + +

    A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. +Methods exist to manupulate these coordinates.

    + +

    The current X, Altitude, Y axis can be retrieved with the methods POINT_VEC2.GetX(), POINT_VEC2.GetAlt(), POINT_VEC2.GetY() respectively. +The methods POINT_VEC2.SetX(), POINT_VEC2.SetAlt(), POINT_VEC2.SetY() change the respective axis with a new value. +The current axis values can be changed by using the methods POINT_VEC2.AddX(), POINT_VEC2.AddAlt(), POINT_VEC2.AddY() +to add or substract a value from the current respective axis value. +Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:

    + +
     local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
    +
    +

    API CHANGE HISTORY

    @@ -114,6 +182,9 @@ In order to keep the credibility of the the author, I want to emphasize that the

    Hereby the change log:

    +

    2017-03-03: POINT_VEC3:Explosion( ExplosionIntensity ) added.
    +2017-03-03: POINT_VEC3:IlluminationBomb() added.

    +

    2017-02-18: POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) added.

    2016-08-12: POINT_VEC3:Translate( Distance, Angle ) added.

    @@ -154,6 +225,24 @@ In order to keep the credibility of the the author, I want to emphasize that the

    Type POINT_VEC2

    + + + + + + + + + + + + - + + + + + - + @@ -241,6 +336,12 @@ In order to keep the credibility of the the author, I want to emphasize that the + + + +
    POINT_VEC2:AddAlt(Altitude) +

    Add to the current land height an altitude.

    +
    POINT_VEC2:AddX(x) +

    Add to the x coordinate of the POINT_VEC2.

    +
    POINT_VEC2:AddY(y) +

    Add to the y coordinate of the POINT_VEC2.

    +
    POINT_VEC2.ClassName @@ -214,13 +303,19 @@ In order to keep the credibility of the the author, I want to emphasize that the
    POINT_VEC2.SetX(x, self)POINT_VEC2:SetAlt(Altitude) +

    Set the altitude of the POINT_VEC2.

    +
    POINT_VEC2:SetX(x)

    Set the x coordinate of the POINT_VEC2.

    POINT_VEC2.SetY(y, self)POINT_VEC2:SetY(y)

    Set the y coordinate of the POINT_VEC2.

    POINT_VEC2.y

    the y coordinate in meters.

    +
    POINT_VEC2.z +
    @@ -248,9 +349,33 @@ In order to keep the credibility of the the author, I want to emphasize that the

    Type POINT_VEC3

    + + + + + + + + + + + + + + + + @@ -383,6 +508,12 @@ In order to keep the credibility of the the author, I want to emphasize that the + + + + @@ -452,19 +583,19 @@ In order to keep the credibility of the the author, I want to emphasize that the - + - + - + @@ -630,6 +761,87 @@ In order to keep the credibility of the the author, I want to emphasize that the
    + +POINT_VEC2:AddAlt(Altitude) + +
    +
    + +

    Add to the current land height an altitude.

    + +

    Parameter

    +
      +
    • + +

      #number Altitude : +The Altitude to add. If nothing (nil) is given, then the current land altitude is set.

      + +
    • +
    +

    Return value

    + +

    #POINT_VEC2:

    + + +
    +
    +
    +
    + + +POINT_VEC2:AddX(x) + +
    +
    + +

    Add to the x coordinate of the POINT_VEC2.

    + +

    Parameter

    +
      +
    • + +

      #number x : +The x coordinate.

      + +
    • +
    +

    Return value

    + +

    #POINT_VEC2:

    + + +
    +
    +
    +
    + + +POINT_VEC2:AddY(y) + +
    +
    + +

    Add to the y coordinate of the POINT_VEC2.

    + +

    Parameter

    +
      +
    • + +

      #number y : +The y coordinate.

      + +
    • +
    +

    Return value

    + +

    #POINT_VEC2:

    + + +
    +
    +
    +
    + #string POINT_VEC2.ClassName @@ -863,60 +1075,87 @@ The Vec3 point.

    Core.Point#POINT_VEC2: self

    + +
    +
    +
    + + +POINT_VEC2:SetAlt(Altitude) + +
    +
    + +

    Set the altitude of the POINT_VEC2.

    + +

    Parameter

    +
      +
    • + +

      #number Altitude : +The land altitude. If nothing (nil) is given, then the current land altitude is set.

      + +
    • +
    +

    Return value

    + +

    #POINT_VEC2:

    + +
    -POINT_VEC2.SetX(x, self) +POINT_VEC2:SetX(x)

    Set the x coordinate of the POINT_VEC2.

    -

    Parameters

    +

    Parameter

    • #number x : The x coordinate.

      -
    • -
    • - -

      self :

      -
    +

    Return value

    + +

    #POINT_VEC2:

    + +
    -POINT_VEC2.SetY(y, self) +POINT_VEC2:SetY(y)

    Set the y coordinate of the POINT_VEC2.

    -

    Parameters

    +

    Parameter

    • #number y : The y coordinate.

      -
    • -
    • - -

      self :

      -
    +

    Return value

    + +

    #POINT_VEC2:

    + +
    @@ -978,6 +1217,20 @@ The new calculated POINT_VEC2.

    the y coordinate in meters.

    + +
    +
    +
    + + + +POINT_VEC2.z + +
    +
    + + +
    @@ -989,6 +1242,87 @@ The new calculated POINT_VEC2.

    + +POINT_VEC3:AddX(x) + +
    +
    + +

    Add to the x coordinate of the POINT_VEC3.

    + +

    Parameter

    +
      +
    • + +

      #number x : +The x coordinate value to add to the current x coodinate.

      + +
    • +
    +

    Return value

    + +

    #POINT_VEC3:

    + + +
    +
    +
    +
    + + +POINT_VEC3:AddY(y) + +
    +
    + +

    Add to the y coordinate of the POINT_VEC3.

    + +

    Parameter

    +
      +
    • + +

      #number y : +The y coordinate value to add to the current y coodinate.

      + +
    • +
    +

    Return value

    + +

    #POINT_VEC3:

    + + +
    +
    +
    +
    + + +POINT_VEC3:AddZ(z) + +
    +
    + +

    Add to the z coordinate of the POINT_VEC3.

    + +

    Parameter

    +
      +
    • + +

      #number z : +The z coordinate value to add to the current z coodinate.

      + +
    • +
    +

    Return value

    + +

    #POINT_VEC3:

    + + +
    +
    +
    +
    + #string POINT_VEC3.ClassName @@ -998,6 +1332,27 @@ The new calculated POINT_VEC2.

    + +
    +
    +
    + + +POINT_VEC3:Explosion(ExplosionIntensity) + +
    +
    + +

    Creates an explosion at the point of a certain intensity.

    + +

    Parameter

    +
      +
    • + +

      #number ExplosionIntensity :

      + +
    • +
    @@ -1536,6 +1891,19 @@ The z coodinate.

    + +POINT_VEC3:IlluminationBomb() + +
    +
    + +

    Creates an illumination bomb at the point.

    + +
    +
    +
    +
    + POINT_VEC3:IsMetric() @@ -1815,81 +2183,81 @@ true means metric, false means NM.

    -POINT_VEC3.SetX(x, self) +POINT_VEC3:SetX(x)

    Set the x coordinate of the POINT_VEC3.

    -

    Parameters

    +

    Parameter

    • #number x : The x coordinate.

      -
    • -
    • - -

      self :

      -
    +

    Return value

    + +

    #POINT_VEC3:

    + +
    -POINT_VEC3.SetY(y, self) +POINT_VEC3:SetY(y)

    Set the y coordinate of the POINT_VEC3.

    -

    Parameters

    +

    Parameter

    • #number y : The y coordinate.

      -
    • -
    • - -

      self :

      -
    +

    Return value

    + +

    #POINT_VEC3:

    + +
    -POINT_VEC3.SetZ(z, self) +POINT_VEC3:SetZ(z)

    Set the z coordinate of the POINT_VEC3.

    -

    Parameters

    +

    Parameter

    • #number z : The z coordinate.

      -
    • -
    • - -

      self :

      -
    +

    Return value

    + +

    #POINT_VEC3:

    + +
    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)

    +
    POINT_VEC3:AddX(x) +

    Add to the x coordinate of the POINT_VEC3.

    +
    POINT_VEC3:AddY(y) +

    Add to the y coordinate of the POINT_VEC3.

    +
    POINT_VEC3:AddZ(z) +

    Add to the z coordinate of the POINT_VEC3.

    +
    POINT_VEC3.ClassName +
    POINT_VEC3:Explosion(ExplosionIntensity) +

    Creates an explosion at the point of a certain intensity.

    POINT_VEC3:GetZ()

    Return the z coordinate of the POINT_VEC3.

    +
    POINT_VEC3:IlluminationBomb() +

    Creates an illumination bomb at the point.

    POINT_VEC3.SetX(x, self)POINT_VEC3:SetX(x)

    Set the x coordinate of the POINT_VEC3.

    POINT_VEC3.SetY(y, self)POINT_VEC3:SetY(y)

    Set the y coordinate of the POINT_VEC3.

    POINT_VEC3.SetZ(z, self)POINT_VEC3:SetZ(z)

    Set the z coordinate of the POINT_VEC3.

    + + + + +
    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..df99aa154 100644 --- a/docs/Documentation/ScheduleDispatcher.html +++ b/docs/Documentation/ScheduleDispatcher.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -314,8 +315,7 @@ Nothing of this code should be modified without testing it thoroughly.

    -

    Initialize the ObjectSchedulers array, which is a weakly coupled table. - If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.

    +

    setmetatable( {}, { __mode = "v" } )

    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..d669d4e48 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,210 @@

    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 and results 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.

    + +

    SCORING automatically calculates the threat level of the objects hit and destroyed by players, +which can be Unit, Static objects.

    + +

    Positive score points are granted when enemy or neutral targets are destroyed. +Negative score points or penalties are given when a friendly target is hit or destroyed. +This brings a lot of dynamism in the scoring, where players need to take care to inflict damage on the right target. +By default, penalties weight heavier in the scoring, to ensure that players don't commit fratricide. +The total score of the player is calculated by adding the scores minus the penalties.

    + +

    Banner Image

    + +

    The score value is calculated based on the threat level of the player and the threat level of the target. +A calculated score takes the threat level of the target divided by a balanced threat level of the player unit.
    +As such, if the threat level of the target is high, and the player threat level is low, a higher score will be given than +if the threat level of the player would be high too.

    + +

    Banner Image

    + +

    When multiple players hit the same target, and finally succeed in destroying the target, then 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.

    + +

    Banner Image

    + +

    Optionally, the score values can be scaled by a scale. Specific scales can be set for positive cores or negative penalties. +The default range of the scores granted is a value between 0 and 10. The default range of penalties given is a value between 0 and 30.

    + +

    Banner Image

    + +

    Additional scores can be granted to specific objects, when the player(s) destroy these objects.

    + +

    Banner Image

    + +

    Various Zones 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 results can also be logged in a CSV file.
    +These 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 player 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 scale

    + +

    Score scales can be set for scores granted when enemies or friendlies are destroyed. +Use the method SCORING.SetScaleDestroyScore() to set the scale of enemy destroys (positive destroys). +Use the method SCORING.SetScaleDestroyPenalty() to set the scale of friendly destroys (negative destroys).

    + +
     local Scoring = SCORING:New( "Scoring File" )
    + Scoring:SetScaleDestroyScore( 10 )
    + Scoring:SetScaleDestroyPenalty( 40 )
    +
    + +

    The above sets the scale 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) Add extra Goal scores upon an event or a condition.

    + +

    A mission has goals and achievements. The scoring system provides an API to set additional scores when a goal or achievement event happens. +Use the method SCORING.AddGoalScore() to add a score for a Player at any time in your mission.

    + +

    1.5) 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.6) 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 scale.

    + +

    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 (TAW): Testing & Advice.
    • +
    • Dutch-Baron (TAW): Testing & Advice.
    • +
    • **Whisper: Testing and Advice.
    • +
    + +

    Authors:

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

    Global(s)

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

    Type SCORING

    - + + + + + + + + + + + + + + + + + @@ -139,7 +352,85 @@ to a database or a BI tool to publish the scoring results to the player communit - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -148,6 +439,18 @@ to a database or a BI tool to publish the scoring results to the player communit + + + + + + + + @@ -163,21 +466,81 @@ to a database or a BI tool to publish the scoring results to the player communit - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -187,13 +550,19 @@ to a database or a BI tool to publish the scoring results to the player communit - + - + + + + + @@ -202,6 +571,66 @@ to a database or a BI tool to publish the scoring results to the player communit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -232,12 +661,6 @@ to a database or a BI tool to publish the scoring results to the player communit - - - -
    SCORING.AllScoresMenuSCORING:AddGoalScore(PlayerUnit, GoalTag, Text, Score) - +

    Add a goal score for a player.

    +
    SCORING: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: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 goal scores.

    +
    SCORING:ReportScoreAllSummary(PlayerGroup) +

    Report all players score

    +
    SCORING:ReportScoreGroupDetailed(PlayerGroup) +

    Report Group Score Detailed

    +
    SCORING:ReportScoreGroupSummary(PlayerGroup) +

    Report Group Score Summary

    SCORING.RunTime +
    SCORING.ScaleDestroyPenalty + +
    SCORING.ScaleDestroyScore +
    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:SetScaleDestroyPenalty(Scale) +

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

    +
    SCORING:SetScaleDestroyScore(Scale) +

    Set the scale 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 +669,6 @@ to a database or a BI tool to publish the scoring results to the player communit
    - - -ClientGroup - -
    -
    - - - -
    -
    -
    -
    - #SCORING SCORING @@ -323,13 +732,190 @@ to a database or a BI tool to publish the scoring results to the player communit
    - - -SCORING.AllScoresMenu + +SCORING:AddGoalScore(PlayerUnit, GoalTag, Text, Score)
    +

    Add a goal score for a player.

    + + +

    The method takes the PlayerUnit for which the Goal score needs to be set. +The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. +A free text can be given that is shown to the players. +The Score can be both positive and negative.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Unit#UNIT PlayerUnit : +The Unit of the Player. Other Properties for the scoring are taken from this PlayerUnit, like coalition, type etc.

      + +
    • +
    • + +

      #string GoalTag : +The string or identifier that is used in the CSV file to identify the goal (sort or group later in Excel).

      + +
    • +
    • + +

      #string Text : +A free text that is shown to the players.

      + +
    • +
    • + +

      #number Score : +The score can be both positive or negative ( Penalty ).

      + +
    • +
    +
    +
    +
    +
    + + +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 +965,214 @@ 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
    @@ -423,6 +1215,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 +1301,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,8 +1328,242 @@ 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 goal scores.

    + +

    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

    + +
    +
    +
    +
    + + + +SCORING.RunTime
    @@ -494,8 +1576,22 @@ ScoringObject:OpenCSV( "Player Scores" )
    - -SCORING.RunTime + +SCORING.ScaleDestroyPenalty + +
    +
    + + + +
    +
    +
    +
    + + + +SCORING.ScaleDestroyScore
    @@ -595,27 +1691,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 +1705,40 @@ self

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

    Additional Object scores

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

    Additional Zone scores.

    +
    @@ -656,6 +1765,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 scale.

    + +

    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:SetScaleDestroyPenalty(Scale) + +
    +
    + +

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

    + + +

    A default calculated penalty is a value between 1 and 10. +The scale magnifies the scores given to the players.

    + +

    Parameter

    +
      +
    • + +

      #number Scale : +The scale of the score given.

      + +
    • +
    +

    Return value

    + +

    #SCORING:

    + + +
    +
    +
    +
    + + +SCORING:SetScaleDestroyScore(Scale) + +
    +
    + +

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

    + + +

    A default calculated score is a value between 1 and 10. +The scale magnifies the scores given to the players.

    + +

    Parameter

    +
      +
    • + +

      #number Scale : +The scale of the score given.

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

    @@ -786,22 +2158,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..ef48ceb81 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

    @@ -833,6 +834,12 @@ A coding example is provided at the description of the SPAWN:_TranslateRotate(SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle)
    +
    SPAWN.uncontrolled +
    @@ -1758,6 +1765,9 @@ The group that was spawned. You can use this group for further actions.

    + +

    Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.

    +
    @@ -2232,7 +2242,7 @@ when nothing was spawned.

    - + #number SPAWN.SpawnMaxGroups @@ -2249,7 +2259,7 @@ when nothing was spawned.

    - + #number SPAWN.SpawnMaxUnitsAlive @@ -2528,7 +2538,7 @@ Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 )
    - + #boolean SPAWN.SpawnUnControlled @@ -3191,6 +3201,20 @@ True = Continue Scheduler

    + +
    +
    +
    + + + +SPAWN.uncontrolled + +
    +
    + + +
    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() + + @@ -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..9befab2b3 100644 --- a/docs/Documentation/Task.html +++ b/docs/Documentation/Task.html @@ -50,6 +50,7 @@
  • Process_JTAC
  • Process_Pickup
  • Route
  • +
  • Scenery
  • ScheduleDispatcher
  • Scheduler
  • Scoring
  • @@ -88,6 +89,7 @@
  • TASK.HasStateMachine():Enquire if the task has a Fsm
  • TASK.AssignToUnit(): Assign a task to a unit. (Needs to be implemented in the derived classes from #TASK.
  • TASK.UnAssignFromUnit(): Unassign the task from a unit.
  • +
  • TASK.SetTimeOut(): Set timer in seconds before task gets cancelled if not assigned.
  • 1.2) Set and enquire task status (beyond the task state machine processing).

    @@ -511,6 +513,12 @@ Use the method TASK.AddScore() to add scores whe TASK:SetStateMachine(TaskUnit, Fsm)

    Add a FiniteStateMachine to Task with key TaskUnit

    + + + + TASK:SetTimeOut(Timer) + +

    Sets the TimeOut for the Task.

    @@ -595,6 +603,12 @@ Use the method TASK.AddScore() to add scores whe TASK.TaskType + + + + TASK.TimeOut + + @@ -643,6 +657,12 @@ Use the method TASK.AddScore() to add scores whe TASK:onafterReplan(From, Event, To)

    FSM function for a TASK

    + + + + TASK:onbeforeTimeOut(Event, From, To) + +

    FSM function for a TASK

    @@ -661,6 +681,12 @@ Use the method TASK.AddScore() to add scores whe TASK:onenterFailed(From, Event, To)

    FSM function for a TASK

    + + + + TASK:onenterPlanned(Event, From, To) + +

    FSM function for a TASK

    @@ -1993,6 +2019,36 @@ self

    + +TASK:SetTimeOut(Timer) + +
    +
    + +

    Sets the TimeOut for the Task.

    + + +

    If Task stayed planned for longer than TimeOut, it gets into Cancelled status.

    + +

    Parameter

    +
      +
    • + +

      #integer Timer : +in seconds

      + +
    • +
    +

    Return value

    + +

    #TASK: +self

    + +
    +
    +
    +
    + TASK:SetType(TaskType) @@ -2200,6 +2256,20 @@ Fsm#FSM_PROCESS

    + +
    +
    +
    + + + +TASK.TimeOut + +
    +
    + + +
    @@ -2355,6 +2425,37 @@ self

    + +TASK:onbeforeTimeOut(Event, From, To) + +
    +
    + +

    FSM function for a TASK

    + +

    Parameters

    +
      +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + TASK:onenterAborted(From, Event, To) @@ -2448,6 +2549,37 @@ self

    + +TASK:onenterPlanned(Event, From, To) + +
    +
    + +

    FSM function for a TASK

    + +

    Parameters

    +
      +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + TASK:onenterSuccess(Event, From, To) @@ -2508,6 +2640,8 @@ self

    +

    Type integer

    +
    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 - 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.

    @@ -540,7 +548,7 @@ This class implements the inherited functions from ZONE_POLYGON_BASE:IsPointVec2InZone(Vec2) + ZONE_POLYGON_BASE:IsVec2InZone(Vec2)

    Returns if a location is within the zone.

    @@ -616,13 +624,13 @@ This class implements the inherited functions from ZONE_RADIUS:IsPointVec2InZone(Vec2) + 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.

    @@ -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..6855b3c7d 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 @@ 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.

    @@ -334,7 +341,17 @@ following a given priority.

    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 and results for use at team or squadron websites.

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

    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 diff --git a/docs/Presentations/SCORING/Dia10.JPG b/docs/Presentations/SCORING/Dia10.JPG new file mode 100644 index 000000000..ff450950b Binary files /dev/null and b/docs/Presentations/SCORING/Dia10.JPG differ diff --git a/docs/Presentations/SCORING/Dia11.JPG b/docs/Presentations/SCORING/Dia11.JPG new file mode 100644 index 000000000..3814c3d85 Binary files /dev/null and b/docs/Presentations/SCORING/Dia11.JPG differ diff --git a/docs/Presentations/SCORING/Dia12.JPG b/docs/Presentations/SCORING/Dia12.JPG new file mode 100644 index 000000000..9228bac57 Binary files /dev/null and b/docs/Presentations/SCORING/Dia12.JPG differ diff --git a/docs/Presentations/SCORING/Dia13.JPG b/docs/Presentations/SCORING/Dia13.JPG new file mode 100644 index 000000000..2e4a6114b Binary files /dev/null and b/docs/Presentations/SCORING/Dia13.JPG differ diff --git a/docs/Presentations/SCORING/Dia14.JPG b/docs/Presentations/SCORING/Dia14.JPG new file mode 100644 index 000000000..f98caa5e1 Binary files /dev/null and b/docs/Presentations/SCORING/Dia14.JPG differ diff --git a/docs/Presentations/SCORING/Dia2.JPG b/docs/Presentations/SCORING/Dia2.JPG new file mode 100644 index 000000000..51ad11dd5 Binary files /dev/null and b/docs/Presentations/SCORING/Dia2.JPG differ diff --git a/docs/Presentations/SCORING/Dia3.JPG b/docs/Presentations/SCORING/Dia3.JPG new file mode 100644 index 000000000..1518e6a24 Binary files /dev/null and b/docs/Presentations/SCORING/Dia3.JPG differ diff --git a/docs/Presentations/SCORING/Dia4.JPG b/docs/Presentations/SCORING/Dia4.JPG new file mode 100644 index 000000000..ab7571f24 Binary files /dev/null and b/docs/Presentations/SCORING/Dia4.JPG differ diff --git a/docs/Presentations/SCORING/Dia5.JPG b/docs/Presentations/SCORING/Dia5.JPG new file mode 100644 index 000000000..44ba79cdd Binary files /dev/null and b/docs/Presentations/SCORING/Dia5.JPG differ diff --git a/docs/Presentations/SCORING/Dia6.JPG b/docs/Presentations/SCORING/Dia6.JPG new file mode 100644 index 000000000..983ebc9e8 Binary files /dev/null and b/docs/Presentations/SCORING/Dia6.JPG differ diff --git a/docs/Presentations/SCORING/Dia7.JPG b/docs/Presentations/SCORING/Dia7.JPG new file mode 100644 index 000000000..c7daf51c2 Binary files /dev/null and b/docs/Presentations/SCORING/Dia7.JPG differ diff --git a/docs/Presentations/SCORING/Dia8.JPG b/docs/Presentations/SCORING/Dia8.JPG new file mode 100644 index 000000000..b993827f2 Binary files /dev/null and b/docs/Presentations/SCORING/Dia8.JPG differ diff --git a/docs/Presentations/SCORING/Dia9.JPG b/docs/Presentations/SCORING/Dia9.JPG new file mode 100644 index 000000000..c1ccceba7 Binary files /dev/null and b/docs/Presentations/SCORING/Dia9.JPG differ diff --git a/docs/README.md b/docs/README.md index 8b0e62149..eacd0bba7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -205,6 +205,8 @@ MOOSE Functional Classes provide various functions that are useful in mission de * [DETECTION](Documentation/Detection.html): Detect other units using the available sensors of the detection unit. The DETECTION_BASE derived classes will provide different methods how the sets of detected objects are built. +* [SCORING](Documentation/Scoring.html): Administer the scoring of player achievements, and create a CSV file logging the scoring events for use at team or squadron websites. + ## 7.4) MOOSE AI Controlling Classes MOOSE AI Controlling Classes provide mechanisms to control AI over long lasting processes.